瀏覽代碼

Updated after crash

Ivars 5 年之前
父節點
當前提交
c528748dc1
共有 100 個檔案被更改,包括 17315 行新增401 行删除
  1. 140
    0
      PhotoStation API/PhotoStation.json
  2. 10
    0
      PhotoStation API/api.txt
  3. 二進制
      PhotoStation API/photostation.saz
  4. 623
    0
      PhotoStation API/webabi.lua
  5. 186
    0
      PhotoStation API/webapi/PhotoStation.api
  6. 10
    0
      PhotoStation API/webapi/Query.api
  7. 4
    0
      PhotoStation API/webapi/about.conf.php
  8. 6
    0
      PhotoStation API/webapi/about.inc.php
  9. 104
    0
      PhotoStation API/webapi/about.php
  10. 3
    0
      PhotoStation API/webapi/album.conf.php
  11. 8
    0
      PhotoStation API/webapi/album.inc.php
  12. 941
    0
      PhotoStation API/webapi/album.php
  13. 887
    0
      PhotoStation API/webapi/albumutil.php
  14. 3
    0
      PhotoStation API/webapi/auth.conf.php
  15. 7
    0
      PhotoStation API/webapi/auth.inc.php
  16. 170
    0
      PhotoStation API/webapi/auth.php
  17. 229
    0
      PhotoStation API/webapi/authutil.php
  18. 3
    0
      PhotoStation API/webapi/category.conf.php
  19. 7
    0
      PhotoStation API/webapi/category.inc.php
  20. 670
    0
      PhotoStation API/webapi/category.php
  21. 3
    0
      PhotoStation API/webapi/comment.conf.php
  22. 7
    0
      PhotoStation API/webapi/comment.inc.php
  23. 308
    0
      PhotoStation API/webapi/comment.php
  24. 3
    0
      PhotoStation API/webapi/cover.conf.php
  25. 7
    0
      PhotoStation API/webapi/cover.inc.php
  26. 176
    0
      PhotoStation API/webapi/cover.php
  27. 26
    0
      PhotoStation API/webapi/download.conf.php
  28. 7
    0
      PhotoStation API/webapi/download.inc.php
  29. 1017
    0
      PhotoStation API/webapi/download.php
  30. 6
    0
      PhotoStation API/webapi/dsm_share.inc.php
  31. 257
    0
      PhotoStation API/webapi/dsm_share.php
  32. 84
    0
      PhotoStation API/webapi/embed.php
  33. 2
    0
      PhotoStation API/webapi/file.conf.php
  34. 6
    0
      PhotoStation API/webapi/file.inc.php
  35. 204
    0
      PhotoStation API/webapi/file.php
  36. 239
    0
      PhotoStation API/webapi/formulautil.php
  37. 2
    0
      PhotoStation API/webapi/group.conf.php
  38. 6
    0
      PhotoStation API/webapi/group.inc.php
  39. 407
    0
      PhotoStation API/webapi/group.php
  40. 3
    0
      PhotoStation API/webapi/info.conf.php
  41. 7
    0
      PhotoStation API/webapi/info.inc.php
  42. 168
    0
      PhotoStation API/webapi/info.php
  43. 6
    0
      PhotoStation API/webapi/log.inc.php
  44. 154
    0
      PhotoStation API/webapi/log.php
  45. 7
    0
      PhotoStation API/webapi/path.inc.php
  46. 383
    0
      PhotoStation API/webapi/path.php
  47. 2
    0
      PhotoStation API/webapi/permission.conf.php
  48. 7
    0
      PhotoStation API/webapi/permission.inc.php
  49. 872
    0
      PhotoStation API/webapi/permission.php
  50. 3
    0
      PhotoStation API/webapi/photo.conf.php
  51. 8
    0
      PhotoStation API/webapi/photo.inc.php
  52. 1187
    0
      PhotoStation API/webapi/photo.php
  53. 3
    0
      PhotoStation API/webapi/photo_tag.conf.php
  54. 9
    0
      PhotoStation API/webapi/photo_tag.inc.php
  55. 644
    0
      PhotoStation API/webapi/photo_tag.php
  56. 146
    0
      PhotoStation API/webapi/photoutil.php
  57. 1707
    0
      PhotoStation API/webapi/project.wpr
  58. 758
    0
      PhotoStation API/webapi/psutils.lua
  59. 3
    0
      PhotoStation API/webapi/query.conf.php
  60. 8
    0
      PhotoStation API/webapi/query.inc.php
  61. 88
    0
      PhotoStation API/webapi/query.php
  62. 3
    0
      PhotoStation API/webapi/rotate.conf.php
  63. 8
    0
      PhotoStation API/webapi/rotate.inc.php
  64. 88
    0
      PhotoStation API/webapi/rotate.php
  65. 28
    0
      PhotoStation API/webapi/sdk/WebAPI.inc.php
  66. 188
    0
      PhotoStation API/webapi/sdk/WebAPI.php
  67. 62
    0
      PhotoStation API/webapi/sdk/WebAPIUtil.php
  68. 3
    0
      PhotoStation API/webapi/shared_album.conf.php
  69. 7
    0
      PhotoStation API/webapi/shared_album.inc.php
  70. 775
    0
      PhotoStation API/webapi/shared_album.php
  71. 6
    0
      PhotoStation API/webapi/slideshow_music.inc.php
  72. 345
    0
      PhotoStation API/webapi/slideshow_music.php
  73. 3
    0
      PhotoStation API/webapi/smart_album.conf.php
  74. 7
    0
      PhotoStation API/webapi/smart_album.inc.php
  75. 889
    0
      PhotoStation API/webapi/smart_album.php
  76. 3
    0
      PhotoStation API/webapi/tag.conf.php
  77. 9
    0
      PhotoStation API/webapi/tag.inc.php
  78. 586
    0
      PhotoStation API/webapi/tag.php
  79. 4
    0
      PhotoStation API/webapi/thumb.conf.php
  80. 6
    0
      PhotoStation API/webapi/thumb.inc.php
  81. 518
    0
      PhotoStation API/webapi/thumb.php
  82. 221
    0
      PhotoStation API/webapi/timeline.php
  83. 0
    0
      PhotoStation API/webapi/webabi.lua
  84. 6
    0
      PhotoStation API/webapi/webapi.conf.php
  85. 142
    0
      PhotoStation API/webapi/webapi.inc.php
  86. 29
    1
      addon.py
  87. 1
    1
      addon.xml
  88. 二進制
      icon.png
  89. 3
    59
      kmake.bat
  90. 6
    0
      make_links.bat
  91. 60
    0
      picture_infotags.txt
  92. 358
    340
      project.wpr
  93. 二進制
      release/plugin.image.photostation-0.1.1.zip
  94. 二進制
      release/plugin.image.photostation-0.1.10.zip
  95. 二進制
      release/plugin.image.photostation-0.1.13.zip
  96. 二進制
      release/plugin.image.photostation-0.1.14.zip
  97. 二進制
      release/plugin.image.photostation-0.1.15.zip
  98. 二進制
      release/plugin.image.photostation-0.1.17.zip
  99. 二進制
      release/plugin.image.photostation-0.1.2.zip
  100. 0
    0
      release/plugin.image.photostation-0.1.3.zip

+ 140
- 0
PhotoStation API/PhotoStation.json 查看文件

@@ -0,0 +1,140 @@
1
+{
2
+    "SYNO.PhotoStation.Auth": {
3
+        "path": "auth.php",
4
+        "methods": {
5
+            "1": ["login", "logout", "checkauth"]
6
+        }
7
+    },
8
+    "SYNO.PhotoStation.Info": {
9
+        "path": "info.php",
10
+        "methods": {
11
+            "1": ["getinfo"]
12
+        }
13
+    },
14
+    "SYNO.PhotoStation.Album": {
15
+        "path": "album.php",
16
+        "methods": {
17
+            "1": ["list", "getinfo", "create", "edit", "delete", "arrangeitem", "move", "cleararrangeitem", "cancel"]
18
+        }
19
+    },
20
+    "SYNO.PhotoStation.Permission": {
21
+        "path": "permission.php",
22
+        "methods": {
23
+            "1": ["getalbum", "editalbum", "editgroup", "list_public_share", "edit_public_share"]
24
+        }
25
+    },
26
+    "SYNO.PhotoStation.Photo": {
27
+        "path": "photo.php",
28
+        "methods": {
29
+            "1": ["list", "listexif", "listfeatureditem", "listgpsgroup", "listgpsgroupeditem", "getinfo", "getexif", "edit", "delete", "copy", "cancel"]
30
+        }
31
+    },
32
+    "SYNO.PhotoStation.Thumb": {
33
+        "path": "thumb.php",
34
+        "methods": {
35
+            "1": ["get", "get_dsm_thumb"]
36
+        }
37
+    },
38
+    "SYNO.PhotoStation.Cover": {
39
+        "path": "cover.php",
40
+        "methods": {
41
+            "1": ["set"]
42
+        }
43
+    },
44
+    "SYNO.PhotoStation.SmartAlbum": {
45
+        "path": "smart_album.php",
46
+        "methods": {
47
+            "1": ["list", "getinfo", "create", "edit", "delete"]
48
+        }
49
+    },
50
+    "SYNO.PhotoStation.File": {
51
+        "path": "file.php",
52
+        "methods": {
53
+            "1": ["uploadphoto", "uploadvideo"]
54
+        }
55
+    },
56
+    "SYNO.PhotoStation.Download": {
57
+        "path": "download.php",
58
+        "methods": {
59
+            "1": ["getphoto", "getvideo", "getitem"]
60
+        }
61
+    },
62
+    "SYNO.PhotoStation.ory": {
63
+        "path": "ory.php",
64
+        "methods": {
65
+            "1": ["list", "getinfo", "create", "edit", "delete", "arrangeory", "listitem", "additem", "removeitem", "arrangeitem"]
66
+        }
67
+    },
68
+    "SYNO.PhotoStation.About": {
69
+        "path": "about.php",
70
+        "methods": {
71
+            "1": ["get", "set", "set_visibility"]
72
+        }
73
+    },
74
+    "SYNO.PhotoStation.Tag": {
75
+        "path": "tag.php",
76
+        "methods": {
77
+            "1": ["list", "getinfo", "create", "edit", "delete", "searchplace", "delete_unconfirmed_tag"]
78
+        }
79
+    },
80
+    "SYNO.PhotoStation.PhotoTag": {
81
+        "path": "photo_tag.php",
82
+        "methods": {
83
+            "1": ["list", "people_tag", "geo_tag", "desc_tag", "delete", "people_tag_confirm"]
84
+        }
85
+    },
86
+    "SYNO.PhotoStation.Comment": {
87
+        "path": "comment.php",
88
+        "methods": {
89
+            "1": ["list", "create", "delete"]
90
+        }
91
+    },
92
+    "SYNO.PhotoStation.Timeline": {
93
+        "path": "timeline.php",
94
+        "methods": {
95
+            "1": ["getindex"]
96
+        }
97
+    },
98
+    "SYNO.PhotoStation.Group": {
99
+        "path": "group.php",
100
+        "methods": {
101
+            "1": ["list", "get", "get_dsm_group", "getmember", "create", "edit", "editmember", "delete"]
102
+        }
103
+    },
104
+    "SYNO.PhotoStation.Rotate": {
105
+        "path": "rotate.php",
106
+        "methods": {
107
+            "1": ["set"]
108
+        }
109
+    },
110
+    "SYNO.PhotoStation.SlideshowMusic": {
111
+        "path": "slideshow_music.php",
112
+        "methods": {
113
+            "1": ["list", "get", "add", "edit", "delete"]
114
+        }
115
+    },
116
+    "SYNO.PhotoStation.DsmShare": {
117
+        "path": "dsm_share.php",
118
+        "methods": {
119
+            "1": ["list", "copy", "copymusic"]
120
+        }
121
+    },
122
+    "SYNO.PhotoStation.SharedAlbum": {
123
+        "path": "shared_album.php",
124
+        "methods": {
125
+            "1": ["list", "getinfo", "getinfo_public", "create", "edit", "delete", "add_items", "remove_items", "edit_public_share", "get_single_item", "set_single_item"]
126
+        }
127
+    },
128
+    "SYNO.PhotoStation.PhotoLog": {
129
+        "path": "log.php",
130
+        "methods": {
131
+            "1": ["list", "clear", "export"]
132
+        }
133
+    },
134
+    "SYNO.PhotoStation.Path": {
135
+        "path": "path.php",
136
+        "methods": {
137
+            "1": ["	", "checkpath"]
138
+        }
139
+    }
140
+}

+ 10
- 0
PhotoStation API/api.txt 查看文件

@@ -0,0 +1,10 @@
1
+
2
+api=SYNO.PhotoStation.Album&method=list&version=1&id=album_323031372f323031372d31322d3234205a69656d6173737665746b692f636c697073&offset=0&limit=120&sort_by=preference&sort_direction=asc&recursive=false&type=album%2Cphoto%2Cvideo&additional=album_permission%2Cphoto_exif%2Cvideo_quality%2Cvideo_codec%2Cthumb_size
3
+
4
+album_permission,photo_exif,video_quality,video_codec,thumb_size
5
+
6
+
7
+http://home.blue.lv/photo/webapi/download.php?api=SYNO.PhotoStation.Download&method=getvideo&version=1&id=video_323031372f323031372d31322d3234205a69656d6173737665746b692f636c697073_30303133342e4d5453&quality_id=2f766f6c756d65312f70686f746f2f323031372f323031372d31322d3234205a69656d6173737665746b692f636c6970732f4065614469722f30303133342e4d54532f53594e4f50484f544f5f46494c4d5f4d2e6d7034&PHPSESSID=gmj2h1mia251deueb8cj1rggn4
8
+http://home.blue.lv/photo/webapi/download.php?api=SYNO.PhotoStation.Download&method=getvideo&version=1&id=video_323031372f323031372d31322d3234205a69656d6173737665746b692f636c697073_30303133332e4d5453&quality_id=2f766f6c756d65312f70686f746f2f323031372f323031372d31322d3234205a69656d6173737665746b692f636c6970732f4065614469722f30303133332e4d54532f53594e4f50484f544f5f46494c4d5f4d2e6d7034&PHPSESSID=o9gkhs159g8gpjp5vkg4v9h1h2
9
+
10
+http://home.blue.lv/photo/webapi/download.php?api=SYNO.PhotoStation.Download&method=getvideo&version=1&id=video_323031372f323031372d31322d3234205a69656d6173737665746b692f636c697073_30303133342e4d5453&PHPSESSID=gmj2h1mia251deueb8cj1rggn4

二進制
PhotoStation API/photostation.saz 查看文件


+ 623
- 0
PhotoStation API/webabi.lua 查看文件

@@ -0,0 +1,623 @@
1
+--[[----------------------------------------------------------------------------
2
+
3
+PSPhotoStationAPI.lua
4
+This file is part of Photo StatLr - Lightroom plugin.
5
+Copyright(c) 2017, Martin Messmer
6
+
7
+Photo Station Upload primitives:
8
+	- initialize
9
+	- login
10
+	- logout
11
+
12
+	- listAlbum
13
+	- movePic
14
+	- deletePic
15
+	- sortPics
16
+
17
+	- addPhotoComments
18
+	- getPhotoComments
19
+
20
+	- getPhotoExifs
21
+
22
+	- getTags
23
+	- getPhotoTags
24
+
25
+	- editPhoto
26
+
27
+	- createSharedAlbum
28
+	- editSharedAlbum
29
+	- listSharedAlbum
30
+	- addPhotosToSharedAlbum
31
+	- removePhotosFromSharedAlbum
32
+
33
+Photo StatLr is free software: you can redistribute it and/or modify
34
+it under the terms of the GNU General Public License as published by
35
+the Free Software Foundation, either version 3 of the License, or
36
+(at your option) any later version.
37
+
38
+Photo StatLr is distributed in the hope that it will be useful,
39
+but WITHOUT ANY WARRANTY; without even the implied warranty of
40
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
41
+GNU General Public License for more details.
42
+
43
+You should have received a copy of the GNU General Public License
44
+along with Photo StatLr.  If not, see <http://www.gnu.org/licenses/>.
45
+
46
+]]
47
+--------------------------------------------------------------------------------
48
+
49
+-- Lightroom API
50
+local LrFileUtils = import 'LrFileUtils'
51
+local LrPathUtils = import 'LrPathUtils'
52
+local LrHttp = import 'LrHttp'
53
+local LrDate = import 'LrDate'
54
+
55
+require "PSUtilities"
56
+
57
+--====== local functions =====================================================--
58
+
59
+--[[
60
+callSynoAPI (h, synoAPI, formData)
61
+	calls the named synoAPI with the respective parameters in formData
62
+	returns nil, on http error
63
+	returns the decoded JSON response as table on success
64
+]]
65
+local function callSynoAPI (h, synoAPI, formData)
66
+	local postHeaders = {
67
+		{ field = 'Content-Type', value = 'application/x-www-form-urlencoded' },
68
+	}
69
+
70
+	local postBody = 'api=' .. synoAPI .. '&' .. formData
71
+
72
+	if synoAPI == 'SYNO.PhotoStation.Auth' then
73
+		writeLogfile(4, "callSynoAPI: LrHttp.post(" .. h.serverUrl .. h.psWebAPI .. h.apiInfo[synoAPI].path .. ",...)\n")
74
+	else
75
+		writeLogfile(4, string.format("callSynoAPI: LrHttp.post(%s%s%s, api=%s&%s\n", h.serverUrl, h.psWebAPI, h.apiInfo[synoAPI].path, synoAPI, formData))
76
+	end
77
+
78
+	local respBody, respHeaders = LrHttp.post(h.serverUrl .. h.psWebAPI .. h.apiInfo[synoAPI].path, postBody, postHeaders, 'POST', h.serverTimeout, string.len(postBody))
79
+
80
+	if not respBody then
81
+	    writeTableLogfile(3, 'respHeaders', respHeaders)
82
+    	if respHeaders then
83
+      		writeLogfile(3, string.format("Error %s on http request: %s\n",
84
+      				ifnil(respHeaders["error"].errorCode, 'Unknown'),
85
+          			trim(ifnil(respHeaders["error"].name, 'Unknown error description'))))
86
+    		local errorCode = tonumber(ifnil(respHeaders["error"].nativeCode, '1002'))
87
+      		return nil, errorCode
88
+    	else
89
+      		return nil, 1001
90
+    	end
91
+	end
92
+	writeLogfile(4, "Got Body:\n" .. respBody .. "\n")
93
+
94
+	local respArray = JSON:decode(respBody)
95
+
96
+	if not respArray then return nil, 1003 end
97
+
98
+	if respArray.error then
99
+		local errorCode = tonumber(respArray.error.code)
100
+		writeLogfile(3, string.format('PSPhotoStationAPI.callSynoAPI: %s returns error %d\n', synoAPI, errorCode))
101
+		return nil, errorCode
102
+	end
103
+
104
+	return respArray
105
+end
106
+
107
+--====== global functions ====================================================--
108
+
109
+PSPhotoStationAPI = {}
110
+
111
+---------------------------------------------------------------------------------------------------------
112
+-- initialize: set serverUrl, loginPath and uploadPath
113
+function PSPhotoStationAPI.initialize(serverUrl, psPath, serverTimeout)
114
+	local h = {} -- the handle
115
+	local apiInfo = {}
116
+
117
+	writeLogfile(4, "PSPhotoStationAPI.initialize(PhotoStationUrl=" .. serverUrl .. psPath .. ", Timeout=" .. serverTimeout .. ")\n")
118
+
119
+	h.serverUrl = serverUrl
120
+	h.serverTimeout = serverTimeout
121
+
122
+	h.psAlbumRoot	= 	psPath .. '#!Albums'
123
+	h.psWebAPI 		= 	psPath .. 'webapi/'
124
+	h.uploadPath 	=	psPath .. 'include/asst_file_upload.php'
125
+
126
+	-- bootstrap the apiInfo table
127
+	apiInfo['SYNO.API.Info'] = {
128
+		path		= "query.php",
129
+		minVersion	= 1,
130
+		maxVersion	= 1,
131
+	}
132
+	h.apiInfo = apiInfo
133
+
134
+	-- get all API paths via 'SYNO.API.Info'
135
+	local formData =
136
+			'query=all&' ..
137
+			'method=query&' ..
138
+			'version=1&' ..
139
+			'ps_username='
140
+
141
+	local respArray, errorCode = callSynoAPI (h, 'SYNO.API.Info', formData)
142
+
143
+	if not respArray then return nil, errorCode end
144
+
145
+	-- rewrite the apiInfo table with API infos retrieved via SYNO.API.Info
146
+	h.apiInfo = respArray.data
147
+-- 	writeTableLogfile(4, 'apiInfo', h.apiInfo)
148
+
149
+	return h
150
+end
151
+
152
+---------------------------------------------------------------------------------------------------------
153
+-- login(h, username, passowrd)
154
+-- does, what it says
155
+function PSPhotoStationAPI.login(h, username, password)
156
+	local formData = 'method=login&' ..
157
+					 'version=1&' ..
158
+					 'username=' .. urlencode(username) .. '&' ..
159
+					 'password=' .. urlencode(password)
160
+
161
+	local respArray, errorCode = callSynoAPI (h, 'SYNO.PhotoStation.Auth', formData)
162
+
163
+	if not respArray then return false, errorCode end
164
+
165
+	return respArray.success
166
+end
167
+
168
+---------------------------------------------------------------------------------------------------------
169
+
170
+-- logout(h)
171
+-- nothing to do here, invalidating the cookie would be perfect here
172
+function PSPhotoStationAPI.logout (h)
173
+	return true
174
+end
175
+
176
+---------------------------------------------------------------------------------------------------------
177
+-- listAlbum: returns all photos/videos and optionally albums in a given album
178
+-- returns
179
+--		albumItems:		table of photo infos, if success, otherwise nil
180
+--		errorcode:		errorcode, if not success
181
+function PSPhotoStationAPI.listAlbum(h, dstDir, listItems)
182
+	-- recursive doesn't seem to work
183
+	local formData = 'method=list&' ..
184
+					 'version=1&' ..
185
+					 'id=' .. PSPhotoStationUtils.getAlbumId(dstDir) .. '&' ..
186
+					 'type=' .. listItems .. '&' ..
187
+					 'offset=0&' ..
188
+					 'limit=-1&' ..
189
+					 'recursive=false&'..
190
+					 'additional=album_permission,photo_exif'
191
+--					 'additional=album_permission,photo_exif,video_codec,video_quality,thumb_size,file_location'
192
+
193
+	local respArray, errorCode = callSynoAPI (h, 'SYNO.PhotoStation.Album', formData)
194
+
195
+	if not respArray then return nil, errorCode end
196
+
197
+	writeTableLogfile(4, 'listAlbum(' .. dstDir .. ')', respArray.data.items)
198
+	return respArray.data.items
199
+end
200
+
201
+---------------------------------------------------------------------------------------------------------
202
+-- deletePic (h, dstFilename, isVideo)
203
+function PSPhotoStationAPI.deletePic (h, dstFilename, isVideo)
204
+	local formData = 'method=delete&' ..
205
+					 'version=1&' ..
206
+					 'id=' .. PSPhotoStationUtils.getPhotoId(dstFilename, isVideo) .. '&'
207
+
208
+	local respArray, errorCode = callSynoAPI (h, 'SYNO.PhotoStation.Photo', formData)
209
+
210
+	if not respArray and errorCode ~= 101 then return false, errorCode end
211
+
212
+	writeLogfile(3, string.format('deletePic(%s) returns OK (errorCode was %d)\n', dstFilename, ifnil(errorCode, 0)))
213
+	return respArray.success
214
+end
215
+
216
+---------------------------------------------------------------------------------------------------------
217
+-- movePic (h, srcFilename, dstAlbum, isVideo)
218
+function PSPhotoStationAPI.movePic(h, srcFilename, dstAlbum, isVideo)
219
+	local formData = 'method=copy&' ..
220
+					 'version=1&' ..
221
+					 'mode=move&' ..
222
+					 'duplicate=ignore&' ..
223
+					 'id=' .. PSPhotoStationUtils.getPhotoId(srcFilename, isVideo) .. '&' ..
224
+					 'sharepath=' .. PSPhotoStationUtils.getAlbumId(dstAlbum)
225
+
226
+	local respArray, errorCode = callSynoAPI (h, 'SYNO.PhotoStation.Photo', formData)
227
+
228
+	if not respArray then return false, errorCode end
229
+
230
+	writeLogfile(3, string.format('movePic(%s, %s) returns OK\n', srcFilename, dstAlbum))
231
+	return respArray.success
232
+
233
+end
234
+
235
+---------------------------------------------------------------------------------------------------------
236
+-- deleteAlbum(h, albumPath)
237
+function PSPhotoStationAPI.deleteAlbum (h, albumPath)
238
+	local formData = 'method=delete&' ..
239
+					 'version=1&' ..
240
+					 'id=' .. PSPhotoStationUtils.getAlbumId(albumPath) .. '&'
241
+
242
+	local respArray, errorCode = callSynoAPI (h, 'SYNO.PhotoStation.Album', formData)
243
+
244
+	if not respArray then return false, errorCode end
245
+
246
+	writeLogfile(3, string.format('deleteAlbum(%s) returns OK\n', albumPath))
247
+	return respArray.success
248
+end
249
+
250
+---------------------------------------------------------------------------------------------------------
251
+-- sortPics (h, albumPath, sortedPhotos)
252
+function PSPhotoStationAPI.sortPics (h, albumPath, sortedPhotos)
253
+	local formData = 'method=arrangeitem&' ..
254
+					 'version=1&' ..
255
+					 'offset=0&' ..
256
+					 'limit='.. #sortedPhotos .. '&' ..
257
+					 'id=' .. PSPhotoStationUtils.getAlbumId(albumPath) .. '&'
258
+	local i, photoPath, item_ids = {}
259
+
260
+	for i, photoPath in ipairs(sortedPhotos) do
261
+		if i == 1 then
262
+			item_ids = PSPhotoStationUtils.getPhotoId(sortedPhotos[i])
263
+		else
264
+			item_ids = item_ids .. ',' .. PSPhotoStationUtils.getPhotoId(sortedPhotos[i])
265
+		end
266
+	end
267
+
268
+	formData = formData .. 'item_id=' .. item_ids
269
+
270
+	local respArray, errorCode = callSynoAPI (h, 'SYNO.PhotoStation.Album', formData)
271
+
272
+	if not respArray then return false, errorCode end
273
+
274
+	writeLogfile(3, string.format('sortPics(%s) returns OK.\n', albumPath))
275
+	return respArray.success
276
+end
277
+
278
+---------------------------------------------------------------------------------------------------------
279
+-- addPhotoComment (h, dstFilename, isVideo, comment, username)
280
+function PSPhotoStationAPI.addPhotoComment (h, dstFilename, isVideo, comment, username)
281
+	local formData = 'method=create&' ..
282
+					 'version=1&' ..
283
+					 'id=' .. PSPhotoStationUtils.getPhotoId(dstFilename, isVideo) .. '&' ..
284
+					 'name=' .. username .. '&' ..
285
+					 'comment='.. urlencode(comment)
286
+
287
+	local respArray, errorCode = callSynoAPI (h, 'SYNO.PhotoStation.Comment', formData)
288
+
289
+	if not respArray then return false, errorCode end
290
+
291
+	writeLogfile(3, string.format('addPhotoComment(%s, %s, %s) returns OK.\n', dstFilename, comment, username))
292
+	return respArray.success
293
+end
294
+
295
+---------------------------------------------------------------------------------------------------------
296
+-- getPhotoComments (h, dstFilename, isVideo)
297
+function PSPhotoStationAPI.getPhotoComments (h, dstFilename, isVideo)
298
+	local formData = 'method=list&' ..
299
+					 'version=1&' ..
300
+					 'id=' .. PSPhotoStationUtils.getPhotoId(dstFilename, isVideo)
301
+
302
+	local respArray, errorCode = callSynoAPI (h, 'SYNO.PhotoStation.Comment', formData)
303
+
304
+	if not respArray then return false, errorCode end
305
+
306
+	writeLogfile(3, string.format('getPhotoComments(%s) returns OK.\n', dstFilename))
307
+	return respArray.data.comments
308
+end
309
+
310
+--[[ currently not needed
311
+---------------------------------------------------------------------------------------------------------
312
+-- addSharedPhotoComment (h, dstFilename, isVideo, comment, username, sharedAlbumName)
313
+function PSPhotoStationAPI.addSharedPhotoComment (h, dstFilename, isVideo, comment, username, sharedAlbumName)
314
+	local formData = 'method=add_comment&' ..
315
+					 'version=1&' ..
316
+					 'item_id=' .. PSPhotoStationUtils.getPhotoId(dstFilename, isVideo) .. '&' ..
317
+					 'name=' .. username .. '&' ..
318
+					 'comment='.. urlencode(comment) ..'&' ..
319
+					 'public_share_id=' .. PSPhotoStationUtils.getSharedAlbumShareId(h, sharedAlbumName)
320
+
321
+	local respArray, errorCode = callSynoAPI (h, 'SYNO.PhotoStation.AdvancedShare', formData)
322
+
323
+	if not respArray then return false, errorCode end
324
+
325
+	writeLogfile(3, string.format('addSharedPhotoComment(%s, %s, %s, %s) returns OK.\n', dstFilename, sharedAlbumName, comment, username))
326
+	return respArray.success
327
+end
328
+]]
329
+
330
+---------------------------------------------------------------------------------------------------------
331
+-- getSharedPhotoComments (h, sharedAlbumName, dstFilename, isVideo)
332
+function PSPhotoStationAPI.getSharedPhotoComments (h, sharedAlbumName, dstFilename, isVideo)
333
+	local formData = 'method=list_comment&' ..
334
+					 'version=1&' ..
335
+					 'offset=0&' ..
336
+					 'limit=-1&' ..
337
+					 'item_id=' 		.. PSPhotoStationUtils.getPhotoId(dstFilename, isVideo) .. '&' ..
338
+					 'public_share_id=' .. PSPhotoStationUtils.getSharedAlbumShareId(h, sharedAlbumName)
339
+
340
+	local respArray, errorCode = callSynoAPI (h, 'SYNO.PhotoStation.AdvancedShare', formData)
341
+
342
+	if not respArray then return false, errorCode end
343
+
344
+	if respArray.data then
345
+		writeLogfile(3, string.format('getSharedPhotoComments(%s, %s) returns %d comments.\n', dstFilename, sharedAlbumName, #respArray.data))
346
+	else
347
+		writeLogfile(3, string.format('getSharedPhotoComments(%s, %s) returns no comments.\n', dstFilename, sharedAlbumName))
348
+	end
349
+	return respArray.data
350
+end
351
+
352
+---------------------------------------------------------------------------------------------------------
353
+-- getSharedAlbumCommentList (h, sharedAlbumName)
354
+function PSPhotoStationAPI.getSharedAlbumCommentList (h, sharedAlbumName)
355
+	local formData = 'method=list_log&' ..
356
+					 'version=1&' ..
357
+					 'offset=0&' ..
358
+					 'limit=-1&' ..
359
+					 'category=comment&' ..
360
+					 'public_share_id=' .. PSPhotoStationUtils.getSharedAlbumShareId(h, sharedAlbumName)
361
+
362
+	local respArray, errorCode = callSynoAPI (h, 'SYNO.PhotoStation.AdvancedShare', formData)
363
+
364
+	if not respArray then return false, errorCode end
365
+
366
+	if respArray.data then
367
+		writeLogfile(3, string.format('getSharedAlbumCommentList(%s) returns %d comments.\n', sharedAlbumName, respArray.data.total))
368
+	else
369
+		writeLogfile(3, string.format('getSharedAlbumCommentList(%s) returns no comments.\n', sharedAlbumName))
370
+	end
371
+	return respArray.data.data
372
+end
373
+
374
+---------------------------------------------------------------------------------------------------------
375
+-- getPhotoExifs (h, dstFilename, isVideo)
376
+function PSPhotoStationAPI.getPhotoExifs (h, dstFilename, isVideo)
377
+	local formData = 'method=getexif&' ..
378
+					 'version=1&' ..
379
+					 'id=' .. PSPhotoStationUtils.getPhotoId(dstFilename, isVideo)
380
+
381
+	local respArray, errorCode = callSynoAPI (h, 'SYNO.PhotoStation.Photo', formData)
382
+
383
+	if not respArray then return false, errorCode end
384
+
385
+	writeLogfile(3, string.format('getPhotoExifs(%s) returns %d exifs.\n', dstFilename, respArray.data.total))
386
+	return respArray.data.exifs
387
+end
388
+
389
+---------------------------------------------------------------------------------------------------------
390
+-- getTags (h, type)
391
+-- get table of tagId/tagString mappings for given type: desc, people, geo
392
+function PSPhotoStationAPI.getTags(h, type)
393
+	local formData = 'method=list&' ..
394
+					 'version=1&' ..
395
+					 'type=' .. type .. '&' ..
396
+--					 'additional=info&' ..
397
+					 'offset=0&' ..
398
+					 'limit=-1'
399
+
400
+	local respArray, errorCode = callSynoAPI (h, 'SYNO.PhotoStation.Tag', formData)
401
+
402
+	if not respArray then return false, errorCode end
403
+
404
+	writeLogfile(3, string.format('getTags returns %d tags.\n', respArray.data.total))
405
+	return respArray.data.tags
406
+end
407
+
408
+---------------------------------------------------------------------------------------------------------
409
+-- createTag (h, type, name)
410
+-- create a new tagId/tagString mapping of or given type: desc, people, geo
411
+function PSPhotoStationAPI.createTag(h, type, name)
412
+	local formData = 'method=create&' ..
413
+					 'version=1&' ..
414
+					 'type=' .. type .. '&' ..
415
+					 'name=' .. urlencode(name)
416
+
417
+	local respArray, errorCode = callSynoAPI (h, 'SYNO.PhotoStation.Tag', formData)
418
+
419
+	if not respArray then return false, errorCode end
420
+
421
+	writeLogfile(3, string.format('createTag(%s, %s) returns tagId %s.\n', type, name, respArray.data.id))
422
+	return respArray.data.id
423
+end
424
+
425
+---------------------------------------------------------------------------------------------------------
426
+-- getPhotoTags (h, dstFilename, isVideo)
427
+-- get table of tags (general,people,geo) of a photo
428
+function PSPhotoStationAPI.getPhotoTags(h, dstFilename, isVideo)
429
+	local formData = 'method=list&' ..
430
+					 'version=1&' ..
431
+					 'type=people,geo,desc&' ..
432
+					 'additional=info&' ..
433
+					 'id=' .. PSPhotoStationUtils.getPhotoId(dstFilename, isVideo)
434
+
435
+	local respArray, errorCode = callSynoAPI (h, 'SYNO.PhotoStation.PhotoTag', formData)
436
+
437
+	if not respArray then return false, errorCode end
438
+
439
+	writeLogfile(3, string.format('getPhotoTags(%s) returns %d tags.\n', dstFilename, #respArray.data.tags))
440
+	return respArray.data.tags
441
+end
442
+
443
+---------------------------------------------------------------------------------------------------------
444
+-- addPhotoTag (h, dstFilename, isVideo, tagId)
445
+-- add a new tag (general,people,geo) to a photo
446
+function PSPhotoStationAPI.addPhotoTag(h, dstFilename, isVideo, type, tagId)
447
+	local formData = 'method=' .. type .. '_tag&' ..
448
+					 'version=1&' ..
449
+					 'tag_id=' .. tagId .. '&' ..
450
+					 'id=' .. PSPhotoStationUtils.getPhotoId(dstFilename, isVideo)
451
+
452
+	local respArray, errorCode = callSynoAPI (h, 'SYNO.PhotoStation.PhotoTag', formData)
453
+
454
+	if not respArray then return false, errorCode end
455
+
456
+	writeLogfile(3, string.format('addPhotoTag(%s) returns %d item_tag_ids.\n', dstFilename, #respArray.data.item_tag_ids))
457
+	return respArray.data.item_tag_ids
458
+end
459
+
460
+---------------------------------------------------------------------------------------------------------
461
+-- editPhoto (h, dstFilename, isVideo, attrValPairs)
462
+-- edit specific metadata field of a photo
463
+function PSPhotoStationAPI.editPhoto(h, dstFilename, isVideo, attrValPairs)
464
+	local formData = 'method=edit&' ..
465
+					 'version=1&' ..
466
+					 'id=' .. PSPhotoStationUtils.getPhotoId(dstFilename, isVideo)
467
+	local logMessage = ''
468
+
469
+	for i = 1, #attrValPairs do
470
+	 	formData = formData .. '&' 		.. attrValPairs[i].attribute .. '=' .. attrValPairs[i].value
471
+	 	logMessage = logMessage .. ', ' .. attrValPairs[i].attribute .. '=' .. attrValPairs[i].value
472
+	end
473
+
474
+	local success, errorCode = callSynoAPI (h, 'SYNO.PhotoStation.Photo', formData)
475
+
476
+	if not success then return false, errorCode end
477
+
478
+	writeLogfile(3, string.format('editPhoto(%s,%s) returns OK.\n', dstFilename, logMessage))
479
+	return true
480
+end
481
+
482
+---------------------------------------------------------------------------------------------------------
483
+-- getSharedAlbums (h)
484
+-- get table of sharedAlbumId/sharedAlbumName mappings
485
+function PSPhotoStationAPI.getSharedAlbums(h)
486
+	local formData = 'method=list&' ..
487
+					 'version=1&' ..
488
+					 'additional=public_share&' ..
489
+					 'offset=0&' ..
490
+					 'limit=-1'
491
+
492
+	local respArray, errorCode = callSynoAPI (h, 'SYNO.PhotoStation.SharedAlbum', formData)
493
+
494
+	if not respArray then return nil, errorCode end
495
+
496
+	writeLogfile(3, string.format('getSharedAlbums() returns %d albums.\n', respArray.data.total))
497
+	return respArray.data.items
498
+end
499
+
500
+--[[
501
+---------------------------------------------------------------------------------------------------------
502
+-- getSharedAlbumInfo (h, sharedAlbumId)
503
+-- get infos for the given Shared Album
504
+function PSPhotoStationAPI.getSharedAlbumInfo(h, sharedAlbumId)
505
+	local formData = 'method=getinfo&' ..
506
+					 'version=1&' ..
507
+					 'additional=public_share&' ..
508
+					 'id=' .. sharedAlbumId
509
+
510
+	local respArray, errorCode = callSynoAPI (h, 'SYNO.PhotoStation.SharedAlbum', formData)
511
+
512
+	if not respArray then return nil, errorCode end
513
+
514
+	writeLogfile(3, string.format('getSharedAlbumInfo() returns %d albums.\n', #respArray.data.shared_albums))
515
+	return respArray.data.shared_albums[1];
516
+end
517
+]]
518
+
519
+---------------------------------------------------------------------------------------------------------
520
+-- createSharedAlbum(h, name)
521
+function PSPhotoStationAPI.createSharedAlbum(h, name)
522
+	local formData = 'method=create&' ..
523
+					 'version=1&' ..
524
+--					 'item_id=<photo_id>&' ..
525
+					 'name=' .. urlencode(name)
526
+
527
+	local respArray, errorCode = callSynoAPI (h, 'SYNO.PhotoStation.SharedAlbum', formData)
528
+
529
+	if not respArray then return false, errorCode end
530
+
531
+	writeLogfile(3, string.format('createSharedAlbum(%s) returns sharedAlbumId %s.\n', name, respArray.data.id))
532
+	return respArray.data.id
533
+end
534
+
535
+
536
+---------------------------------------------------------------------------------------------------------
537
+-- editSharedAlbum(h, sharedAlbumName, sharedAlbumAttributes)
538
+function PSPhotoStationAPI.editSharedAlbum(h, sharedAlbumName, sharedAlbumAttributes)
539
+	local numAttributes = 0
540
+	local formData = 'method=edit_public_share&' ..
541
+					 'version=1&' ..
542
+					 'id=' .. PSPhotoStationUtils.getSharedAlbumId(h, sharedAlbumName)
543
+
544
+	for attr, value in pairs(sharedAlbumAttributes) do
545
+		formData = formData .. '&' .. attr .. '=' .. urlencode(tostring(value))
546
+		numAttributes = numAttributes + 1
547
+	end
548
+
549
+	local respArray, errorCode = callSynoAPI (h, 'SYNO.PhotoStation.SharedAlbum', formData)
550
+
551
+	if not respArray then return nil, errorCode end
552
+
553
+	writeLogfile(3, string.format('editSharedAlbum(%s, %d attributes) returns shareId %s.\n', sharedAlbumName, numAttributes, respArray.data.shareid))
554
+	return respArray.data
555
+end
556
+
557
+---------------------------------------------------------------------------------------------------------
558
+-- listSharedAlbum: returns all photos/videos in a given shared album
559
+-- returns
560
+--		albumItems:		table of photo infos, if success, otherwise nil
561
+--		errorcode:		errorcode, if not success
562
+function PSPhotoStationAPI.listSharedAlbum(h, dstDir, listItems)
563
+	local formData = 'method=list&' ..
564
+					 'version=1&' ..
565
+					 'filter_shared_album=' .. PSPhotoStationUtils.getSharedAlbumId(h, dstDir) .. '&' ..
566
+					 'type=' .. listItems .. '&' ..
567
+					 'offset=0&' ..
568
+					 'limit=-1&' ..
569
+					 'recursive=false&'..
570
+					 'additional=photo_exif'
571
+--					 'additional=photo_exif,video_codec,video_quality,thumb_size'
572
+
573
+	local respArray, errorCode = callSynoAPI (h, 'SYNO.PhotoStation.Photo', formData)
574
+
575
+	if not respArray then return nil, errorCode end
576
+
577
+	writeTableLogfile(4, 'listAlbum', respArray.data.items)
578
+	return respArray.data.items
579
+end
580
+
581
+---------------------------------------------------------------------------------------------------------
582
+-- PhotoStation.addPhotosToSharedAlbum(h, sharedAlbumName, photos)
583
+-- add photos to Shared Album
584
+function PSPhotoStationAPI.addPhotosToSharedAlbum(h, sharedAlbumName, photos)
585
+	local photoIds = {}
586
+	for i = 1, #photos do
587
+		photoIds[i] = PSPhotoStationUtils.getPhotoId(photos[i].dstFilename, photos[i].isVideo)
588
+	end
589
+	local itemList = table.concat(photoIds, ',')
590
+	local formData = 'method=add_items&' ..
591
+				 'version=1&' ..
592
+				 'id=' ..  PSPhotoStationUtils.getSharedAlbumId(h, sharedAlbumName) .. '&' ..
593
+				 'item_id=' .. itemList
594
+
595
+	local respArray, errorCode = callSynoAPI (h, 'SYNO.PhotoStation.SharedAlbum', formData)
596
+
597
+	if not respArray then return false, errorCode end
598
+
599
+	writeLogfile(3, string.format('addPhotosToSharedAlbum(%s, %d photos) returns OK.\n', sharedAlbumName, #photos))
600
+	return true
601
+end
602
+
603
+---------------------------------------------------------------------------------------------------------
604
+-- PhotoStation.removePhotosFromSharedAlbum(h, sharedAlbumName, photos)
605
+-- remove photos from Shared Album
606
+function PSPhotoStationAPI.removePhotosFromSharedAlbum(h, sharedAlbumName, photos)
607
+	local photoIds = {}
608
+	for i = 1, #photos do
609
+		photoIds[i] = PSPhotoStationUtils.getPhotoId(photos[i].dstFilename, photos[i].isVideo)
610
+	end
611
+	local itemList = table.concat(photoIds, ',')
612
+	local formData = 'method=remove_items&' ..
613
+				 'version=1&' ..
614
+				 'id=' .. PSPhotoStationUtils.getSharedAlbumId(h, sharedAlbumName) .. '&' ..
615
+				 'item_id=' .. itemList
616
+
617
+	local respArray, errorCode = callSynoAPI (h, 'SYNO.PhotoStation.SharedAlbum', formData)
618
+
619
+	if not respArray then return false, errorCode end
620
+
621
+	writeLogfile(3, string.format('removePhotosFromSharedAlbum(%s,%d photos) returns OK.\n', sharedAlbumName, #photos))
622
+	return true
623
+end

+ 186
- 0
PhotoStation API/webapi/PhotoStation.api 查看文件

@@ -0,0 +1,186 @@
1
+{
2
+        "SYNO.PhotoStation.Auth": {
3
+                "path": "auth.php",
4
+                "minVersion": 1,
5
+                "maxVersion": 1,
6
+                "methods": {
7
+                        "1": ["login", "logout", "checkauth"]
8
+                }
9
+        },
10
+        "SYNO.PhotoStation.Info": {
11
+                "path": "info.php",
12
+                "minVersion": 1,
13
+                "maxVersion": 1,
14
+                "methods": {
15
+                        "1": ["getinfo"]
16
+                }
17
+        },
18
+        "SYNO.PhotoStation.Album": {
19
+                "path": "album.php",
20
+                "minVersion": 1,
21
+                "maxVersion": 1,
22
+                "methods": {
23
+                        "1": ["list", "getinfo", "create", "edit", "delete", "arrangeitem", "move", "cleararrangeitem", "cancel"]
24
+                }
25
+        },
26
+        "SYNO.PhotoStation.Permission": {
27
+                "path": "permission.php",
28
+                "minVersion": 1,
29
+                "maxVersion": 1,
30
+                "methods": {
31
+                        "1": ["getalbum", "editalbum", "editgroup", "list_public_share", "edit_public_share"]
32
+                }
33
+        },
34
+	"SYNO.PhotoStation.Photo": {
35
+		"path": "photo.php",
36
+		"minVersion": 1,
37
+		"maxVersion": 1,
38
+		"methods": {
39
+			"1": ["list", "listexif", "listfeatureditem", "listgpsgroup", "listgpsgroupeditem", "getinfo", "getexif", "edit", "delete", "copy", "cancel"]
40
+		}
41
+	},
42
+	"SYNO.PhotoStation.Thumb": {
43
+		"path": "thumb.php",
44
+		"minVersion": 1,
45
+		"maxVersion": 1,
46
+		"methods": {
47
+			"1": ["get", "get_dsm_thumb"]
48
+		}
49
+	},
50
+        "SYNO.PhotoStation.Cover": {
51
+		"path": "cover.php",
52
+		"minVersion": 1,
53
+		"maxVersion": 1,
54
+		"methods": {
55
+			"1": ["set"]
56
+		}
57
+	},
58
+        "SYNO.PhotoStation.SmartAlbum": {
59
+		"path": "smart_album.php",
60
+		"minVersion": 1,
61
+		"maxVersion": 1,
62
+		"methods": {
63
+			"1": ["list", "getinfo", "create", "edit", "delete"]
64
+		}
65
+	},
66
+	"SYNO.PhotoStation.File": {
67
+		"path": "file.php",
68
+		"minVersion": 1,
69
+		"maxVersion": 1,
70
+		"methods": {
71
+			"1": ["uploadphoto", "uploadvideo"]
72
+		}
73
+	},
74
+	"SYNO.PhotoStation.Download": {
75
+		"path": "download.php",
76
+		"minVersion": 1,
77
+		"maxVersion": 1,
78
+		"methods": {
79
+			"1": ["getphoto", "getvideo", "getitem"]
80
+		}
81
+	},
82
+        "SYNO.PhotoStation.Category": {
83
+                "path": "category.php",
84
+                "minVersion": 1,
85
+                "maxVersion": 1,
86
+                "methods": {
87
+                        "1": ["list", "getinfo", "create", "edit", "delete", "arrangecategory", "listitem", "additem", "removeitem", "arrangeitem"]
88
+                }
89
+        },
90
+	"SYNO.PhotoStation.About": {
91
+		"path": "about.php",
92
+		"minVersion": 1,
93
+		"maxVersion": 1,
94
+		"methods": {
95
+			"1": ["get", "set", "set_visibility"]
96
+		}
97
+	},
98
+	"SYNO.PhotoStation.Tag": {
99
+		"path": "tag.php",
100
+		"minVersion": 1,
101
+		"maxVersion": 1,
102
+		"methods": {
103
+			"1": ["list", "getinfo", "create", "edit", "delete", "searchplace", "delete_unconfirmed_tag"]
104
+		}
105
+	},
106
+	"SYNO.PhotoStation.PhotoTag": {
107
+		"path": "photo_tag.php",
108
+		"minVersion": 1,
109
+		"maxVersion": 1,
110
+		"methods": {
111
+			"1": ["list", "people_tag", "geo_tag", "desc_tag", "delete", "people_tag_confirm"]
112
+		}
113
+	},
114
+	"SYNO.PhotoStation.Comment": {
115
+		"path": "comment.php",
116
+		"minVersion": 1,
117
+		"maxVersion": 1,
118
+		"methods": {
119
+			"1": ["list", "create", "delete"]
120
+		}
121
+	},
122
+	"SYNO.PhotoStation.Timeline": {
123
+		"path": "timeline.php",
124
+		"minVersion": 1,
125
+		"maxVersion": 1,
126
+		"methods": {
127
+			"1": ["getindex"]
128
+		}
129
+	},
130
+	"SYNO.PhotoStation.Group": {
131
+		"path": "group.php",
132
+		"minVersion": 1,
133
+		"maxVersion": 1,
134
+		"methods": {
135
+			"1": ["list", "get", "get_dsm_group", "getmember", "create", "edit", "editmember", "delete"]
136
+		}
137
+	},
138
+        "SYNO.PhotoStation.Rotate": {
139
+                "path": "rotate.php",
140
+                "minVersion": 1,
141
+                "maxVersion": 1,
142
+                "methods": {
143
+                        "1": ["set"]
144
+                }
145
+        },
146
+	"SYNO.PhotoStation.SlideshowMusic": {
147
+		"path": "slideshow_music.php",
148
+		"minVersion": 1,
149
+		"maxVersion": 1,
150
+		"methods": {
151
+			"1": ["list", "get", "add", "edit", "delete"]
152
+		}
153
+	},
154
+	"SYNO.PhotoStation.DsmShare": {
155
+		"path": "dsm_share.php",
156
+		"minVersion": 1,
157
+		"maxVersion": 1,
158
+		"methods": {
159
+			"1": ["list", "copy", "copymusic"]
160
+		}
161
+	},
162
+	"SYNO.PhotoStation.SharedAlbum": {
163
+		"path": "shared_album.php",
164
+		"minVersion": 1,
165
+		"maxVersion": 1,
166
+		"methods": {
167
+			"1": ["list", "getinfo", "getinfo_public", "create", "edit", "delete", "add_items", "remove_items", "edit_public_share", "get_single_item", "set_single_item"]
168
+		}
169
+	},
170
+	"SYNO.PhotoStation.PhotoLog": {
171
+		"path": "log.php",
172
+		"minVersion": 1,
173
+		"maxVersion": 1,
174
+		"methods": {
175
+			"1": ["list", "clear", "export"]
176
+		}
177
+	},
178
+	"SYNO.PhotoStation.Path": {
179
+		"path": "path.php",
180
+		"minVersion": 1,
181
+		"maxVersion": 1,
182
+		"methods": {
183
+			"1": ["	", "checkpath"]
184
+		}
185
+	}
186
+}

+ 10
- 0
PhotoStation API/webapi/Query.api 查看文件

@@ -0,0 +1,10 @@
1
+{
2
+	"SYNO.API.Info": {
3
+		"path": "query.php",
4
+		"minVersion": 1,
5
+		"maxVersion": 1,
6
+		"methods": {
7
+			"1": ["query"]
8
+		}
9
+	}
10
+}

+ 4
- 0
PhotoStation API/webapi/about.conf.php 查看文件

@@ -0,0 +1,4 @@
1
+<?php
2
+	define('INFO_CONF_WEBAPI_SERVICE_INCLUDE_PATH', dirname(__FILE__).'/webapi.inc.php');
3
+	define('ABOUT_HTML_PATH', SYNOTHEME_DIR.'/about.html');
4
+	define('TEMPLATE_ABOUT_HTML_PATH', SYNO_PKG_DIR.'/target/etc/about.template.html');

+ 6
- 0
PhotoStation API/webapi/about.inc.php 查看文件

@@ -0,0 +1,6 @@
1
+<?php
2
+	require_once '../include/syno_conf.php';
3
+	require_once 'about.conf.php';
4
+
5
+	require_once INFO_CONF_WEBAPI_SERVICE_INCLUDE_PATH;
6
+	require_once SZ_WEBAPI_CLASS_PATH;

+ 104
- 0
PhotoStation API/webapi/about.php 查看文件

@@ -0,0 +1,104 @@
1
+<?PHP
2
+require_once 'about.inc.php';
3
+
4
+class AboutAPI extends WebAPI
5
+{
6
+	public function __construct()
7
+	{
8
+		parent::__construct(SZ_WEBAPI_API_DESCRIPTION_PATH);
9
+	}
10
+
11
+	protected function Process()
12
+	{
13
+		switch ($this->method) {
14
+			case 'get':
15
+				$this->Get();
16
+				break;
17
+			case 'set':
18
+				$this->Set();
19
+				break;
20
+			case 'set_visibility':
21
+				$this->SetVisibility();
22
+				break;
23
+			default:
24
+				break;
25
+		}
26
+	}
27
+
28
+	private function Get()
29
+	{
30
+		$html = '';
31
+		$hasContent = false;
32
+
33
+		if (file_exists(ABOUT_HTML_PATH)) {
34
+			$html = @file_get_contents(ABOUT_HTML_PATH);
35
+			$hasContent = true;
36
+		} else {
37
+			$html = @file_get_contents(TEMPLATE_ABOUT_HTML_PATH);
38
+		}
39
+
40
+		if ($html === false) {
41
+			$this->SetError(WEBAPI_ERR_UNKNOWN);
42
+			return;
43
+		}
44
+
45
+		$resp = array(
46
+			'html' => $html,
47
+			'has_content' => $hasContent
48
+		);
49
+
50
+		$this->SetResponse($resp);
51
+	}
52
+
53
+	private function Set()
54
+	{
55
+		csSYNOPhotoMisc::CheckSessionTimeOut();
56
+
57
+		$isAdmin = isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']);
58
+
59
+		if (!$isAdmin) {
60
+			$this->SetError(WEBAPI_ERR_NO_PERMISSION);
61
+			return;
62
+		}
63
+
64
+		if (!isset($_REQUEST['html'])) {
65
+			$this->SetError(WEBAPI_ERR_BAD_REQUEST);
66
+			return;
67
+		}
68
+		$html = $_REQUEST['html'];
69
+
70
+		if (!is_dir(SYNOTHEME_DIR) && !mkdir(SYNOTHEME_DIR)) {
71
+			$this->SetError(WEBAPI_ERR_UNKNOWN);
72
+			return;
73
+		}
74
+
75
+		$ret = @file_put_contents(ABOUT_HTML_PATH, $html);
76
+		if ($ret === false) {
77
+			$this->SetError(WEBAPI_ERR_UNKNOWN);
78
+			return;
79
+		}
80
+	}
81
+
82
+	private function SetVisibility()
83
+	{
84
+		csSYNOPhotoMisc::CheckSessionTimeOut();
85
+
86
+		$isAdmin = isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']);
87
+
88
+		if (!$isAdmin) {
89
+			$this->SetError(WEBAPI_ERR_NO_PERMISSION);
90
+			return;
91
+		}
92
+
93
+		if (isset($_REQUEST['hidden'])) {
94
+			$hidden = ('true' === $_REQUEST['hidden'] ? 'on' : 'off');
95
+			csSYNOPhotoMisc::UpdateConfigDB('photo', 'disable_aboutme', $hidden, 'photo_config');
96
+		}
97
+		if (isset($_REQUEST['title'])) {
98
+			csSYNOPhotoMisc::UpdateConfigDB('photo', 'about_me_title', $_REQUEST['title'], 'photo_config');
99
+		}
100
+	}
101
+}
102
+
103
+$api = new AboutAPI();
104
+$api->Run();

+ 3
- 0
PhotoStation API/webapi/album.conf.php 查看文件

@@ -0,0 +1,3 @@
1
+<?php
2
+    define('ALBUM_CONF_WEBAPI_SERVICE_INCLUDE_PATH', dirname(__FILE__).'/webapi.inc.php');
3
+?>

+ 8
- 0
PhotoStation API/webapi/album.inc.php 查看文件

@@ -0,0 +1,8 @@
1
+<?php
2
+    require_once('album.conf.php');
3
+    require_once("../include/syno_conf.php");
4
+    require_once(SYNOPHOTO_INCLUDE_ALBUM_UTIL);
5
+
6
+    require_once(ALBUM_CONF_WEBAPI_SERVICE_INCLUDE_PATH);
7
+    require_once(SZ_WEBAPI_CLASS_PATH);
8
+?>

+ 941
- 0
PhotoStation API/webapi/album.php 查看文件

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

+ 887
- 0
PhotoStation API/webapi/albumutil.php 查看文件

@@ -0,0 +1,887 @@
1
+<?php
2
+
3
+require_once('album.inc.php');
4
+
5
+class AlbumAPIUtil {
6
+
7
+    /**
8
+     * @param $parentAlbumName 
9
+     * @param $data 
10
+     * @return 
11
+     *   id - success 
12
+     *   -1 - general fail
13
+     *   -2 - no upload right
14
+     *   -3 - not admin
15
+     *   -4 - album hsa exist
16
+     */
17
+    static function SYNOPHOTO_ADMIN_AddAlbum($parentAlbumName = '', $params)
18
+    {
19
+        if (null !== $params['sort_by'] || null !== $params['sort_direction'] || null !== $params['conversion'] ||
20
+            null !== $params['allow_comment'] || null !== $params['type'] || null !== $params['password']) {
21
+            if (!isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user'])) {
22
+                return -3;
23
+            }
24
+        } else {
25
+            if (!csSynoPhotoMisc::CheckAlbumUploadable(('' === $parentAlbumName) ? '/' : $parentAlbumName)) {
26
+                return -2;
27
+            }
28
+        }
29
+
30
+
31
+        $album_data = array();
32
+        // only 1st level share can set 'allow_comment' and 'allow conversion'
33
+        if ('' === $parentAlbumName) {
34
+            if (null === $params['allow_comment']) {
35
+                $album_data['comment'] = ('on' === csSYNOPhotoMisc::GetConfigDB("photo", "album_def_allow_comment", "photo_config")) ? 't' : 'f';
36
+            } else {
37
+                $album_data['comment'] = ('true' === $params['allow_comment']) ? 't' : 'f';
38
+            }
39
+            if (null === $params['conversion']) {
40
+                $album_data['conversion'] = ('on' === csSYNOPhotoMisc::GetConfigDB("photo", "def_album_disable_conversion", "photo_config")) ? 'f' : 't';
41
+            } else {
42
+                $album_data['conversion'] = ('true' === $params['conversion']) ? 't' : 'f';
43
+            }
44
+        } else {
45
+            $album_data['comment'] = 'f';
46
+            $album_data['conversion'] = 't';
47
+        }
48
+
49
+        $album_name = stripcslashes($params['name']);
50
+        $album_data['sharename'] = ($parentAlbumName == '') ? $album_name : $parentAlbumName.'/'.$album_name;
51
+        $album_data['title'] = stripcslashes($params['title']);
52
+        $album_data['description'] = stripcslashes($params['description']);
53
+
54
+        $default_album_public = 'f';
55
+        if (isSet($_SESSION[SYNOPHOTO_ADMIN_USER]['system_config']['pkgCfg']['albumdefpublic']) &&
56
+            'yes' == $_SESSION[SYNOPHOTO_ADMIN_USER]['system_config']['pkgCfg']['albumdefpublic']) {
57
+            $default_album_public = 't';
58
+        }
59
+        $album_data['public'] = null === $params['type'] ? $default_album_public : (('public' === $params['type']) ? 't' : 'f');
60
+        $album_data['is_subdir'] = ($parentAlbumName == '') ? 'f' : 't';
61
+		$album_data['password'] = (null !== $params['password']) ? md5($params['password']) : '';
62
+		$inherit = ('' === $album_data['password']) ? $params['inheritParent'] : false;
63
+
64
+        $path = SYNOPHOTO_SERVICE_REAL_DIR.'/'.$album_data['sharename'];
65
+
66
+		//3rd param is true while create album but not assign permission so that inheriting permission of parent album
67
+		//4th param is true while the album creator is not admin so that it need to inherit the 'public' column of parent album,
68
+		//		althought 3rd param is false, AddAlbumToDB still call inherit because of creator is not admin.
69
+        $createID = SYNOPHOTO_ADMIN_AddAlbumToDB($album_data, $path, $inherit, !$inherit);
70
+        if ('album existed' === $createID) {
71
+            return -4;
72
+        }
73
+
74
+        //sort
75
+        $data = array();
76
+        $data['type'] = (null !== $params['sort_by']) ? self::ConvertToSortCode($params['sort_by']) : -1;
77
+        $data['order'] = (null !== $params['sort_by']) ? self::ConvertToSortDirectionCode($params['sort_direction']) : 0;
78
+        csSYNOPhotoAlbum::GetAlbumInstance()->SaveAlbumThumbSortType($album_data['sharename'], $data);
79
+
80
+        csSYNOPhotoDB::GetDBInstance()->SetSessionCache(true);
81
+        return $createID;
82
+    }
83
+
84
+    /**
85
+     * @param $shareName 
86
+     * @param $params 
87
+     * @return 
88
+     *   0 - success
89
+     *  -1 - general fail
90
+     *  -2 - no manage right
91
+     *  -3 - not admin
92
+     */
93
+    static function SYNOPHOTO_ADMIN_UpdateAlbum($shareName, $params)
94
+    {
95
+        // description, title - manage right
96
+        if ((null !== $params['description'] || null !== $params['title']) && null === $params['sort_by'] && null === $params['sort_direction'] &&
97
+            null === $params['allow_comment'] && null === $params['type'] && null === $params['password'] && null === $params['conversion']) {
98
+            if (!csSynoPhotoMisc::CheckAlbumManageable(('' === $shareName) ? '/' : $shareName)) {
99
+                return -2;
100
+            }
101
+        // not changed
102
+        } elseif (null === $params['description'] && null === $params['title'] && null === $params['sort_by'] && null === $params['sort_direction'] &&
103
+                  null === $params['allow_comment'] && null === $params['type'] && null === $params['password'] && null === $params['conversion']) {
104
+            return 0;
105
+        } else {
106
+        // title, sort_by, sort_direction, allow_comment, type, password, conversion - admin
107
+            if (!isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user'])) {
108
+                return -3;
109
+            }
110
+        }
111
+
112
+        if ('' === $shareName) {
113
+            $shareName = '/';
114
+        }
115
+
116
+
117
+        $setCondition = array();
118
+		$setQueryParam = array();
119
+        if (null !== $params['title']) {
120
+            $setValue = "title = ? ";
121
+            array_push($setCondition, $setValue);
122
+			$setQueryParam[] = stripcslashes($params['title']);
123
+        }
124
+        if (null !== $params['description']) {
125
+            $setValue = "description = ?";
126
+            array_push($setCondition, $setValue);
127
+			$setQueryParam[] = stripcslashes($params['description']);
128
+        }
129
+
130
+        $objs = Album::GetBySharename(array($shareName), true);
131
+        $share = $objs[$shareName];
132
+		if ($share) {
133
+			$shareid = $share['shareid'];
134
+		} else {
135
+			return -1;
136
+		}
137
+
138
+		if ('public' === $params['type']) {
139
+			$setValue = "public = 't', password = ''";
140
+			array_push($setCondition, $setValue);
141
+
142
+			/* clear access right table if album is public */
143
+			PHOTO_DB_Query("DELETE FROM " . PHOTO_ACCESS_RIGHT_TABLE . " WHERE shareid = ".$shareid);
144
+		} else if ('private' === $params['type']) {
145
+			$setValue = "public = 'f', password = ''";
146
+			array_push($setCondition, $setValue);
147
+
148
+			/* remove all permissions if album is password */
149
+			PHOTO_DB_Query("DELETE FROM " . PHOTO_ACCESS_RIGHT_TABLE . " WHERE shareid = ".$shareid);
150
+			PHOTO_DB_Query("DELETE FROM " . PHOTO_UPLOAD_RIGHT_TABLE . " WHERE shareid = ".$shareid);
151
+			PHOTO_DB_Query("DELETE FROM " . PHOTO_MANAGE_RIGHT_TABLE . " WHERE shareid = ".$shareid);
152
+
153
+		} else if ('password' === $params['type'] && $params['password'] !== '') {
154
+			$setValue = "public = 'f',password='".md5($params['password'])."'";
155
+			array_push($setCondition, $setValue);
156
+
157
+			/* remove all permissions if album is password */
158
+			PHOTO_DB_Query("DeLETE FROM " . PHOTO_ACCESS_RIGHT_TABLE . " WHERE shareid = ".$shareid);
159
+			PHOTO_DB_Query("DELETE FROM " . PHOTO_UPLOAD_RIGHT_TABLE . " WHERE shareid = ".$shareid);
160
+			PHOTO_DB_Query("DELETE FROM " . PHOTO_MANAGE_RIGHT_TABLE . " WHERE shareid = ".$shareid);
161
+		}
162
+
163
+        if (null !== $params['allow_comment']) {
164
+            if($params['allow_comment'] == 'true') {
165
+                $setValue = "comment = 't'";
166
+            } else {
167
+                $setValue = "comment = 'f'";
168
+            }
169
+            array_push($setCondition, $setValue);
170
+        }
171
+
172
+        if (null !== $params['conversion']) {
173
+            if ($params['conversion'] === 'true' && !$share['conversion']) {
174
+                $needReindex = true;
175
+                $setValue = "conversion = 't'";
176
+                array_push($setCondition, $setValue);
177
+            } elseif ($params['conversion'] !=='true' && $share['conversion']) {
178
+                $setValue = "conversion = 'f'";
179
+                array_push($setCondition, $setValue);
180
+            }
181
+        }
182
+
183
+        $setString = implode(',', $setCondition);
184
+        $query = "UPDATE photo_share SET {$setString} WHERE sharename = ?";
185
+		$setQueryParam[] = $shareName;
186
+        $db_result = PHOTO_DB_Query($query, $setQueryParam);
187
+
188
+        //sort
189
+        $data = array();
190
+        if (null !== $params['sort_by']) {
191
+            $data['type'] = self::ConvertToSortCode($params['sort_by']);
192
+        }
193
+        if (null !== $params['sort_direction']) {
194
+            $data['order'] = self::ConvertToSortDirectionCode($params['sort_direction']);
195
+        }
196
+        if ('/' !== $shareName) {
197
+            csSYNOPhotoAlbum::GetAlbumInstance()->SaveAlbumThumbSortType($shareName, $data);
198
+        }
199
+        csSYNOPhotoDB::GetDBInstance()->SetSessionCache(true);
200
+
201
+        if ($needReindex) {
202
+            @exec("/usr/syno/bin/synoindex -P PhotoStation -R " . escapeshellarg(SYNOPHOTO_SERVICE_REAL_DIR . "/" . $shareName));
203
+        }
204
+
205
+        return 0;
206
+    }
207
+
208
+    static function ConvertToTypeName($type) {
209
+        switch ($type) {
210
+        case 0:
211
+            $typeName = 'album';
212
+            break;
213
+        case 1:
214
+            $typeName = 'photo';
215
+            break;
216
+        case 2:
217
+            $typeName = 'video';
218
+            break;
219
+        default:
220
+            break;
221
+        }
222
+        return $typeName;
223
+    }
224
+
225
+    static function ConvertToSortDirection($value) {
226
+        switch ($value) {
227
+        case -1:
228
+            $sortDirection = 'default';
229
+            break;
230
+        case 0:
231
+            $sortDirection = 'asc';
232
+            break;
233
+        case 1:
234
+            $sortDirection = 'desc';
235
+            break;
236
+        default:
237
+            break;
238
+        }
239
+        return $sortDirection;
240
+    }
241
+
242
+    static function ConvertToSortDirectionCode($name) {
243
+        switch ($name) {
244
+        case 'default':
245
+            $sortCode = -1;
246
+            break;
247
+        case 'asc':
248
+            $sortCode = 0;
249
+            break;
250
+        case 'desc':
251
+            $sortCode = 1;
252
+            break;
253
+        default:
254
+            break;
255
+        }
256
+        return $sortCode;
257
+    }
258
+
259
+    static function ConvertToSortName($type) {
260
+        switch ($type) {
261
+        case -1:
262
+            $sortBy = 'default';
263
+            break;
264
+        case 0:
265
+            $sortBy = 'filename';
266
+            break;
267
+        case 1:
268
+            $sortBy = 'takendate';
269
+            break;
270
+        case 2:
271
+            $sortBy = 'createdate';
272
+            break;
273
+        case 3:
274
+            $sortBy = 'preference';
275
+            break;
276
+        default:
277
+            break;
278
+        }
279
+        return $sortBy;
280
+    }
281
+
282
+    static function ConvertToSortCode($name) {
283
+        switch ($name) {
284
+        case 'default':
285
+            $sortCode = -1;
286
+            break;
287
+        case 'filename':
288
+            $sortCode = 0;
289
+            break;
290
+        case 'takendate':
291
+            $sortCode = 1;
292
+            break;
293
+        case 'createdate':
294
+            $sortCode = 2;
295
+            break;
296
+        case 'preference':
297
+            $sortCode = 3;
298
+            break;
299
+        default:
300
+            break;
301
+        }
302
+        return $sortCode;
303
+    }
304
+
305
+    static function FilterByType($items, $type)
306
+    {
307
+        $filterItems = array();
308
+        foreach ($items as $item) {
309
+            $typeName = self::ConvertToTypeName($item['itemType']);
310
+            if (in_array($typeName, $type)) {
311
+                array_push($filterItems, $item);
312
+            }
313
+        }
314
+        return $filterItems;
315
+    }
316
+
317
+    static function SortItem($photo_video_items, $sortBy, $sortDirection, $offset, $limit) {
318
+        $itemsSort = array();
319
+
320
+        // sort by photo, video
321
+        if ('filename' === $sortBy) {
322
+            if ('asc' === $sortDirection) {
323
+                usort($photo_video_items, 'self::SortFilename_ASC');
324
+            } else {
325
+                usort($photo_video_items, 'self::SortFilename_DESC');
326
+            }
327
+        } elseif ('takendate' === $sortBy) {
328
+            if ('asc' === $sortDirection) {
329
+                usort($photo_video_items, 'self::SortTakendate_ASC');
330
+            } else {
331
+                usort($photo_video_items, 'self::SortTakendate_DESC');
332
+            }
333
+        } elseif ('createdate' === $sortBy) {
334
+            if ('asc' === $sortDirection) {
335
+                usort($photo_video_items, 'self::SortCreatedate_ASC');
336
+            } else {
337
+                usort($photo_video_items, 'self::SortCreatedate_DESC');
338
+            }
339
+        }
340
+
341
+        if (false === $offset && false === $limit) {
342
+            return $photo_video_items;
343
+        }
344
+        // set offset and limit
345
+        $cutItemsSort = array();
346
+        if (0 > (int)$limit) {
347
+            $cutItemsSort = array_slice($photo_video_items, $offset);
348
+        } else {
349
+            $cutItemsSort = array_slice($photo_video_items, $offset, $limit);
350
+        }
351
+
352
+        return $cutItemsSort;
353
+    }
354
+
355
+    private function ExtractItem($items, &$albumItems, &$photo_video_items) {
356
+        $index = count($items);
357
+        foreach ($items as $key => $item) {
358
+            if ('album' !== self::ConvertToTypeName($item['itemType'])) {
359
+                $index = $key;
360
+                break;
361
+            }
362
+        }
363
+        $albumItems = array_slice($items, 0, $index);
364
+        $photo_video_items = array_slice($items, $index);
365
+    }
366
+
367
+    static function SortFilename_ASC($a, $b) {
368
+        if ($a['name'] == $b['name']) {
369
+            return 0;
370
+        }
371
+        return ($a['name'] < $b['name']) ? -1 : 1;
372
+    }
373
+
374
+    static function SortFilename_DESC($a, $b) {
375
+        if ($a['name'] == $b['name']) {
376
+            return 0;
377
+        }
378
+        return ($a['name'] > $b['name']) ? -1 : 1;
379
+    }
380
+
381
+    static function SortTakendate_ASC($a, $b) {
382
+        if ($a['takendate'] == $b['takendate']) {
383
+            return ($a['name'] < $b['name']) ? -1 : 1;
384
+        }
385
+        return ($a['takendate'] < $b['takendate']) ? -1 : 1;
386
+    }
387
+
388
+    static function SortTakendate_DESC($a, $b) {
389
+        if ($a['takendate'] == $b['takendate']) {
390
+            return ($a['name'] > $b['name']) ? -1 : 1;
391
+        }
392
+        return ($a['takendate'] > $b['takendate']) ? -1 : 1;
393
+    }
394
+
395
+    static function SortCreatedate_ASC($a, $b) {
396
+        if ($a['createdate'] == $b['createdate']) {
397
+            return ($a['name'] < $b['name']) ? -1 : 1;
398
+        }
399
+        return ($a['createdate'] < $b['createdate']) ? -1 : 1;
400
+    }
401
+
402
+    static function SortCreatedate_DESC($a, $b) {
403
+        if ($a['createdate'] == $b['createdate']) {
404
+            return ($a['name'] > $b['name']) ? -1 : 1;
405
+        }
406
+        return ($a['createdate'] > $b['createdate']) ? -1 : 1;
407
+    }
408
+
409
+    static function SortPreference($items, $sharePath) {
410
+        $sort = csSYNOPhotoAlbum::GetAlbumThumbSortType($sharePath);
411
+        $list = array();
412
+        $result = array();
413
+        if (!empty($sort['list'])) {
414
+            $list = $sort['list'];
415
+            $preArr = array();
416
+            foreach ($list as $key) {
417
+                foreach ($items as $item) {
418
+                    if ($key === $item['name']) {
419
+                        array_push($result, $item);
420
+                    }
421
+                }
422
+            }
423
+            
424
+            // rest photo and video item
425
+            foreach ($items as $item) {
426
+                if (!in_array($item, $result)) {
427
+                    array_push($result, $item);
428
+                }
429
+            }
430
+
431
+        } else {
432
+            $result = $items;
433
+        }
434
+        
435
+        return $result;
436
+    }
437
+
438
+    /*
439
+     *  @return [string] broken / small, large
440
+     */
441
+    static function GetThumbStatus($dbPath, $needSize, $orig_resolutionx, $orig_resolutiony, $blThumbRotated, $blConversion) {
442
+        /* special case for gif, return original file */
443
+		$path = SYNOPHOTO_SERVICE_REAL_DIR_PREFIX.$dbPath;
444
+        $info = pathinfo($path);
445
+        if ('gif' === strtolower($info['extension'])) {
446
+            $ret['status'] = 'small,large';
447
+            if ($needSize) {
448
+                $imgInfo = @getImageSize($path);
449
+				$mtime = @filemtime($path);
450
+                $ret['size']['preview']['resolutionx'] = $imgInfo[0];
451
+                $ret['size']['preview']['resolutiony'] = $imgInfo[1];
452
+                $ret['size']['preview']['mtime'] = $mtime;
453
+                $ret['size']['small']['resolutionx'] = $imgInfo[0];
454
+                $ret['size']['small']['resolutiony'] = $imgInfo[1];
455
+                $ret['size']['small']['mtime'] = $mtime;
456
+                $ret['size']['large']['resolutionx'] = $imgInfo[0];
457
+                $ret['size']['large']['resolutiony'] = $imgInfo[1];
458
+                $ret['size']['large']['mtime'] = $mtime;
459
+            }
460
+            return $ret;
461
+        }
462
+
463
+        if ($needSize) {
464
+            $ret['size']['preview']['resolutionx'] = 0;
465
+            $ret['size']['preview']['resolutiony'] = 0;
466
+            $ret['size']['small']['resolutionx'] = 0;
467
+            $ret['size']['small']['resolutiony'] = 0;
468
+            $ret['size']['large']['resolutionx'] = 0;
469
+            $ret['size']['large']['resolutiony'] = 0;
470
+        }
471
+
472
+        $file_name = basename($path);
473
+        $dir = dirname($path);
474
+
475
+        if (SYNOPhotoEA::checkFilePathByDirFile($dir, $file_name, SYNOPhotoEA::FILE_THUMB_M_FAIL, $thumbSmallBroken) &&
476
+            SYNOPhotoEA::checkFilePathByDirFile($dir, $file_name, SYNOPhotoEA::FILE_THUMB_XL_FAIL, $thumbLargeBroken)) {
477
+            $ret['status'] = 'broken';
478
+        } else {
479
+            $thumbStatus = array();
480
+			$hasSmall = $hasLarge = false;
481
+			if (!$blConversion) {
482
+				$thumbStatus[] = 'disable';
483
+			}
484
+			if (SYNOPhotoEA::checkFilePathByDirFile($dir, $file_name, SYNOPhotoEA::FILE_THUMB_M, $thumbSmallPath)) {
485
+				$hasSmall = true;
486
+                if ($needSize) {
487
+					$ret['size']['small'] = self::CalculateThumbSize($orig_resolutionx, $orig_resolutiony, $blThumbRotated, SYNOPHOTO_THUMBMEDIUM_WIDTH);
488
+                    $ret['size']['small']['mtime'] = @filemtime($thumbSmallPath);
489
+                }
490
+            }
491
+            if (SYNOPhotoEA::checkFilePathByDirFile($dir, $file_name, SYNOPhotoEA::FILE_THUMB_PREVIEW, $thumbPreviewPath)) {
492
+                $thumbStatus[] = 'preview';
493
+                if ($needSize) {
494
+                    if ($hasSmall) {
495
+                        $ret['size']['preview'] = self::CalculateThumbSize($orig_resolutionx, $orig_resolutiony, $blThumbRotated, SYNOPHOTO_THUMBPREVIEW_WIDTH);
496
+                    } else {
497
+                        $ret['size']['preview'] = self::GetThumbSize($path, $thumbPreviewPath);
498
+                    }
499
+                    $ret['size']['preview']['mtime'] = @filemtime($thumbPreviewPath);
500
+				}
501
+
502
+				//use preview-thumb as small-thumb in thumbnail-less browsing
503
+				if (!$blConversion && !$hasSmall) {
504
+					$hasSmall = true;
505
+					if ($needSize) {
506
+						$ret['size']['small'] = $ret['size']['preview'];
507
+						$ret['size']['small']['mtime'] = $ret['size']['preview']['mtime'];
508
+					}
509
+				}
510
+			}
511
+			if (SYNOPhotoEA::checkFilePathByDirFile($dir, $file_name, SYNOPhotoEA::FILE_THUMB_XL, $thumbLargePath)) {
512
+				$hasLarge = true;
513
+                if ($needSize) {
514
+					$ret['size']['large'] = self::CalculateThumbSize($orig_resolutionx, $orig_resolutiony, $blThumbRotated, SYNOPHOTO_THUMBXLARGE_WIDTH);
515
+                    $ret['size']['large']['mtime'] = @filemtime($thumbLargePath);
516
+                }
517
+            }
518
+
519
+			if (!$hasLarge && SYNOPhotoEA::checkFilePathByDirFile($dir, $file_name, SYNOPhotoEA::FILE_THUMB_LARGE_PREVIEW, $thumbLargePreviewPath)) {
520
+				$thumbStatus[] = 'large';
521
+				if ($needSize) {
522
+					$ret['size']['large'] = self::GetThumbSize($path, $thumbLargePreviewPath);
523
+					$ret['size']['large']['mtime'] = @filemtime($thumbLargePreviewPath);
524
+				}
525
+			}
526
+
527
+			//use original file as large thumb in thumbnail-less browsing
528
+			if (!$blConversion && !$hasLarge && in_array(strtolower($info['extension']), array('jpeg', 'bmp', 'png', 'jpg', 'jpe'/*, 'image/tiff'*/))) {
529
+				$hasLarge = true;
530
+				if ($needSize) {
531
+					$ret['size']['large']['resolutionx'] = $orig_resolutionx;
532
+					$ret['size']['large']['resolutiony'] = $orig_resolutiony;
533
+					$ret['size']['large']['mtime'] = @filemtime($path);
534
+				}
535
+				$thumbStatus[] = "large_original";
536
+			}
537
+
538
+			if ($hasSmall) {
539
+				$thumbStatus[] = 'small';
540
+			}
541
+			if ($hasLarge) {
542
+				$thumbStatus[] = 'large';
543
+			}
544
+
545
+            $ret['status'] = implode(',', $thumbStatus);
546
+        }
547
+
548
+        return $ret;
549
+	}
550
+
551
+	static function CalculateThumbSize($orig_resolutionx, $orig_resolutiony, $blThumbRotated, $thumbSize)
552
+	{
553
+		$thumb['resolutionx'] = $thumb['resolutiony'] = 0;
554
+
555
+		if (0 >= $orig_resolutionx || 0 >= $orig_resolutiony || 0 >= $thumbSize) {
556
+			goto End;
557
+		}
558
+
559
+		$ratio = $orig_resolutionx / $orig_resolutiony;
560
+		if (0 >= $ratio) {
561
+			goto End;
562
+		}
563
+
564
+		$thumb['resolutionx'] = $orig_resolutionx;
565
+		$thumb['resolutiony'] = $orig_resolutiony;
566
+
567
+		if ($thumbSize < $orig_resolutionx && $thumbSize < $orig_resolutiony) {
568
+			if ($orig_resolutionx >= $orig_resolutiony) {
569
+				$thumb['resolutionx'] = ceil($thumbSize * $ratio);
570
+				$thumb['resolutiony'] = $thumbSize;
571
+			} else {
572
+				$thumb['resolutionx'] = $thumbSize;
573
+				$thumb['resolutiony'] = ceil($thumbSize / $ratio);
574
+			}
575
+		}
576
+
577
+		if ($blThumbRotated) {
578
+			$tmp = $thumb['resolutionx'];
579
+			$thumb['resolutionx'] = $thumb['resolutiony'];
580
+			$thumb['resolutiony'] = $tmp;
581
+		}
582
+	End:
583
+		return $thumb;
584
+	}
585
+
586
+    static function GetThumbSize($imgPath, $thumbPath) {
587
+        $thumbInfo = @getImageSize($thumbPath);
588
+        $ret = array(
589
+            'resolutionx' => $thumbInfo[0],
590
+            'resolutiony' => $thumbInfo[1]
591
+        );
592
+
593
+        $info = pathinfo($imgPath);
594
+        if (!in_array(strtolower($info['extension']), array('jpeg', 'bmp', 'png', 'jpg', 'jpe'/*, 'image/tiff'*/))) {
595
+            goto END;
596
+        }
597
+
598
+        $imgInfo = @getImageSize($imgPath);
599
+        $rotated = ($thumbInfo[0] > $thumbInfo[1] && $imgInfo[0] <= $imgInfo[1]) || ($thumbInfo[0] <= $thumbInfo[1] && $imgInfo[0] > $imgInfo[1]);
600
+
601
+        if ($rotated && $thumbInfo[0] > $imgInfo[1]) {
602
+            $ret['resolutionx'] = $imgInfo[1];
603
+            $ret['resolutiony'] = $imgInfo[0];
604
+        } else if (!$rotated && $thumbInfo[0] > $imgInfo[0]) {
605
+            $ret['resolutionx'] = $imgInfo[0];
606
+            $ret['resolutiony'] = $imgInfo[1];
607
+        }
608
+
609
+    END:
610
+        return $ret;
611
+    }
612
+
613
+	static function FillThumbStatus(&$item, $coverDBPath, $needThumbSize, $orig_resolutionx, $orig_resolutiony, $blThumbRotated, $blConversion)
614
+	{
615
+		if ('' !== $coverDBPath) {
616
+			$thumbnail = self::GetThumbStatus($coverDBPath, $needThumbSize, $orig_resolutionx, $orig_resolutiony, $blThumbRotated, $blConversion);
617
+		} else {
618
+			$thumbnail = array('status' => 'default');
619
+			if (!$blConversion) {
620
+				$thumbnail['status'] .= ',disable';
621
+			}
622
+			if ($needThumbSize) {
623
+				$thumbnail['size'] = array(
624
+					'preview' => array('resolutionx' => 0, 'resolutiony' => 0),
625
+					'small' => array('resolutionx' => 0, 'resolutiony' => 0),
626
+					'large' => array('resolutionx' => 0, 'resolutiony' => 0)
627
+				);
628
+			}
629
+		}
630
+
631
+		$item['thumbnail_status'] = $thumbnail['status'];
632
+		if ($needThumbSize) {
633
+			if (!is_array($item['additional'])) {
634
+				$item['additional'] = array();
635
+			}
636
+			$item['additional']['thumb_size'] = $thumbnail['size'];
637
+			$item['additional']['thumb_size']['sig'] = bin2hex($coverDBPath);
638
+		}
639
+	}
640
+
641
+	static function GetCoverResolutionRotation($coverPath) {
642
+		if ('' === $coverPath) {
643
+			return array(0, 0, false);
644
+		}
645
+		$file = csSYNOPhotoMisc::IsPhotoFile($coverPath) ?
646
+			File::GetPhotoByDBPath(array($coverPath)) : File::GetVideoByDBPath(array($coverPath));
647
+
648
+		$info = $file[$coverPath];
649
+
650
+		return array((int)$info['resolutionx'], (int)$info['resolutiony'], 0 !== (int)$info['version'] % 2);
651
+	}
652
+
653
+    static function GetAlbumPermission($sharePath) {
654
+        $permission['browse'] = csSYNOPhotoMisc::CheckAlbumAccessible($sharePath);
655
+        $permission['upload'] = csSYNOPhotoMisc::CheckAlbumUploadable($sharePath);
656
+        $permission['manage'] = csSYNOPhotoMisc::CheckAlbumManageable($sharePath);
657
+        return $permission;
658
+    }
659
+
660
+	static function GetUnConfirmItemList($limit=false, $offset=false)
661
+	{
662
+        $result = array();
663
+
664
+		$limitOffsetString = PHOTO_DB_GetLimitOffsetString($limit, $offset);
665
+
666
+        $query = "SELECT photo_image.path FROM photo_image_label LEFT JOIN photo_image ON photo_image_label.image_id=photo_image.id WHERE status='f'";
667
+        $queryCount = "SELECT COUNT(DISTINCT photo_image.path) FROM photo_image_label LEFT JOIN photo_image ON photo_image_label.image_id=photo_image.id WHERE status='f'";
668
+        $albumCondition = csSYNOPhotoMisc::GetAccessibleAlbumQueryConditionWithExcludeFormat();
669
+
670
+        if(!$albumCondition['albumCond']) {
671
+            return $result;
672
+        }
673
+
674
+        $query .= " AND {$albumCondition['albumCond']}";
675
+        $query .= " ORDER BY photo_image.create_time DESC ";
676
+        $query .= $limitOffsetString;
677
+        $queryCount .= " AND {$albumCondition['albumCond']}";
678
+        $db_result = PHOTO_DB_Query($query, $albumCondition['sqlParam']);
679
+
680
+        while (false !== ($row = PHOTO_DB_FetchRow($db_result))) {
681
+            if ($row['path']) {
682
+                $result[SYNOPHOTO_SERVICE_REAL_DIR_PREFIX.$row['path']] = SYNOPHOTO_ITEM_TYPE_PHOTO;
683
+            }
684
+        }
685
+        $db_result = PHOTO_DB_Query($queryCount, $sqlParam);
686
+        $row = PHOTO_DB_FetchRow($db_result);
687
+        $result['total'] = $row[0];
688
+        return $result;
689
+    }
690
+
691
+    /*
692
+     *  @param $videoPath video db path
693
+     */
694
+    static function GetConvertedVideoInfo($dPath)
695
+    {
696
+        $query = "SELECT * FROM video_convert WHERE video_path = ?";
697
+        $sqlParam = array($dPath);
698
+        $db_result = PHOTO_DB_Query($query, $sqlParam);
699
+        $videoArr = array();
700
+        while ($videoInfo = PHOTO_DB_FetchRow($db_result)) {
701
+            $videoQuality['id'] = bin2hex(SYNOPHOTO_SERVICE_REAL_DIR_PREFIX.$videoInfo['convert_file_path']);
702
+            $videoQuality['container'] = $videoInfo['container_type'];
703
+            $videoQuality['vcodec'] = $videoInfo['vcodec'];
704
+			$videoQuality['acodec'] = $videoInfo['acodec'];
705
+			$videoQuality['filesize'] = (int)$videoInfo['filesize'];
706
+            $videoQuality['resolutionx'] = (int)$videoInfo['resolutionx'];
707
+            $videoQuality['resolutiony'] = (int)$videoInfo['resolutiony'];
708
+            $videoQuality['video_bitrate'] = (int)$videoInfo['video_bitrate'];
709
+            $videoQuality['audio_bitrate'] = (int)$videoInfo['audio_bitrate'];
710
+            $videoQuality['video_profile'] = (int)$videoInfo['video_profile'];
711
+            $videoQuality['video_level'] = (int)$videoInfo['video_level'];
712
+            $videoQuality['profile_name'] = self::GetConvertedName($videoInfo['convert_file_path']);
713
+            array_push($videoArr, $videoQuality);
714
+        }
715
+        return $videoArr;
716
+    }
717
+
718
+    static function GetConvertedName($convertPath)
719
+    {
720
+        $fileName = basename($convertPath);
721
+        $pool = array(SYNOPhotoEA::FILE_FILM_FLV, SYNOPhotoEA::FILE_FILM_H264_MP4, SYNOPhotoEA::FILE_FILM_H_MP4, SYNOPhotoEA::FILE_FILM_M_MP4, SYNOPhotoEA::FILE_FILM_L_MP4,
722
+                      SYNOPhotoEA::FILE_FILM_MOBILE_MP4, SYNOPhotoEA::FILE_FILM_MOBILE_IPHONE, SYNOPhotoEA::FILE_FILM_MOBILE_ANDROID);
723
+        foreach ($pool as $name) {
724
+            $oldName = SYNOPhotoEA::SYNO_PHOTO_PREFIX_OLD.$name;
725
+            $newName = SYNOPhotoEA::SYNO_PHOTO_PREFIX.$name;
726
+            if ($oldName === $fileName || $newName === $fileName) {
727
+                if (SYNOPhotoEA::FILE_FILM_FLV === $name) {
728
+                    $ret = 'flv';
729
+                } elseif (SYNOPhotoEA::FILE_FILM_H264_MP4 === $name) {
730
+                    $ret = 'orig_h264';
731
+                } elseif (SYNOPhotoEA::FILE_FILM_MPEG4_MP4 === $name) {
732
+                    $ret = 'orig_mp4';
733
+                } elseif (SYNOPhotoEA::FILE_FILM_H_MP4 === $name) {
734
+                    $ret = 'high';
735
+                } elseif (SYNOPhotoEA::FILE_FILM_M_MP4 === $name) {
736
+                    $ret = 'medium';
737
+                } elseif (SYNOPhotoEA::FILE_FILM_L_MP4 === $name) {
738
+                    $ret = 'low';
739
+                } elseif (SYNOPhotoEA::FILE_FILM_MOBILE_MP4 === $name) {
740
+                    $ret = 'mobile';
741
+                } elseif (SYNOPhotoEA::FILE_FILM_MOBILE_IPHONE === $name) {
742
+                    $ret = 'orig_iphone';
743
+                } elseif (SYNOPhotoEA::FILE_FILM_MOBILE_ANDROID === $name) {
744
+                    $ret = 'orig_android';
745
+                } elseif (SYNOPhotoEA::FILE_FILM_CONVERT_MPEG4_MP4 === $name) {
746
+                    $ret = 'convert_mp4';
747
+                }
748
+                break;
749
+            }
750
+        }
751
+        return $ret;
752
+    }
753
+
754
+    static function IsAlbumCommentalbGlobal($checkSession = false)
755
+    {
756
+        $blAllowUserComment = ('on' === csSYNOPhotoMisc::GetConfigDB("photo", "allow_user_comment", "photo_config"));
757
+        $blAllowGuestComment = ('on' === csSYNOPhotoMisc::GetConfigDB("photo", "allow_guest_comment", "photo_config"));
758
+
759
+        if ($blAllowGuestComment) {
760
+            return true;
761
+        } elseif ($blAllowUserComment) {
762
+            if ($checkSession) {
763
+                csSYNOPhotoMisc::CheckSessionTimeOut(true);
764
+                return true;
765
+            } else {
766
+                if (isset($_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_user']) || isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user'])) {
767
+                    return true;
768
+                }
769
+            }
770
+        }
771
+
772
+        return false;
773
+    }
774
+
775
+    static function IsShowAlbumHit() {
776
+        $value = csSYNOPhotoMisc::GetConfigDB("photo", "show_album_hit", "photo_config");
777
+        $show_album_hit = 'off' === $value ? false : true ;
778
+        return $show_album_hit;
779
+    }
780
+
781
+	static function FormAlbum($album_db_object, $global_allow_comment, $show_hits) {
782
+		$name = pathinfo($album_db_object['sharename'], PATHINFO_BASENAME);
783
+		$res = array(
784
+			'sharepath' => $album_db_object['sharename'],
785
+			'name' => $name,
786
+			'title' => $album_db_object['title'] ? $album_db_object['title'] : $name,
787
+			'description' => $album_db_object['description'],
788
+			'hits' => $show_hits ? $album_db_object['hits'] : 0,
789
+			'type' => $album_db_object['public'] ? 'public' : ($album_db_object['password'] ? 'password' : 'private'),
790
+			'conversion' => $album_db_object['conversion'],
791
+			'allow_comment' => $global_allow_comment
792
+		);
793
+
794
+		return $res;
795
+	}
796
+
797
+	static function FormAdditional($type, $item, $params) {
798
+        if ('album' === $type) {
799
+            $sharePath = $item['sharename'];
800
+			$shareid = $item['shareid'];
801
+            if (in_array('album_sorting', $params['additional'])) {
802
+                $sortData = csSYNOPhotoAlbum::GetAlbumInstance()->GetAlbumThumbSortType($sharePath);
803
+                $sortBy = AlbumAPIUtil::ConvertToSortName($sortData['type']);
804
+                $additional['album_sorting']['sort_by'] = $sortBy;
805
+                $additional['album_sorting']['sort_direction'] = AlbumAPIUtil::ConvertToSortDirection($sortData['order']);
806
+                $additional['album_sorting']['has_preference_sort'] = !empty($sortData);
807
+            }
808
+            if (in_array('album_permission', $params['additional'])) {
809
+                // here
810
+                $additional['album_permission'] = AlbumAPIUtil::GetAlbumPermission($sharePath);
811
+            }
812
+            if (in_array('item_count', $params['additional'])) {
813
+                $additional['item_count'] = array(
814
+					'photo' => Album::GetPhotoCount($item['shareid']),
815
+					'video' => Album::GetVideoCount($item['shareid'])
816
+                );
817
+            }
818
+        } elseif ('photo' === $type) {
819
+            if (in_array('photo_exif', $params['additional'])) {
820
+                $photoInfo = $item;
821
+                $additional['photo_exif']['takendate'] = $photoInfo['timetaken'];
822
+                $additional['photo_exif']['camera'] = $photoInfo['camera_make'];
823
+                $additional['photo_exif']['camera_model'] = $photoInfo['camera_model'];
824
+                $additional['photo_exif']['exposure'] = $photoInfo['exposure'];
825
+                $additional['photo_exif']['aperture'] = $photoInfo['aperture'];
826
+                $additional['photo_exif']['iso'] = (int)$photoInfo['iso'];
827
+				$additional['photo_exif']['gps'] = json_decode($photoInfo['gps'], true);
828
+				$additional['photo_exif']['focal_length'] = $photoInfo['focal_length_v2'];
829
+				$additional['photo_exif']['lens'] = $photoInfo['lens_v2'];
830
+				$additional['photo_exif']['flash'] = $photoInfo['flash_v2'];
831
+            }
832
+        } elseif ('video' === $type) {
833
+            if (in_array('video_codec', $params['additional'])) {
834
+                $videoInfo = $item;
835
+                $additional['video_codec']['container'] = $videoInfo['container_type'];
836
+                $additional['video_codec']['vcodec'] = $videoInfo['video_codec'];
837
+                $additional['video_codec']['acodec'] = $videoInfo['audio_codec'];
838
+                $additional['video_codec']['resolutionx'] = (int)$videoInfo['resolutionx'];
839
+                $additional['video_codec']['resolutiony'] = (int)$videoInfo['resolutiony'];
840
+                $additional['video_codec']['frame_bitrate'] = (int)$videoInfo['frame_bitrate'];
841
+                $additional['video_codec']['video_bitrate'] = (int)$videoInfo['video_bitrate'];
842
+                $additional['video_codec']['audio_bitrate'] = (int)$videoInfo['audio_bitrate'];
843
+            }
844
+            if (in_array('video_quality', $params['additional'])) {
845
+                $additional['video_quality'] = AlbumAPIUtil::GetConvertedVideoInfo($item['path']);
846
+            }
847
+        }
848
+
849
+		if (in_array('file_location', $params['additional'])) {
850
+			$additional['file_location'] = 'album' === $type ?
851
+				$sharePath : substr($item['path'], strlen(SYNOPHOTO_SERVICE_REAL_DIR_PATH));
852
+		}
853
+        return $additional;
854
+	}
855
+
856
+	static function GetRootAlbumInfo($params){
857
+        $info['description'] = '';
858
+        $info['sharepath'] = '/';
859
+        $info['name'] = '/';
860
+        $info['title'] = csSYNOPhotoMisc::GetConfigDB("photo", "photo_page_title", "photo_config");
861
+        $info['hits'] = null;
862
+        $info['allow_comment'] = ('on' === csSYNOPhotoMisc::GetConfigDB("photo", "album_def_allow_comment", "photo_config")) ? true : false;
863
+        $info['type'] = 'public';
864
+        $info['conversion'] = csSYNOPhotoMisc::IsAlbumAllowConversion('/');
865
+
866
+        $item['id'] = null;
867
+        $item['info'] = $info;
868
+        if (in_array('album_sorting', $params['additional'])) {
869
+            $value = csSYNOPhotoMisc::GetConfigDB("album", "album_order_type", "photo_config");
870
+            $additional['album_sorting']['sort_by'] = ('1' === $value) ? 'preference' : 'filename';
871
+            $value = csSYNOPhotoMisc::GetConfigDB("album", "album_order_type_is_desc", "photo_config");
872
+            $additional['album_sorting']['sort_direction'] = ('1' === $value) ? 'desc' : 'asc';
873
+            $value = csSYNOPhotoAlbum::GetAlbumThumbSortType($info['sharepath']);
874
+            $additional['album_sorting']['has_preference_sort'] = !empty($value);
875
+        }
876
+        if (in_array('album_permission', $params['additional'])) {
877
+            $permission = self::GetAlbumPermission('/');
878
+            $additional['album_permission'] = $permission;
879
+        }
880
+        if (null !== $additional) {
881
+            $item['additional'] = $additional;
882
+        }
883
+        return $item;
884
+	}
885
+}
886
+
887
+?>

+ 3
- 0
PhotoStation API/webapi/auth.conf.php 查看文件

@@ -0,0 +1,3 @@
1
+<?php
2
+    define('LOGIN_CONF_WEBAPI_SERVICE_INCLUDE_PATH', dirname(__FILE__).'/webapi.inc.php');
3
+?>

+ 7
- 0
PhotoStation API/webapi/auth.inc.php 查看文件

@@ -0,0 +1,7 @@
1
+<?php
2
+    require_once('auth.conf.php');
3
+    require_once("../include/syno_conf.php");
4
+
5
+    require_once(LOGIN_CONF_WEBAPI_SERVICE_INCLUDE_PATH);
6
+    require_once(SZ_WEBAPI_CLASS_PATH);
7
+?>

+ 170
- 0
PhotoStation API/webapi/auth.php 查看文件

@@ -0,0 +1,170 @@
1
+<?php
2
+
3
+require_once('auth.inc.php');
4
+require_once('authutil.php');
5
+require_once('albumutil.php');
6
+require_once('../include/shared_album.php');
7
+
8
+class AuthAPI extends WebAPI {
9
+    function __construct() {
10
+        parent::__construct(SZ_WEBAPI_API_DESCRIPTION_PATH);
11
+    }
12
+
13
+    protected function Process() {
14
+        if (!strcasecmp($this->method, "login")) {
15
+            $this->Login();
16
+        } elseif (!strcasecmp($this->method, "logout")) {
17
+            $this->Logout();
18
+        } elseif (!strcasecmp($this->method, "checkauth")) {
19
+            $this->CheckAuth();
20
+        }
21
+    }
22
+
23
+    private function GetConfig() {
24
+        $resp = array();
25
+        // php session id
26
+        $resp['sid'] = session_id();
27
+
28
+        // get username
29
+        // not personal photo
30
+        if ('root' === SYNOPHOTO_ADMIN_USER) {
31
+            $resp['username'] = (isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']) && !isset($_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_user'])) ? 'admin' : $_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_user'];
32
+        } else {
33
+        // personal photo
34
+            $resp['username'] = (isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']) && !isset($_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_user'])) ? SYNOPHOTO_ADMIN_NAME : $_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_user'];
35
+        }
36
+        $resp['reg_syno_user'] = isset($_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_user']);
37
+
38
+        // get manager
39
+        $resp['is_admin'] = isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']);
40
+
41
+        // get commentable
42
+        $resp['allow_comment'] = AlbumAPIUtil::IsAlbumCommentalbGlobal();
43
+
44
+        $permission['browse'] = csSYNOPhotoMisc::CheckAlbumAccessible('/');
45
+        $permission['upload'] = csSYNOPhotoMisc::CheckAlbumUploadable('/');
46
+        $permission['manage'] = csSYNOPhotoMisc::CheckAlbumManageable('/');
47
+        $resp['permission'] = $permission;
48
+
49
+        $resp['enable_face_recog'] = false;
50
+        if (isset($_SESSION[SYNOPHOTO_ADMIN_USER]['system_config']['pkgCfg']['runfacerecognition']) &&
51
+	  'yes' == $_SESSION[SYNOPHOTO_ADMIN_USER]['system_config']['pkgCfg']['runfacerecognition'] &&
52
+          false === strstr($_SESSION[SYNOPHOTO_ADMIN_USER]['system_config']['defCfg']['unique'], '88f628')) {
53
+            $resp['enable_face_recog'] = true;
54
+        }
55
+
56
+		$resp['allow_public_share'] = SharedAlbum::CheckPublicSharePermission();
57
+
58
+        return $resp;
59
+    }
60
+
61
+    private function PhotoLogin() {
62
+        $error_code = WEBAPI_ERR_NONE;
63
+		$command = "/usr/syno/bin/synoautoblock";
64
+		$params = ["--deny", $_SERVER['REMOTE_ADDR']];
65
+		csSYNOPhotoMisc::SystemCmd($command, $params, $retval);
66
+		$command = "";
67
+		$params = array();
68
+        if ($retval == 1) {
69
+            $error_code = PHOTOSTATION_AUTH_LOGIN_NOPRIVILEGE;
70
+        //if username is ADMIN
71
+        } elseif (strtoupper($_REQUEST['username']) == "ADMIN" || strtoupper($_REQUEST['username']) == strtoupper(SYNOPHOTO_ADMIN_NAME)) {
72
+            $ret = AuthAPIUtil::SYNOPHOTO_LOGIN_ValidateAdmin();
73
+            if(1 == $ret) {
74
+                $command = "/usr/syno/bin/synoautoblock";
75
+				$params = ["--reset", $_SERVER['REMOTE_ADDR']];
76
+			} else {
77
+                $error_code = PHOTOSTATION_AUTH_LOGIN_ERROR;
78
+				$command = "/usr/syno/bin/synoautoblock";
79
+				$params = ["--attempt", $_SERVER['REMOTE_ADDR']];
80
+            }
81
+        } else {
82
+        // not ADMIN
83
+            // PhotoStation user
84
+            if ("0" == $_SESSION[SYNOPHOTO_ADMIN_USER]['photo_config']['global']['account_system'] || 'root' != SYNOPHOTO_ADMIN_USER){
85
+                $ret = AuthAPIUtil::SYNOPHOTO_LOGIN_ValidateUser();
86
+            } else {
87
+            // DSM user
88
+                $ret = AuthAPIUtil::SYNOPHOTO_LOGIN_ValidateDsmUser();
89
+            }
90
+
91
+            if($ret == 1) {
92
+				$command = "/usr/syno/bin/synoautoblock";
93
+				$params = ["--reset", $_SERVER['REMOTE_ADDR']];
94
+            } elseif ($ret == -1) {
95
+                $error_code = PHOTOSTATION_AUTH_LOGIN_DISABLE_ACCOUNT;  // for PhotoStation user
96
+            } elseif ($ret == -3) {
97
+                $error_code = PHOTOSTATION_AUTH_LOGIN_GUEST_ERROR;  // for DSM user
98
+            } else {
99
+                $error_code = PHOTOSTATION_AUTH_LOGIN_ERROR;
100
+				$command = "/usr/syno/bin/synoautoblock";
101
+				$params = ["--attempt", $_SERVER['REMOTE_ADDR']];
102
+            }
103
+        }
104
+
105
+        if ($command != "") {
106
+            putenv('SYNOAUTOBLOCK_SERVICE=Photo Station');
107
+			csSYNOPhotoMisc::SystemCmd($command, $params, $retval);
108
+            putenv('SYNOAUTOBLOCK_SERVICE');
109
+        }
110
+        return $error_code;
111
+    }
112
+
113
+    private function IsEnableDemoMode()
114
+    {
115
+        $isDemoMode = csSYNOPhotoMisc::GetConfigFile(SYNO_CNF_FILE, 'enable_demomode');
116
+        if ('yes' === $isDemoMode['enable_demomode']) {
117
+            return true;
118
+        }
119
+        return false;
120
+    }
121
+
122
+    private function PhotoLogout() {
123
+        unset($_SESSION[SYNOPHOTO_ADMIN_USER]);
124
+    }
125
+
126
+    private function Login() {
127
+		if (!isset($_REQUEST['username'])) {
128
+			$this->SetError(WEBAPI_ERR_BAD_REQUEST);
129
+			goto End;
130
+		}
131
+        if (!$this->IsEnableDemoMode())  {
132
+            $error_code = $this->PhotoLogin();
133
+            if (WEBAPI_ERR_NONE != $error_code) {
134
+                $this->SetError($error_code);
135
+                goto End;
136
+            }
137
+        }
138
+        csSYNOPhotoDB::GetDBInstance()->SetSessionCache(true);
139
+		$resp = $this->GetConfig();
140
+		$this->SetResponse($resp);
141
+		if(isset($_REQUEST['remember_me'])){
142
+			setcookie('PHPSESSID', session_id(), time() + 60*60*24*30, '/');
143
+			setcookie('photo_remember_me', 1, time() + 60*60*24*30, '/');
144
+		}
145
+    End:
146
+        return;
147
+    }
148
+
149
+    private function Logout() {
150
+        $this->PhotoLogout();
151
+		setcookie('PHPSESSID', '', time() - 180, '/');
152
+		setcookie('photo_remember_me', '', time() - 180, '/');
153
+    }
154
+
155
+    private function CheckAuth() {
156
+        $resp = $this->GetConfig();
157
+		AuthAPIUtil::SYNOPHOTO_LOGIN_UpdateUserSession($resp['username']);
158
+		if ($resp['is_admin'] !== isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user'])) {
159
+			csSYNOPhotoDB::GetDBInstance()->SetSessionCache(true);
160
+			$resp = $this->GetConfig();
161
+		}
162
+		$this->SetResponse($resp);
163
+	}
164
+}
165
+
166
+$api = new AuthAPI();
167
+$api->Run();
168
+
169
+?>
170
+

+ 229
- 0
PhotoStation API/webapi/authutil.php 查看文件

@@ -0,0 +1,229 @@
1
+<?php
2
+
3
+require_once('auth.inc.php');
4
+
5
+class AuthAPIUtil {
6
+
7
+    static function SYNOPHOTO_LOGIN_USERDATA_PARSER($pUser)
8
+    {
9
+        global $SYNOPHOTO_DSM_USER_INFO;
10
+        $result = array();
11
+        if (!is_array($pUser)) {
12
+            return null;
13
+        }
14
+        $i = 0;
15
+		$groups = array();
16
+        foreach ($pUser as $item) {
17
+			if (preg_match('/\((\d+)\)/', $item, $match)) {
18
+				$groups[] = $match[1];
19
+			}
20
+
21
+            if (!preg_match('/(^\w+\s?\w+)\s*\:/', $item, $match) || !preg_match('/\[(.*)\]/', $item, $match_val)) {
22
+                continue;
23
+            }
24
+
25
+            if (false !== $index = array_search($match[1], $SYNOPHOTO_DSM_USER_INFO)) { // $key = 2;
26
+                $result[$index] = $match_val[1];
27
+                $result[$SYNOPHOTO_DSM_USER_INFO[$index]] = $match_val[1];
28
+            }
29
+        }
30
+		$result['groups'] = implode(',', $groups);
31
+        return $result;
32
+    }
33
+
34
+    static function SYNOPHOTO_LOGIN_AddLog($username, $success)
35
+    {
36
+        $ip = $_SERVER['REMOTE_ADDR'];
37
+        $conf = csSYNOPhotoMisc::GetConfigFile(SYNO_CNF_FILE, 'enable_demomode');
38
+        if ('yes' == $conf['enable_demomode']) {
39
+            $ip = 'xxx.xxx.xxx.xxx';
40
+        }
41
+
42
+        if ($success) {
43
+            PhotoLog::Add($username." logged in from "."[".$ip."].", true, strtolower($username));
44
+            return;
45
+        }
46
+        PhotoLog::Add($username." failed to log in from "."[".$ip."].", false, strtolower($username));
47
+    }
48
+
49
+    /*
50
+     *  return
51
+     *    1 - success
52
+     *    0 - fail
53
+     */
54
+    static function SYNOPHOTO_LOGIN_ValidateAdmin()
55
+    {
56
+        $user = ('root' == SYNOPHOTO_ADMIN_USER)? 'admin':SYNOPHOTO_ADMIN_NAME;
57
+        $command = "/usr/syno/bin/synoauth ".escapeshellarg($user)." ".escapeshellarg($_REQUEST['password']);
58
+        @exec($command, $pAuth, $retval);
59
+
60
+        if (0 == $retval) {
61
+            unset($_SESSION[SYNOPHOTO_ADMIN_USER]);
62
+			csSYNOPhotoMisc::PhotoSessionStart();
63
+
64
+            csSYNOPhotoDB::GetDBInstance()->SetSessionSystemConfigsFromFile();
65
+            $_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user'] = SYNOPHOTO_ADMIN_PASS;
66
+            self::SYNOPHOTO_LOGIN_AddLog($_REQUEST['username'], true);
67
+            return 1;
68
+        }
69
+        self::SYNOPHOTO_LOGIN_AddLog($_REQUEST['username'], false);
70
+        return 0;
71
+    }
72
+
73
+    /*
74
+     *  return
75
+     *    1 - success
76
+     *    0 - fail
77
+     *   -1 - disable user (Photo Station Account System)
78
+     *   -2 - not photo station user (Photo Station Account System)
79
+     *   -3 - guest account is not allowed to login (DSM Account System)
80
+     */
81
+    static function SYNOPHOTO_LOGIN_ValidateDsmUser()
82
+    {
83
+        if (!strcasecmp($_REQUEST['username'], 'guest')) {
84
+            return -3;
85
+        }
86
+        $command = "/usr/syno/bin/synophoto_dsm_user --auth ".escapeshellarg($_REQUEST['username'])." ".escapeshellarg($_REQUEST['password']);
87
+        @exec($command, $pAuth, $auth_retval);
88
+
89
+        $user_info = array();
90
+        if (0 == $auth_retval) {
91
+            $command = "/usr/syno/bin/synophoto_dsm_user --getinfo ".escapeshellarg($_REQUEST['username']);
92
+            @exec($command, $pUser, $retval);
93
+            $user_info = self::SYNOPHOTO_LOGIN_USERDATA_PARSER($pUser);
94
+        } else {
95
+            self::SYNOPHOTO_LOGIN_AddLog($_REQUEST['username'], false);
96
+            return 0;
97
+        }
98
+
99
+        if ($user_info) {
100
+            unset($_SESSION[SYNOPHOTO_ADMIN_USER]);
101
+			csSYNOPhotoMisc::PhotoSessionStart();
102
+
103
+            csSYNOPhotoDB::GetDBInstance()->SetSessionSystemConfigsFromFile();
104
+            $_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_userid'] = $user_info[2];
105
+            $_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_user'] = $user_info[0];
106
+            $_SESSION[SYNOPHOTO_ADMIN_USER]['use_dsm_account'] = true;
107
+			$_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_groupid'] = $user_info['groups'];
108
+
109
+            if ($user_info[10]) {
110
+                $_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user'] = SYNOPHOTO_ADMIN_PASS;
111
+            }
112
+            self::SYNOPHOTO_LOGIN_AddLog($_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_user'], true);
113
+            return 1;
114
+        }
115
+        self::SYNOPHOTO_LOGIN_AddLog($_REQUEST['username'], false);
116
+        return 0;
117
+    }
118
+
119
+    /*
120
+     * return 
121
+     *   1 - success
122
+     *   0 - fail
123
+     *  -1 - disable user (Photo Station Account System)
124
+     *  -2 - not photo station user (Photo Station Account System)
125
+     *  -3 - guest account is not allowed to login (DSM Account System)
126
+     */
127
+    static function SYNOPHOTO_LOGIN_ValidateUser()
128
+    {
129
+        $escape = PHOTO_DB_GetEscape();
130
+        $query_usr = "Select count(*) from photo_user where lower(username) like ? $escape";
131
+        $sqlParam = array(PHOTO_DB_EscapForLike(strtolower($_REQUEST['username'])));
132
+        $count = PHOTO_DB_QueryCount($GLOBALS['dbconn_photo'], $query_usr, $sqlParam);
133
+
134
+        if ($count == 0) {
135
+            // user is not photo station user
136
+            return -2;
137
+        }
138
+
139
+        $query = "Select * from photo_user where lower(username) like ? $escape and password = '".md5($_REQUEST['password'])."'";
140
+        $sqlParam = array(PHOTO_DB_EscapForLike(strtolower($_REQUEST['username'])));
141
+        $result = PHOTO_DB_Query($query, $sqlParam);
142
+
143
+        $user_info = PHOTO_DB_FetchRow($result);
144
+        if ($user_info) {
145
+            if(PHOTO_DB_ConvertBool($user_info[4]) == 't') {
146
+                //disabled
147
+                return -1;
148
+            }
149
+
150
+            unset($_SESSION[SYNOPHOTO_ADMIN_USER]);
151
+			csSYNOPhotoMisc::PhotoSessionStart();
152
+
153
+            csSYNOPhotoDB::GetDBInstance()->SetSessionSystemConfigsFromFile();
154
+            $_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_userid'] = $user_info[0];
155
+            $_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_user'] = $user_info[1];
156
+            $_SESSION[SYNOPHOTO_ADMIN_USER]['use_dsm_account'] = false;
157
+
158
+			/* set user's belonging groups */
159
+			$query = "SELECT groupid FROM " . PHOTO_USER_GROUP_TABLE . " WHERE userid = $user_info[0]";
160
+			$db_result = PHOTO_DB_Query($query);
161
+			$_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_groupid'] = '';
162
+			$groups = array();
163
+			while ($row = PHOTO_DB_FetchRow($db_result)) {
164
+				$groups[] = $row['groupid'];
165
+			}
166
+			$_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_groupid'] = implode(',', $groups);
167
+
168
+            if (PHOTO_DB_ConvertBool($user_info[6]) == 't') {
169
+                $_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user'] = SYNOPHOTO_ADMIN_PASS;
170
+            }
171
+            self::SYNOPHOTO_LOGIN_AddLog($_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_user'], true);
172
+            return 1;
173
+        }
174
+        self::SYNOPHOTO_LOGIN_AddLog($_REQUEST['username'], false);
175
+        return 0;
176
+    }
177
+
178
+	static function SYNOPHOTO_LOGIN_UpdateUserSession($username) {
179
+		if (null == $username) {
180
+			return;
181
+		}
182
+		// PhotoStation User
183
+		if ("0" == $_SESSION[SYNOPHOTO_ADMIN_USER]['photo_config']['global']['account_system'] || 'root' != SYNOPHOTO_ADMIN_USER){
184
+			$isAdmin = 'f';
185
+			$query = "SELECT userid, admin, disabled FROM photo_user WHERE username = ?";
186
+			$db_result = PHOTO_DB_Query($query, array($username));
187
+			while ($row = PHOTO_DB_FetchRow($db_result)) {
188
+				if (PHOTO_DB_IsTrue($row['disabled'])) {
189
+					unset($_SESSION[SYNOPHOTO_ADMIN_USER]);
190
+					return false;
191
+				}
192
+				$userid = $row['userid'];
193
+				$isAdmin = $row['admin'];
194
+			}
195
+			if('t' === PHOTO_DB_ConvertBool($isAdmin) || (('root' === SYNOPHOTO_ADMIN_USER) ? 'admin' : substr(SYNOPHOTO_ADMIN_USER, 1)) === $username) {
196
+				$_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user'] = SYNOPHOTO_ADMIN_PASS;
197
+			} else {
198
+				unset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']);
199
+			}
200
+
201
+			$query = "SELECT groupid FROM photo_user_group WHERE userid = ?";
202
+			$db_result = PHOTO_DB_Query($query, array($userid));
203
+			$_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_groupid'] = '';
204
+			$groups = array();
205
+			while ($row = PHOTO_DB_FetchRow($db_result)) {
206
+				$groups[] = $row['groupid'];
207
+			}
208
+			$_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_groupid'] = implode(',', $groups);
209
+		// DSM User
210
+		} else {
211
+			$command = "/usr/syno/bin/synophoto_dsm_user --getinfo ".escapeshellarg($username);
212
+			@exec($command, $pUser, $retval);
213
+			$user_info = self::SYNOPHOTO_LOGIN_USERDATA_PARSER($pUser);
214
+			if (!$user_info || $user_info['Expired'] !== "false") {
215
+				unset($_SESSION[SYNOPHOTO_ADMIN_USER]);
216
+				return false;
217
+			}
218
+			$_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_groupid'] = $user_info['groups'];
219
+			if ($user_info[10]) {
220
+				$_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user'] = SYNOPHOTO_ADMIN_PASS;
221
+			} else {
222
+				unset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']);
223
+			}
224
+		}
225
+	}
226
+}
227
+
228
+?>
229
+

+ 3
- 0
PhotoStation API/webapi/category.conf.php 查看文件

@@ -0,0 +1,3 @@
1
+<?php
2
+    define('CATEGORY_CONF_WEBAPI_SERVICE_INCLUDE_PATH', dirname(__FILE__).'/webapi.inc.php');
3
+?>

+ 7
- 0
PhotoStation API/webapi/category.inc.php 查看文件

@@ -0,0 +1,7 @@
1
+<?php
2
+    require_once('category.conf.php');
3
+    require_once("../include/syno_conf.php");
4
+
5
+    require_once(CATEGORY_CONF_WEBAPI_SERVICE_INCLUDE_PATH);
6
+    require_once(SZ_WEBAPI_CLASS_PATH);
7
+?>

+ 670
- 0
PhotoStation API/webapi/category.php 查看文件

@@ -0,0 +1,670 @@
1
+<?php
2
+
3
+require_once('category.inc.php');
4
+require_once('albumutil.php');
5
+
6
+class CategoryAPI extends WebAPI {
7
+    function __construct() {
8
+        parent::__construct(SZ_WEBAPI_API_DESCRIPTION_PATH);
9
+    }
10
+
11
+    protected function Process() {
12
+        if (!strcasecmp($this->method, "list")) {
13
+			session_write_close();
14
+            $this->CategoryList();
15
+        } elseif (!strcasecmp($this->method, "getinfo")) {
16
+			session_write_close();
17
+            $this->GetInfo();
18
+        } elseif (!strcasecmp($this->method, "listitem")) {
19
+            csSYNOPhotoDB::GetDBInstance()->SetSessionCache();
20
+			session_write_close();
21
+            $this->ListItem();
22
+        } else {
23
+            csSYNOPhotoDB::GetDBInstance()->SetSessionCache(true);
24
+            csSYNOPhotoMisc::CheckSessionTimeOut();
25
+
26
+            if (!strcasecmp($this->method, "create")) {
27
+                $this->Create();
28
+            } elseif (!strcasecmp($this->method, "delete")) {
29
+                $this->Delete();
30
+            } elseif (!strcasecmp($this->method, "edit")) {
31
+                $this->Edit();
32
+            } elseif (!strcasecmp($this->method, "arrangecategory")) {
33
+                $this->ArrangeCategory();
34
+            } elseif (!strcasecmp($this->method, "additem")) {
35
+                $this->AddItem();
36
+            } elseif (!strcasecmp($this->method, "removeitem")) {
37
+                $this->RemoveItem();
38
+            } elseif (!strcasecmp($this->method, "arrangeitem")) {
39
+                $this->ArrangeItem();
40
+            }
41
+        }
42
+    }
43
+
44
+    private function BooleanUniform($data) {
45
+        if (true === $data || 't' === $data) {
46
+            return true;
47
+        } elseif (false === $data || 'f' === $data) {
48
+            return false;
49
+        }
50
+        return false;
51
+    }
52
+
53
+    private function GetCategoryDatabaseID($data) {
54
+        $arr = explode('category_', $data);
55
+        $id = $arr[1];
56
+
57
+        if (empty($id)) {
58
+            return false;
59
+        }
60
+        if (!is_numeric($id)) {
61
+            return false;
62
+        }
63
+        return $id;
64
+    }
65
+
66
+    private function CheckItemID($data) {
67
+        $items = explode(',', $data);
68
+        foreach ($items as $item) {
69
+            $arr = explode('_', $item);
70
+            if (!in_array($arr[0], array('album', 'tag', 'smart'))) {
71
+                return false;
72
+            }
73
+        }
74
+        return $items;
75
+    }
76
+
77
+    private function CheckOffsetLimit($offset, $limit) {
78
+        if (!is_numeric($offset) || !is_numeric($limit)) {
79
+            return false;
80
+        }
81
+        if (0 > (int)$offset || -1 > (int)$limit) {
82
+            return false;
83
+        }
84
+        return true;
85
+    }
86
+
87
+    private function IsAccessible($id) {
88
+        $isAdmin = isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']);
89
+        $hidden = csSYNOPhotoDB::GetDBInstance()->IsCategoryAccessible($id);
90
+        if (false === $isAdmin && true === $hidden) {
91
+            return false;
92
+        }
93
+        return true;
94
+    }
95
+
96
+    private function GetItemID($type, $id) {
97
+        switch ($type) {
98
+        case 'album':
99
+            $row = csSYNOPhotoDB::GetDBInstance()->GetFieldByKeyValue('sharename', 'photo_share', 'shareid', $id);
100
+            $albumID = bin2hex($row['sharename']);
101
+            $itemID = "album_{$albumID}";
102
+            break;
103
+        case 'tag':
104
+            $itemID = "tag_{$id}";
105
+            break;
106
+        case 'smart':
107
+            $itemID = "smart_{$id}";
108
+            break;
109
+        default:
110
+            return false;
111
+            break;
112
+        }
113
+        return $itemID;
114
+    }
115
+
116
+    private function GetItemName($itemID) {
117
+        $arr = explode('_', $itemID );
118
+        if (2 != count($arr)) {
119
+            return false;
120
+        }
121
+        if (!in_array($arr[0], array('album', 'smart', 'tag'))) {
122
+            return false;
123
+        }
124
+        switch ($arr[0]) {
125
+        case 'album':
126
+            $name = @pack('H*', $arr[1]);
127
+            break;
128
+        case 'smart':
129
+            $name = @pack('H*', $arr[1]);
130
+            break;
131
+        case 'tag':
132
+            $row = csSYNOPhotoDB::GetDBInstance()->GetFieldByKeyValue('name', 'photo_label', 'id', $arr[1]);
133
+            $name = $row['name'];
134
+            break;
135
+        default:
136
+            break;
137
+        }
138
+        return $name;
139
+    }
140
+
141
+    private function CategoryList() {
142
+        if (!isset($_REQUEST['offset']) || !isset($_REQUEST['limit'])) {
143
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
144
+            goto End;
145
+        }
146
+        if (!$this->CheckOffsetLimit($_REQUEST['offset'], $_REQUEST['limit'])) {
147
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
148
+            goto End;
149
+        }
150
+            
151
+        // check if admin
152
+        $isAdmin = isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']);
153
+
154
+        // database query, +0 => convert to int
155
+		$dbCategories = Category::ListItem($_REQUEST['offset']+0, $_REQUEST['limit']+0, $isAdmin);
156
+
157
+        // form to webapi format
158
+        $categories = array();
159
+        foreach ($dbCategories as $dbCategory) {
160
+			$category = Decorator::CategoryFormula($dbCategory);
161
+			$categories[] = $category;
162
+        }
163
+
164
+        $resp['total'] = (int)csSYNOPhotoDB::GetDBInstance()->GetTotalCategory($isAdmin);
165
+        $offset = (0 > $_REQUEST['limit']) ? $resp['total'] : (int)($_REQUEST['offset'] + $_REQUEST['limit']);
166
+        $resp['offset'] = ($offset >  $resp['total']) ?  $resp['total'] : $offset;
167
+        $resp['categories'] = $categories;
168
+        $this->SetResponse($resp);
169
+    End:
170
+        return;
171
+    }
172
+
173
+    private function GetInfo() {
174
+        if (!isset($_REQUEST['id'])) {
175
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
176
+            goto End;
177
+        }
178
+
179
+        // get and check category id
180
+        $id = $this->GetCategoryDatabaseID($_REQUEST['id']);
181
+        if (false === $id) {
182
+            $this->SetError(PHOTOSTATION_CATEGORY_WRONG_ID_FORMAT);
183
+            goto End;
184
+        }
185
+
186
+        // check accessible
187
+        if (!$this->IsAccessible($id)) {
188
+            $this->SetError(PHOTOSTATION_CATEGORY_ACCESS_DENY);
189
+            goto End;
190
+        }
191
+
192
+        // datebase query
193
+        $objs = Category::GetByIds(array($id));
194
+		$info = $objs[$id];
195
+        if (!$info) {
196
+            $this->SetError(PHOTOSTATION_CATEGORY_GETINFO_FAIL);
197
+            goto End;
198
+        }
199
+        
200
+        // form to webapi format
201
+        $categories = array();
202
+        $category['id'] = 'category_'.$info['id'];
203
+        $category['name'] = $info['name'];
204
+        $category['hidden'] = $info['hidden'];
205
+        $category['home'] = $category['id'] === csSYNOPhotoMisc::GetConfigDB("album", "home_category", "photo_config");
206
+        array_push($categories, $category);
207
+
208
+        $resp['categories'] = $categories;
209
+        $this->SetResponse($resp);
210
+    End:
211
+        return;
212
+    }
213
+
214
+    private function Create() {
215
+        // check if admin
216
+        $isAdmin = isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']);
217
+        if (!$isAdmin) {
218
+            $this->SetError(PHOTOSTATION_CATEGORY_ACCESS_DENY);
219
+            goto End;
220
+        }
221
+
222
+		$name = trim($_REQUEST['name']);
223
+        if (!isset($_REQUEST['name']) || '' === $name) {
224
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
225
+            goto End;
226
+        }
227
+
228
+        $hidden = 'true' === $_REQUEST['hidden'] ? true : false;
229
+        if (Category::CheckNameExists($name)) {
230
+            $this->SetError(PHOTOSTATION_CATEGORY_DUPLICATE);
231
+            goto End;
232
+        }
233
+
234
+        // database query
235
+		$createId = Category::Add($name, $hidden);
236
+        if (!$createId) {
237
+            $this->SetError(PHOTOSTATION_CATEGORY_CREATE_FAIL);
238
+            goto End;
239
+        }
240
+
241
+        $resp['id'] = 'category_'.$createId;
242
+        $this->SetResponse($resp);
243
+    End:
244
+        return;
245
+    }
246
+
247
+    private function Delete() {
248
+        // check if admin
249
+        $isAdmin = isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']);
250
+        if (!$isAdmin) {
251
+            $this->SetError(PHOTOSTATION_CATEGORY_ACCESS_DENY);
252
+            goto End;
253
+        }
254
+
255
+        if (!isset($_REQUEST['id'])) {
256
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
257
+            goto End;
258
+        }
259
+
260
+        // get and check category id
261
+        $id = $this->GetCategoryDatabaseID($_REQUEST['id']);
262
+        if (!$id) {
263
+            $this->SetError(PHOTOSTATION_CATEGORY_WRONG_ID_FORMAT);
264
+            goto End;
265
+        }
266
+
267
+        // database query
268
+        if (!Category::DeleteByIds(array($id))) {
269
+            $this->SetError(PHOTOSTATION_CATEGORY_DELETE_FAIL);
270
+            goto End;
271
+        }
272
+
273
+        $homeVal = csSYNOPhotoMisc::GetConfigDB("album", "home_category", "photo_config");
274
+        if ($homeVal === $_REQUEST['id']) {
275
+            csSYNOPhotoMisc::UpdateConfigDB("album", "home_category", '', "photo_config");
276
+        }
277
+        $this->SetResponse($resp);
278
+    End:
279
+        return;
280
+    }
281
+
282
+    private function Edit() {
283
+        // check if admin
284
+        $isAdmin = isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']);
285
+        if (!$isAdmin) {
286
+            $this->SetError(PHOTOSTATION_CATEGORY_ACCESS_DENY);
287
+            goto End;
288
+        }
289
+
290
+        if (!isset($_REQUEST['id'])) {
291
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
292
+            goto End;
293
+        }
294
+
295
+        // get and check category id
296
+        $id = $this->GetCategoryDatabaseID($_REQUEST['id']);
297
+        if (!$id) {
298
+            $this->SetError(PHOTOSTATION_CATEGORY_WRONG_ID_FORMAT);
299
+            goto End;
300
+        }
301
+
302
+        $name = isset($_REQUEST['name']) ? trim($_REQUEST['name']) : NULL;
303
+		$hidden = isset($_REQUEST['hidden']) ?
304
+			('true' === $_REQUEST['hidden'] ? true : false) : NULL;
305
+
306
+		if (NULL === $hidden && ('' === $name || NULL === $name)) {
307
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
308
+            goto End;
309
+        }
310
+
311
+        if (Category::isDuplicated($id, $name)) {
312
+            $this->SetError(PHOTOSTATION_CATEGORY_DUPLICATE);
313
+            goto End;
314
+        }
315
+        // database query
316
+		if (!Category::EditById($id, $name, $hidden)) {
317
+            $this->SetError(PHOTOSTATION_CATEGORY_EDIT_FAIL);
318
+            goto End;
319
+        }
320
+
321
+        $setHome = isset($_REQUEST['home']) ? 'true' === $_REQUEST['home'] : false;
322
+        $homeVal = csSYNOPhotoMisc::GetConfigDB("album", "home_category", "photo_config");
323
+        if ($setHome) {
324
+            csSYNOPhotoMisc::UpdateConfigDB("album", "home_category", $_REQUEST['id'], "photo_config");
325
+        } elseif ($homeVal === $_REQUEST['id']) {
326
+            csSYNOPhotoMisc::UpdateConfigDB("album", "home_category", '', "photo_config");
327
+        }
328
+        $this->SetResponse($resp);
329
+    End:
330
+        return;
331
+    }
332
+
333
+    private function ArrangeCategory() {
334
+        // check if admin
335
+        $isAdmin = isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']);
336
+        if (!$isAdmin) {
337
+            $this->SetError(PHOTOSTATION_CATEGORY_ACCESS_DENY);
338
+            goto End;
339
+        }
340
+
341
+        if (!isset($_REQUEST['offset']) || !isset($_REQUEST['limit']) || !isset($_REQUEST['category_id'])) {
342
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
343
+            goto End;
344
+        }
345
+
346
+        if (!$this->CheckOffsetLimit($_REQUEST['offset'], $_REQUEST['limit'])) {
347
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
348
+            goto End;
349
+        }
350
+
351
+        // get and check category id
352
+        $ids = array();
353
+        $categoryIDs = array();
354
+        $categoryIDs = explode(',', $_REQUEST['category_id']);
355
+        foreach ($categoryIDs as $categoryID) {
356
+            if (false === ($id = $this->GetCategoryDatabaseID($categoryID))) {
357
+                $this->SetError(WEBAPI_ERR_BAD_REQUEST);
358
+                goto End;
359
+            }
360
+            array_push($ids, $id);
361
+        }
362
+
363
+        // database query
364
+        if (!csSYNOPhotoDB::GetDBInstance()->ArrangeCategory($ids, $_REQUEST['offset'], $_REQUEST['limit'])) {
365
+            $this->SetError(PHOTOSTATION_CATEGORY_ARRANGE_FAIL);
366
+            goto End;
367
+        }
368
+
369
+        $this->SetResponse($resp);
370
+    End:
371
+        return;
372
+
373
+    }
374
+
375
+    private function AddItem() {
376
+        // check if admin
377
+        $isAdmin = isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']);
378
+        if (!$isAdmin) {
379
+            $this->SetError(PHOTOSTATION_CATEGORY_ACCESS_DENY);
380
+            goto End;
381
+        }
382
+
383
+        if (!isset($_REQUEST['id']) || !isset($_REQUEST['item_id'])) {
384
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
385
+            goto End;
386
+        }
387
+
388
+        // get and check category id
389
+        $id = $this->GetCategoryDatabaseID($_REQUEST['id']);
390
+        if (!$id) {
391
+            $this->SetError(PHOTOSTATION_CATEGORY_WRONG_ID_FORMAT);
392
+            goto End;
393
+        }
394
+        // check item id
395
+        $itemIDs = $this->CheckItemID($_REQUEST['item_id']);
396
+        if (!$itemIDs) {
397
+            $this->SetError(PHOTOSTATION_CATEGORY_WRONG_ID_FORMAT);
398
+            goto End;
399
+        }
400
+
401
+        if (!csSYNOPhotoDB::GetDBInstance()->AddCategoryItem($id, $itemIDs)) {
402
+            $this->SetError(PHOTOSTATION_CATEGORY_ADD_ITEM_FAIL);
403
+            goto End;
404
+        }
405
+
406
+        $this->SetResponse($resp);
407
+    End:
408
+        return;
409
+    }
410
+
411
+    private function FormAlbumInfo($itemID)
412
+    {
413
+        $query = "select * from photo_share where shareid=?";
414
+        $sqlParam = array($itemID);
415
+        $db_result = PHOTO_DB_Query($query, $sqlParam);
416
+        if (false === ($db_result = PHOTO_DB_Query($query, $sqlParam))) {
417
+            return false;
418
+        }
419
+        if (false === ($row = PHOTO_DB_FetchRow($db_result))) {
420
+            return false;
421
+        }
422
+        $info['sharepath'] = $row['sharename'];
423
+        $info['name'] = basename($row['sharename']);
424
+        $info['title'] = empty($row['title']) ? $info['name'] : $row['title'];
425
+        $info['description'] = $row['description'];
426
+        $info['hits'] = $row['hits'];
427
+        if (PHOTO_DB_IsTrue($row['public'])) {
428
+            $albumPermission = 'public';
429
+        } else {
430
+            $albumPermission = empty($row['password']) ? 'private' : 'password';
431
+        }
432
+        $info['type'] = $albumPermission;
433
+        return $info;
434
+    }
435
+
436
+    private function FormAdditional($type, $additional, $itemID)
437
+    {
438
+        if ('album' === $type && in_array('album_permission', $additional)) {
439
+            $arr = explode('album_', $itemID);
440
+            if (2 === count($arr)) {
441
+                $albumName = @pack('H*', $arr[1]);
442
+                $data['album_permission'] = AlbumAPIUtil::GetAlbumPermission($albumName);
443
+            }
444
+        }
445
+        return $data;
446
+    }
447
+
448
+    private function IsAlbumPassword($albumName)
449
+    {
450
+        $query = 'SELECT * FROM photo_share WHERE sharename=?';
451
+        $sqlParam = array($albumName);
452
+        if (false === ($db_result = PHOTO_DB_Query($query, $sqlParam))) {
453
+            return false;
454
+        }
455
+        if (false === ($row = PHOTO_DB_FetchRow($db_result))) {
456
+            return false;
457
+        }
458
+        if (false == PHOTO_DB_IsTrue($row['public']) && !empty($row['password'])) {
459
+            return true;
460
+        }
461
+        return false;
462
+    }
463
+
464
+    private function ListItem() {
465
+        if (!isset($_REQUEST['id']) || !isset($_REQUEST['offset']) || !isset($_REQUEST['limit'])) {
466
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
467
+            goto End;
468
+        }
469
+        $additionalParams = explode(',', $_REQUEST['additional']);
470
+        $needThumbSize = in_array('thumb_size', $additionalParams);
471
+
472
+        if (!$this->CheckOffsetLimit($_REQUEST['offset'], $_REQUEST['limit'])) {
473
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
474
+            goto End;
475
+        }
476
+
477
+        // get and check category id
478
+        $id = $this->GetCategoryDatabaseID($_REQUEST['id']);
479
+        if (!$id) {
480
+            $this->SetError(PHOTOSTATION_CATEGORY_WRONG_ID_FORMAT);
481
+            goto End;
482
+        }
483
+
484
+        // check accessible
485
+        if (!$this->IsAccessible($id)) {
486
+            $this->SetError(PHOTOSTATION_CATEGORY_ACCESS_DENY);
487
+            goto End;
488
+        }
489
+
490
+        // database query
491
+        $dbItems = csSYNOPhotoDB::GetDBInstance()->ListCategoryItem($id, $_REQUEST['offset'], $_REQUEST['limit']);
492
+        if (false === $dbItems) {
493
+            $this->SetError(PHOTOSTATION_CATEGORY_LIST_ITEM_FAIL);
494
+            goto End;
495
+        }
496
+        $allow_comment = AlbumAPIUtil::IsAlbumCommentalbGlobal();
497
+
498
+        // form to webapi format
499
+        $items = array();
500
+        $denyCount = 0;
501
+        foreach ($dbItems as $dbItem) {
502
+            unset($item);
503
+            if ('album' === $dbItem['type']) {
504
+                $typeID = $dbItem['album_id'];
505
+            } elseif ('tag' === $dbItem['type']) {
506
+                $typeID = $dbItem['tag_id'];
507
+            } elseif ('smart' === $dbItem['type']) {
508
+                $typeID = $dbItem['smart_id'];
509
+            }
510
+            if (false === ($itemID = $this->GetItemID($dbItem['type'], $typeID))) {
511
+                continue;
512
+            }
513
+
514
+            // private can't show
515
+            if ('album' === $dbItem['type']) {
516
+                $arr = explode('album_', $itemID);
517
+                $albumName = @pack('H*', $arr[1]);
518
+                if (!csSYNOPhotoMisc::CheckAlbumAccessible($albumName) && !$this->IsAlbumPassword($albumName)) {
519
+                    $denyCount++;
520
+                    continue;
521
+                }
522
+            }
523
+            $item['id'] = $itemID;
524
+            $item['type'] = $dbItem['type'];
525
+            $item['name'] = $this->GetItemName($itemID);
526
+            $additional = $this->FormAdditional($dbItem['type'], $additionalParams, $itemID);
527
+
528
+			$coverPath = '';
529
+            if ('album' === $dbItem['type']) {
530
+                $arr = explode('album_', $itemID);
531
+                if (2 === count($arr)) {
532
+                    $albumName = @pack('H*', $arr[1]);
533
+                    $result = csSYNOPhotoAlbum::GetAlbumInstance()->GetAlbumCover($albumName);
534
+
535
+					$coverPath = $result['coverPath'];
536
+					$relativePath = substr($coverPath, strlen(SYNOPHOTO_SERVICE_REAL_DIR_PATH));
537
+					$dirname = dirname($relativePath);
538
+					$sharename = in_array($dirname, array('.', ''), true) ? '/' : $dirname;
539
+                }
540
+                $item['info'] = $this->FormAlbumInfo($typeID);
541
+                $item['info']['allow_comment'] = $allow_comment;
542
+                if (null !== $additional) {
543
+                    $item['additional']['album_permission'] = $additional['album_permission'];
544
+                }
545
+            } elseif ('tag' === $dbItem['type']) {
546
+                $arr = explode('tag_', $itemID);
547
+                if (2 === count($arr)) {
548
+                    $result = csSYNOPhotoAlbum::GetAlbumInstance()->GetLabelAlbumCover($arr[1]);
549
+
550
+					$coverPath = $result['coverPath'];
551
+					$relativePath = substr($coverPath, strlen(SYNOPHOTO_SERVICE_REAL_DIR_PATH));
552
+					$dirname = dirname($relativePath);
553
+					$sharename = in_array($dirname, array('.', ''), true) ? '/' : $dirname;
554
+                }
555
+            } elseif ('smart' === $dbItem['type']) {
556
+                $arr = explode('smart_', $itemID);
557
+                if (2 === count($arr)) {
558
+                    $smartName = @pack('H*', $arr[1]);
559
+                    $album = SmartAlbum::GetSmartAlbumInstance()->GetCoverOfSmartAlbumByName($smartName);
560
+
561
+					$coverPath = '';
562
+					if ($album['success'] && $album['cover']) {
563
+						$coverPath = $album['cover']['dbPath'];
564
+					}
565
+					$relativePath = substr($coverPath, strlen(SYNOPHOTO_SERVICE_REAL_DIR_PATH));
566
+					$dirname = dirname($relativePath);
567
+					$sharename = in_array($dirname, array('.', ''), true) ? '/' : $dirname;
568
+                }
569
+            }
570
+			$blConversion = csSYNOPhotoMisc::IsAlbumAllowConversion($sharename);
571
+			list($orig_resolutionx, $orig_resolutiony, $blThumbRotated) = AlbumAPIUtil::GetCoverResolutionRotation($coverPath);
572
+			AlbumAPIUtil::FillThumbStatus($item, $coverPath, $needThumbSize, $orig_resolutionx, $orig_resolutiony, $blThumbRotated, $blConversion);
573
+            array_push($items, $item);
574
+        }
575
+
576
+        // fixme
577
+        $resp['total'] = (int)csSYNOPhotoDB::GetDBInstance()->GetTotalCategoryItem($id) - $denyCount;
578
+        $offset = (0 > (int)$_REQUEST['limit']) ? $resp['total'] : (int)($_REQUEST['offset'] + $_REQUEST['limit']);
579
+        $resp['offset'] = ($offset >  $resp['total']) ?  $resp['total'] : $offset;
580
+        $resp['items'] = $items;
581
+        $this->SetResponse($resp);
582
+    End:
583
+        return;
584
+    }
585
+
586
+    private function RemoveItem() {
587
+        // check if admin
588
+        $isAdmin = isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']);
589
+        if (!$isAdmin) {
590
+            $this->SetError(PHOTOSTATION_CATEGORY_ACCESS_DENY);
591
+            goto End;
592
+        }
593
+
594
+        if (!isset($_REQUEST['id']) || !isset($_REQUEST['item_id'])) {
595
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
596
+            goto End;
597
+        }
598
+
599
+        // get and check category id
600
+        $id = $this->GetCategoryDatabaseID($_REQUEST['id']);
601
+        if (!$id) {
602
+            $this->SetError(PHOTOSTATION_CATEGORY_WRONG_ID_FORMAT);
603
+            goto End;
604
+        }
605
+        // check item id
606
+        $itemIDs = $this->CheckItemID($_REQUEST['item_id']);
607
+        if (!$itemIDs) {
608
+            $this->SetError(PHOTOSTATION_CATEGORY_WRONG_ID_FORMAT);
609
+            goto End;
610
+        }
611
+
612
+        // database query
613
+        if (!csSYNOPhotoDB::GetDBInstance()->RemoveCategoryItem($id, $itemIDs)) {
614
+            $this->SetError(PHOTOSTATION_CATEGORY_REMOVE_ITEM_FAIL);
615
+            goto End;
616
+        }
617
+
618
+
619
+        $this->SetResponse($resp);
620
+    End:
621
+        return;
622
+    }
623
+
624
+    private function ArrangeItem() {
625
+        // check if admin
626
+        $isAdmin = isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']);
627
+        if (!$isAdmin) {
628
+            $this->SetError(PHOTOSTATION_CATEGORY_ACCESS_DENY);
629
+            goto End;
630
+        }
631
+
632
+        if (!isset($_REQUEST['id']) || !isset($_REQUEST['offset']) || !isset($_REQUEST['limit'])) {
633
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
634
+            goto End;
635
+        }
636
+
637
+        if (!$this->CheckOffsetLimit($_REQUEST['offset'], $_REQUEST['limit'])) {
638
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
639
+            goto End;
640
+        }
641
+
642
+        // get and check category id
643
+        $id = $this->GetCategoryDatabaseID($_REQUEST['id']);
644
+        if (!$id) {
645
+            $this->SetError(PHOTOSTATION_CATEGORY_WRONG_ID_FORMAT);
646
+            goto End;
647
+        }
648
+        // check item id
649
+        $itemIDs = $this->CheckItemID($_REQUEST['item_id']);
650
+        if (!$itemIDs) {
651
+            $this->SetError(PHOTOSTATION_CATEGORY_WRONG_ID_FORMAT);
652
+            goto End;
653
+        }
654
+
655
+        // database query
656
+        if (!csSYNOPhotoDB::GetDBInstance()->ArrangeCategoryItem($id, $itemIDs, $_REQUEST['offset'], $_REQUEST['limit'])) {
657
+            $this->SetError(PHOTOSTATION_CATEGORY_ARRANGE_ITEM_FAIL);
658
+            goto End;
659
+        }
660
+
661
+        $this->SetResponse($resp);
662
+    End:
663
+        return;
664
+    }
665
+}
666
+
667
+$api = new CategoryAPI();
668
+$api->Run();
669
+
670
+?>

+ 3
- 0
PhotoStation API/webapi/comment.conf.php 查看文件

@@ -0,0 +1,3 @@
1
+<?php
2
+    define('COMMENT_CONF_WEBAPI_SERVICE_INCLUDE_PATH', dirname(__FILE__).'/webapi.inc.php');
3
+?>

+ 7
- 0
PhotoStation API/webapi/comment.inc.php 查看文件

@@ -0,0 +1,7 @@
1
+<?php
2
+    require_once('comment.conf.php');
3
+    require_once("../include/syno_conf.php");
4
+
5
+    require_once(COMMENT_CONF_WEBAPI_SERVICE_INCLUDE_PATH);
6
+    require_once(SZ_WEBAPI_CLASS_PATH);
7
+?>

+ 308
- 0
PhotoStation API/webapi/comment.php 查看文件

@@ -0,0 +1,308 @@
1
+<?php
2
+require_once('comment.inc.php');
3
+require_once('photoutil.php');
4
+require_once('albumutil.php');
5
+require_once(SYNOPHOTO_INCLUDE_SEND_MAIL);
6
+
7
+define('PHP_CMD', '/usr/bin/php -n -d extension_dir=/lib/php/modules -d extension=syno_compiler.so -d extension=curl.so -d extension=bz2.so');
8
+
9
+class CommentAPI extends WebAPI {
10
+    function __construct()
11
+    {
12
+        parent::__construct(SZ_WEBAPI_API_DESCRIPTION_PATH);
13
+    }
14
+
15
+    protected function Process()
16
+    {
17
+        if (!strcasecmp($this->method, "list")) {
18
+            $this->CommentList();
19
+        } elseif (!strcasecmp($this->method, "create")) {
20
+            $this->Create();
21
+        } elseif (!strcasecmp($this->method, "delete")) {
22
+            $this->Delete();
23
+        }
24
+    }
25
+
26
+    /**
27
+     *  @params - $id like: photo_dir_name, video_dir_name
28
+     */
29
+    private function GetItemInfo($id)
30
+    {
31
+        $arr = explode('_', $id);
32
+        if (3 !== count($arr)) {
33
+            return false;
34
+        }
35
+        if (!in_array($arr[0], array('photo', 'video'))) {
36
+            return false;
37
+        }
38
+        // get id
39
+        $dirName = @pack('H*', $arr[1]);
40
+        $fileName = @pack('H*', $arr[2]);
41
+        if ('/' === $dirName) {
42
+            $filePath = $fileName;
43
+        } else {
44
+            $filePath = $dirName.'/'.$fileName;
45
+        }
46
+        $realPath = SYNOPHOTO_SERVICE_REAL_DIR.'/'.$filePath;
47
+
48
+        if (!csSynoPhotoMisc::CheckPathValid($realPath)) {
49
+            return false;
50
+        }
51
+
52
+        $table = ('photo' === $arr[0]) ? 'photo_image' : 'video';
53
+        if ('root' === SYNOPHOTO_ADMIN_USER) {
54
+            $row = csSYNOPhotoDB::GetDBInstance()->GetFieldByKeyValue('id', $table, 'path', $realPath);
55
+        } else {
56
+            $row = csSYNOPhotoDB::GetDBInstance()->GetFieldByKeyValue('id', $table, 'path', $filePath);
57
+        }
58
+        $data['id'] = $row['id'];
59
+        $data['path'] = $realPath;
60
+        $data['sharePath'] = $filePath;
61
+        $data['isPhoto'] = ('photo' === $arr[0]) ? true : false;
62
+        return $data;
63
+    }
64
+
65
+    private function GetParams_Create()
66
+    {
67
+        if (!isset($_REQUEST['id']) || !isset($_REQUEST['name']) || !isset($_REQUEST['comment'])) {
68
+            return false;
69
+        }
70
+        if ('' === trim($_REQUEST['name']) || '' === trim($_REQUEST['comment'])) {
71
+            return false;
72
+        }
73
+        // get photo, video id
74
+        if (false === ($data = $this->GetItemInfo($_REQUEST['id']))) {
75
+            return false;
76
+        }
77
+        $params['id'] = $data['id'];
78
+        $params['path'] = $data['path'];
79
+        $params['sharePath'] = $data['sharePath'];
80
+        $params['isPhoto'] = $data['isPhoto'];
81
+
82
+        if (!empty($_REQUEST['email'])) {
83
+            if (false === WebAPIUtil::IsEMail($_REQUEST['email'])) {
84
+                return false;
85
+            }
86
+        }
87
+        $params['email'] = $_REQUEST['email'];
88
+        $params['name'] = $_REQUEST['name'];
89
+        $params['comment'] = $_REQUEST['comment'];
90
+        $userType = csSYNOPhotoMisc::GetUserType();
91
+        $params['userType'] = $userType;
92
+        $params['validate_number'] = $_REQUEST['validate_number'];
93
+        return $params;
94
+    }
95
+
96
+    private function GetParams_Delete()
97
+    {
98
+        if (!isset($_REQUEST['id']) || !isset($_REQUEST['comment_id'])) {
99
+            return false;
100
+        }
101
+        // get photo, video id
102
+        if (false === ($data = $this->GetItemInfo($_REQUEST['id']))) {
103
+            return false;
104
+        }
105
+        $params['id'] = $data['id'];
106
+        $params['path'] = $data['path'];
107
+        $params['sharePath'] = $data['sharePath'];
108
+        $params['isPhoto'] = $data['isPhoto'];
109
+
110
+        // get comment id
111
+        $params['comment_id'] = array();
112
+        $arr = explode(',', $_REQUEST['comment_id']);
113
+        foreach ($arr as $comment_id) {
114
+            $split = explode('comment_', $comment_id);
115
+            if (2 !== count($split)) {
116
+                return false;
117
+            }
118
+            array_push($params['comment_id'], $split[1]);
119
+        }
120
+        return $params;
121
+    }
122
+
123
+    private function GetParams_List()
124
+    {
125
+        if (!isset($_REQUEST['id'])) {
126
+            return false;
127
+        }
128
+        if (false === ($data = $this->GetItemInfo($_REQUEST['id']))) {
129
+            return false;
130
+        }
131
+        $params['id'] = $data['id'];
132
+        $params['path'] = $data['path'];
133
+        $params['sharePath'] = $data['sharePath'];
134
+        $params['isPhoto'] = $data['isPhoto'];
135
+        return $params;
136
+    }
137
+
138
+    private function GetCommentID($photoID, $videoPath, $name, $email, $comment, $isPhoto)
139
+    {
140
+        if (true === $isPhoto) {
141
+            $query = "SELECT id FROM photo_comment WHERE photo_id=? AND name=? AND email=? AND comment=? ORDER BY id DESC LIMIT 1";
142
+            $sqlParam = array($photoID, $name, $email, $comment);
143
+        } else {
144
+            $query = "SELECT id FROM video_comment WHERE path=? AND name=? AND email=? AND comment=? ORDER BY id DESC LIMIT 1";
145
+            $sqlParam = array($videoPath, $name, $email, $comment);
146
+        }
147
+
148
+        if (false === ($db_result = PHOTO_DB_Query($query, $sqlParam))) {
149
+            return false;
150
+        }
151
+        if (false === ($row = PHOTO_DB_FetchRow($db_result))) {
152
+            return false;
153
+        }
154
+        return $row['id'];
155
+    }
156
+
157
+    private function FormComment($data)
158
+    {
159
+        $comments = array();
160
+        foreach ($data as $item) {
161
+            $comment['id'] = 'comment_'.$item['id'];
162
+            $comment['name'] = $item['name'];
163
+            $comment['email'] = $item['email'];
164
+            $comment['comment'] = $item['comment'];
165
+            $comment['date'] = $item['date'];
166
+            array_push($comments, $comment);
167
+        }
168
+        return $comments;
169
+    }
170
+
171
+    private function CommentList()
172
+    {
173
+        if (false === ($params = $this->GetParams_List())) {
174
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
175
+            goto End;
176
+        }
177
+        // database query
178
+        if (true === $params['isPhoto']) {
179
+            $result = csSYNOPhotoDB::GetDBInstance()->GetPhotoComments($params['id']);
180
+        } else {
181
+            $result = csSYNOPhotoDB::GetDBInstance()->GetVideoComments($params['path']);
182
+        }
183
+        $comments = $this->FormComment($result);
184
+        
185
+        $resp['comments'] = $comments;
186
+        $this->SetResponse($resp);
187
+    End:
188
+        return;
189
+    }
190
+
191
+    private function Delete()
192
+    {
193
+        csSYNOPhotoDB::GetDBInstance()->SetSessionCache(true);
194
+
195
+        csSYNOPhotoMisc::CheckSessionTimeOut(true);
196
+
197
+        if (false === ($params = $this->GetParams_Delete())) {
198
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
199
+            goto End;
200
+        }
201
+        // check album maganeable
202
+        if (false === csSynoPhotoMisc::CheckAlbumManageable(dirname($params['sharePath']))) {
203
+            $this->SetError(PHOTOSTATION_COMMENT_ACCESS_DENY);
204
+            goto End;
205
+        }
206
+        // database query
207
+        if (true === $params['isPhoto']) {
208
+            foreach ($params['comment_id'] as $comment_id) {
209
+                csSYNOPhotoDB::GetDBInstance()->DeleteComment(1, $comment_id);
210
+            }
211
+        } else {
212
+            foreach ($params['comment_id'] as $comment_id) {
213
+                csSYNOPhotoDB::GetDBInstance()->DeleteComment(2, $comment_id);
214
+            }
215
+        }
216
+    End:
217
+        return;
218
+    }
219
+
220
+    private function Create()
221
+    {
222
+        csSYNOPhotoDB::GetDBInstance()->SetSessionCache(true);
223
+
224
+        if (false === ($params = $this->GetParams_Create())) {
225
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
226
+            goto End;
227
+        }
228
+
229
+        // check album is commentable
230
+        if (false === AlbumAPIUtil::IsAlbumCommentalbGlobal(true)) {
231
+            $this->SetError(PHOTOSTATION_COMMENT_ACCESS_DENY);
232
+            goto End;
233
+        }
234
+
235
+        // check validate number. guest must have correct validate number
236
+        if (0 === $params['userType']) {
237
+            if (null === $params['validate_number']) {
238
+                $this->SetError(PHOTOSTATION_COMMENT_VALIDATE_FAIL);
239
+                goto End;
240
+            }
241
+
242
+            $validateCount = strlen($params['validate_number']);
243
+            if (4 === $validateCount) {
244
+                if ((string)$params['validate_number'] !== (string)$_SESSION[SYNOPHOTO_ADMIN_USER]['post_key_photo']) {
245
+                    $this->SetError(PHOTOSTATION_COMMENT_VALIDATE_FAIL);
246
+                    goto End;
247
+                }
248
+            } elseif (12 === $validateCount) { // for DS photo+
249
+                $phppath = '/var/packages/PhotoStation/target/photo_scripts/encrypted/gen_comment_validate_string.php';
250
+                $cmd = PHP_CMD." {$phppath} ".escapeshellarg($params['email']).' '.escapeshellarg($params['comment']);
251
+                @exec($cmd, $signature, $ret);
252
+                if (0 !== $ret) {
253
+                    $this->SetError(PHOTOSTATION_COMMENT_VALIDATE_FAIL);
254
+                    goto End;
255
+                }
256
+                if ($signature[0] !== (string)$params['validate_number']) {
257
+                    $this->SetError(PHOTOSTATION_COMMENT_VALIDATE_FAIL);
258
+                    goto End;
259
+                }
260
+            } else {
261
+                $this->SetError(PHOTOSTATION_COMMENT_VALIDATE_FAIL);
262
+                goto End;
263
+            }
264
+        }
265
+
266
+        // database query
267
+        if (false === csSYNOPhotoBrowse::GetBrowseInstance()->AddNewComment($params['path'], ($params['isPhoto']) ? SYNOPHOTO_ITEM_TYPE_PHOTO : SYNOPHOTO_ITEM_TYPE_VIDEO,
268
+                                                              $params['name'], $params['email'], $params['comment'])) {
269
+                $this->SetError(PHOTOSTATION_COMMENT_CREATE_FAIL);
270
+                goto End;
271
+        }
272
+
273
+        // get inserted comment id
274
+        $insertedID = $this->GetCommentID($params['id'], $params['path'], $params['name'], $params['email'], $params['comment'], $params['isPhoto']);
275
+
276
+        $resp['id'] = 'comment_'.$insertedID;
277
+        $this->SetResponse($resp);
278
+
279
+		// Send mail to admin
280
+		$path = $params['path'];
281
+		$commentInfo = array();
282
+		$commentInfo['name'] = $params['name'];
283
+		$commentInfo['email'] = $params['email'];
284
+		$commentInfo['comment'] = $params['comment'];
285
+		if(isset($_POST['url'])) {
286
+			$commentInfo['photoUrl'] = substr($_POST['url'], strpos($_POST['url'], '#'));
287
+			$commentInfo['albumUrl'] = substr( $commentInfo['photoUrl'],
288
+				0, strrpos($commentInfo['photoUrl'], '/') );
289
+		}
290
+		$albumPath = substr( $params['sharePath'], 0, strrpos($params['sharePath'], '/') );
291
+		if (@file_exists($path)) {
292
+			if (false !== ($item = PhotoAPIUtil::getItemByPath($path, array(), ($params['isPhoto']?'photo':'video'), false))) {
293
+				$commentInfo['title'] = $item['info']['title'];
294
+				$commentInfo['albumTitle'] = $albumPath;
295
+				SYNOPHOTO6_SEND_MAIL_SendCommentMailToAdmin(-1, -1, $commentInfo);
296
+			}
297
+		}
298
+
299
+    End:
300
+        return;
301
+    }
302
+}
303
+
304
+$api = new CommentAPI();
305
+$api->Run();
306
+?>
307
+
308
+

+ 3
- 0
PhotoStation API/webapi/cover.conf.php 查看文件

@@ -0,0 +1,3 @@
1
+<?php
2
+    define('COVER_CONF_WEBAPI_SERVICE_INCLUDE_PATH', dirname(__FILE__).'/webapi.inc.php');
3
+?>

+ 7
- 0
PhotoStation API/webapi/cover.inc.php 查看文件

@@ -0,0 +1,7 @@
1
+<?php
2
+    require_once('cover.conf.php');
3
+    require_once("../include/syno_conf.php");
4
+
5
+    require_once(COVER_CONF_WEBAPI_SERVICE_INCLUDE_PATH);
6
+    require_once(SZ_WEBAPI_CLASS_PATH);
7
+?>

+ 176
- 0
PhotoStation API/webapi/cover.php 查看文件

@@ -0,0 +1,176 @@
1
+<?php
2
+
3
+require_once('cover.inc.php');
4
+
5
+class CoverAPI extends WebAPI {
6
+    function __construct()
7
+    {
8
+        parent::__construct(SZ_WEBAPI_API_DESCRIPTION_PATH);
9
+    }
10
+
11
+    protected function Process()
12
+    {
13
+        csSYNOPhotoMisc::CheckSessionTimeOut(true);
14
+
15
+        if (!strcasecmp($this->method, "set")) {
16
+            $this->SetCover();
17
+        }
18
+    }
19
+
20
+    /**
21
+     *  @params - $id like: photo_dir_name, video_dir_name
22
+     */
23
+    private function GetItemInfo($id)
24
+    {
25
+        $arr = explode('_', $id);
26
+        if (3 !== count($arr)) {
27
+            return false;
28
+        }
29
+        if (!in_array($arr[0], array('photo', 'video'))) {
30
+            return false;
31
+        }
32
+        // get id
33
+        $dirName = @pack('H*', $arr[1]);
34
+        $fileName = @pack('H*', $arr[2]);
35
+        $filePath = $dirName.'/'.$fileName;
36
+
37
+        $realPath = SYNOPHOTO_SERVICE_REAL_DIR.'/'.$filePath;
38
+
39
+        if (!csSynoPhotoMisc::CheckPathValid($realPath)) {
40
+			return false;
41
+        }
42
+
43
+        $table = ('photo' === $arr[0]) ? 'photo_image' : 'video';
44
+        if ('root' === SYNOPHOTO_ADMIN_USER) {
45
+            $row = csSYNOPhotoDB::GetDBInstance()->GetFieldByKeyValue('id', $table, 'path', $realPath);
46
+        } else {
47
+            $row = csSYNOPhotoDB::GetDBInstance()->GetFieldByKeyValue('id', $table, 'path', $filePath);
48
+        }
49
+        $data['id'] = $row['id'];
50
+        $data['path'] = $realPath;
51
+        $data['sharePath'] = $filePath;
52
+        $data['isPhoto'] = ('photo' === $arr[0]) ? true : false;
53
+
54
+        return $data;
55
+    }
56
+
57
+    private function GetParams_Set()
58
+    {
59
+        if (!isset($_REQUEST['id']) || !isset($_REQUEST['item_id'])) {
60
+            return false;
61
+        }
62
+        // get album id
63
+        $arr = explode('album_', $_REQUEST['id']);
64
+        if (2 !== count($arr)) {
65
+            return false;
66
+        }
67
+        // check album exist
68
+        $shareName = @pack('H*', $arr[1]);
69
+        $params['album_sharePath'] = $shareName;
70
+
71
+        $albumFullPath = SYNOPHOTO_SERVICE_REAL_DIR."/".$shareName;
72
+
73
+        if (!csSynoPhotoMisc::CheckPathValid($albumFullPath)) {
74
+            return false;
75
+        }
76
+
77
+        $params['album_path'] = $albumFullPath;
78
+
79
+        // get photo, video id
80
+        if (false === ($data = $this->GetItemInfo($_REQUEST['item_id']))) {
81
+            return false;
82
+        }
83
+        $params['id'] = $data['id'];
84
+        $params['path'] = $data['path'];
85
+        $params['sharePath'] = $data['sharePath'];
86
+        $params['isPhoto'] = $data['isPhoto'];
87
+
88
+        return $params;
89
+    }
90
+
91
+    private function SetAblumCover($albumPath, $coverPath)
92
+    {
93
+        $albumFullPath = SYNOPHOTO_SERVICE_REAL_DIR."/".$albumPath;
94
+
95
+        if (!csSynoPhotoMisc::CheckPathValid($albumFullPath)) {
96
+            return false;
97
+        }
98
+
99
+        $eaDir = dirname($albumFullPath)."/".SYNOPHOTO_EADIR;
100
+        if(!@file_exists($eaDir)) {
101
+            @mkdir($eaDir);
102
+            @chmod($eaDir, 0777);
103
+        }
104
+        $coverDir = $eaDir.'/'.basename($albumPath);
105
+        if(!@file_exists($coverDir)) {
106
+            @mkdir($coverDir);
107
+            @chmod($coverDir, 0777);
108
+        }
109
+        $coverFile = $coverDir.'/'.SYNOPhotoEA::getFilename(SYNOPhotoEA::FILE_ALBUM_COVER);
110
+        $coverValue =  substr($coverPath, (strlen($albumPath) + 1));
111
+
112
+        if (false === ($handle = fopen($coverFile, "w+"))) {
113
+            return false;
114
+        }
115
+        if ($handle) {
116
+            if (false === fwrite($handle, $coverValue)) {
117
+                return false;
118
+            }
119
+            fclose($handle);
120
+    		@chmod($coverFile, 0777);
121
+        }
122
+        return true;
123
+    }
124
+
125
+    private function SetCover()
126
+    {
127
+        csSYNOPhotoDB::GetDBInstance()->SetSessionCache(true);
128
+
129
+        if (false === ($params = $this->GetParams_Set())) {
130
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
131
+            goto End;
132
+        }
133
+
134
+        // check album exist
135
+        $row = csSYNOPhotoDB::GetDBInstance()->GetFieldByKeyValue('count(shareid)', 'photo_share', 'sharename', $params['album_sharePath']);
136
+        if (1 !== (int)$row[0]) {
137
+            $this->SetError(PHOTOSTATION_COVER_ALBUM_NOT_EXIST);
138
+            goto End;
139
+        }
140
+
141
+        // check photo/video exist
142
+        $table = $params['isPhoto'] ? 'photo_image' : 'video';
143
+        $row = csSYNOPhotoDB::GetDBInstance()->GetFieldByKeyValue('count(id)', $table, 'path', ('root' === SYNOPHOTO_ADMIN_USER) ? $params['path'] : $params['sharePath']);
144
+        if (1 !== (int)$row[0]) {
145
+            $this->SetError(PHOTOSTATION_COVER_PHOTO_VIDEO_NOT_EXIST);
146
+            goto End;
147
+        }
148
+
149
+        // check photo/video in album
150
+        $ret = strpos($params['path'], $params['album_path']);
151
+        if (false === $ret || 0 !== $ret) {
152
+            $this->SetError(PHOTOSTATION_COVER_PHOTO_VIDEO_NOT_IN_ALBUM);
153
+            goto End;
154
+        }
155
+
156
+        // check album maganeable
157
+        if (false === csSynoPhotoMisc::CheckAlbumManageable($params['album_sharePath'])) {
158
+            $this->SetError(PHOTOSTATION_COVER_ACCESS_DENY);
159
+            goto End;
160
+        }
161
+
162
+        // set album cover
163
+        if (false === $this->SetAblumCover($params['album_sharePath'], $params['sharePath'])) {
164
+            $this->SetError(PHOTOSTATION_COVER_SET_FAIL);
165
+            goto End;
166
+        }
167
+
168
+    End:
169
+        return;
170
+    }
171
+}
172
+
173
+$api = new CoverAPI();
174
+$api->Run();
175
+
176
+?>

+ 26
- 0
PhotoStation API/webapi/download.conf.php 查看文件

@@ -0,0 +1,26 @@
1
+<?php
2
+	define('DOWNLOAD_CONF_WEBAPI_SERVICE_INCLUDE_PATH', dirname(__FILE__) . '/webapi.inc.php');
3
+	/* The mimetype of raw image files is not listed here */
4
+	$SYNOPHOTO_ALLOW_PICT_NAMES_MIME = array(
5
+		'jpg' => 'image/jpeg',
6
+		'jpeg' => 'image/jpeg',
7
+		'jpe' => 'image/jpeg',
8
+		'bmp' => 'image/bmp',
9
+		'gif' => 'image/gif',
10
+		'png' => 'image/png',
11
+		'tiff' => 'image/tiff',
12
+		'tif' => 'image/tiff'
13
+	);
14
+	$SYNOPHOTO_VIDEO_MIME = array(
15
+		'3gp' => 'video/3gpp',
16
+		'asf' => 'video/x-ms-asf',
17
+		'avi' => 'video/x-msvideo',
18
+		'm4v' => 'video/x-m4v',
19
+		'mov' => 'video/quicktime',
20
+		'mp4' => 'video/mp4',
21
+		'mpe' => 'video/mpeg',
22
+		'mpeg'=> 'video/mpeg',
23
+		'mpg'=>	'video/mpeg',
24
+		'qt' => 'video/quicktime',
25
+		'wmv' => 'video/x-ms-wmv'
26
+	);

+ 7
- 0
PhotoStation API/webapi/download.inc.php 查看文件

@@ -0,0 +1,7 @@
1
+<?php
2
+	require_once('download.conf.php');
3
+	require_once('photoutil.php');
4
+	require_once("../include/syno_conf.php");
5
+
6
+	require_once(DOWNLOAD_CONF_WEBAPI_SERVICE_INCLUDE_PATH);
7
+	require_once(SZ_WEBAPI_CLASS_PATH);

+ 1017
- 0
PhotoStation API/webapi/download.php
文件差異過大導致無法顯示
查看文件


+ 6
- 0
PhotoStation API/webapi/dsm_share.inc.php 查看文件

@@ -0,0 +1,6 @@
1
+<?php
2
+	require_once("../include/syno_conf.php");
3
+	require_once(dirname(__FILE__).'/webapi.inc.php');
4
+	require_once('../include/dsm_share.php');
5
+	require_once(SZ_WEBAPI_CLASS_PATH);
6
+?>

+ 257
- 0
PhotoStation API/webapi/dsm_share.php 查看文件

@@ -0,0 +1,257 @@
1
+<?php
2
+require_once('dsm_share.inc.php');
3
+
4
+class DsmShareAPI extends WebAPI {
5
+	private $operationPemission = "dsm_account";
6
+	protected $idPrefix = 'dsmshare_';
7
+
8
+	function __construct() {
9
+		parent::__construct(SZ_WEBAPI_API_DESCRIPTION_PATH);
10
+	}
11
+
12
+	protected function Process()
13
+	{
14
+		if (!strcasecmp($this->method, "list")) {
15
+			$this->ShareList();
16
+		} else if (!strcasecmp($this->method, "copy")) {
17
+			$this->ShareCopy();
18
+		} else if (!strcasecmp($this->method, "copymusic")) {
19
+			$this->ShareCopyMusic();
20
+		}
21
+	}
22
+
23
+	private function ShareList()
24
+	{
25
+		$resp = array();
26
+
27
+		$params = $this->GetParams_List();
28
+		if (!$params) {
29
+			$this->SetError(WEBAPI_ERR_BAD_REQUEST);
30
+			goto End;
31
+		}
32
+
33
+		$data = DsmShare::ListItem($params['user'], $params['id'], $params['type'], $params['offset'], $params['limit']);
34
+		$items = array();
35
+
36
+		foreach ($data['data'] as $k => $v) {
37
+			$v['id'] = $this->EncodeItemId($v['id']);
38
+			$items[] = $v;
39
+		}
40
+		$resp['items'] = $items;
41
+		$resp['total'] = $data['total'];
42
+		$offset = (0 > (int)$params['limit']) ? $resp['total'] : $params['offset'] + $params['limit'];
43
+		$resp['offset'] = ($offset > $resp['total']) ?  $resp['total'] : $offset;
44
+
45
+		$this->SetResponse($resp);
46
+	End:
47
+		return;
48
+	}
49
+
50
+	private function ShareCopy()
51
+	{
52
+		$params = $this->GetParams_Copy();
53
+		if (!$params) {
54
+			$this->SetError(WEBAPI_ERR_BAD_REQUEST);
55
+			goto End;
56
+		}
57
+
58
+		$res = DsmShare::CopyItem($params['user'], $params['id'], $params['sharepath'], $params['duplicate']);
59
+
60
+		if (!$res) {
61
+			$this->SetError(PHOTOSTATION_DSMSHARE_UPLOAD_ERROR);
62
+			goto End;
63
+		}
64
+	End:
65
+		return;
66
+	}
67
+
68
+	private function ShareCopyMusic()
69
+	{
70
+		$params = $this->GetParams_CopyMusic();
71
+		if (!$params) {
72
+			$this->SetError(WEBAPI_ERR_BAD_REQUEST);
73
+			goto End;
74
+		}
75
+
76
+		if (SlideshowMusic::LIMIT < SlideshowMusic::GetCount() + count($params['id'])) {
77
+			$this->SetError(array(PHOTOSTATION_SLIDESHOWMUSIC_EXCEED_LIMIT, SlideshowMusic::LIMIT));
78
+			goto End;
79
+		}
80
+
81
+		foreach ($params['id'] as $id) {
82
+			if (!SlideshowMusic::Add(basename($id), $id, '', $params['user'])) {
83
+				$this->SetError(PHOTOSTATION_DSMSHARE_UPLOAD_ERROR);
84
+				goto End;
85
+			}
86
+		}
87
+
88
+	End:
89
+		return;
90
+	}
91
+
92
+	private function GetParams_List()
93
+	{
94
+		$id = !$_REQUEST['id'] || 'fm_root' === $_REQUEST['id'] ? '' : $this->DecodeItemId($_REQUEST['id']);
95
+
96
+		if (false === $id) {
97
+			return false;
98
+		}
99
+
100
+		$types = explode(',', preg_replace('/\s/', '', $_REQUEST['type']));
101
+
102
+		foreach ($types as $type) {
103
+			if (!in_array($type, DsmShare::$allowTypes)) {
104
+				return false;
105
+			}
106
+		}
107
+
108
+		$user = $this->GetUser();
109
+
110
+		if (!$user) {
111
+			return false;
112
+		}
113
+
114
+		// $variable + 0 => convert to integer
115
+		$params = array(
116
+			'id' => $id,
117
+			'user' => $user,
118
+			'type' => $types,
119
+			'offset' => !isset($_REQUEST['offset']) || $_REQUEST['offset'] < 0 ? 0 : $_REQUEST['offset'] + 0,
120
+			'limit' => !isset($_REQUEST['limit']) || $_REQUEST['limit'] < 0 ? NULL : $_REQUEST['limit'] + 0,
121
+		);
122
+		return $params;
123
+	}
124
+
125
+	private function GetUser()
126
+	{
127
+		$user = false;
128
+
129
+		if (isset($_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_user'])) {
130
+			$user = $_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_user'];
131
+		} else if (isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user'])) {
132
+			$user = ("root" === SYNOPHOTO_ADMIN_USER)? "admin" : SYNOPHOTO_ADMIN_NAME;
133
+		}
134
+		return $user;
135
+	}
136
+
137
+	private function GetParams_Copy()
138
+	{
139
+		if(!isset($_REQUEST['id']) || !isset($_REQUEST['sharepath'])) {
140
+			return false;
141
+		}
142
+
143
+		$idWithPrefix = explode(',', $_REQUEST['id']);
144
+
145
+		$ids = array();
146
+
147
+		$sharepath = trim($_REQUEST['sharepath'], "/");
148
+
149
+		$dbPath = $sharepath ? $sharepath : '/';
150
+
151
+		foreach ($idWithPrefix as $k) {
152
+			$decodeId = $this->DecodeItemId($k);
153
+			if (false === $decodeId) {
154
+				return false;
155
+			}
156
+			if (dirname($decodeId) === SYNOPHOTO_SERVICE_REAL_DIR."/".$sharepath) {
157
+				return false;
158
+			}
159
+			$ids[$decodeId] = 1;
160
+		}
161
+
162
+		if (!csSYNOPhotoMisc::CheckPathValid($sharepath) || !csSYNOPhotoMisc::CheckAlbumUploadable($dbPath)) {
163
+			return false;
164
+		}
165
+
166
+		$duplicate = $_REQUEST['duplicate'] ? $_REQUEST['duplicate'] : DsmShare::IGNORE;
167
+
168
+		if (!in_array($duplicate, array(DsmShare::OVERWRITE, DsmShare::IGNORE))) {
169
+			return false;
170
+		}
171
+
172
+		$user = $this->GetUser();
173
+
174
+		if (!$user) {
175
+			return false;
176
+		}
177
+
178
+		// $variable + 0 => convert to integer
179
+		$params = array(
180
+			'id' => array_keys($ids),
181
+			'user' => $user,
182
+			'sharepath' => $sharepath,
183
+			'duplicate' => $duplicate
184
+		);
185
+		return $params;
186
+	}
187
+
188
+	private function GetParams_CopyMusic()
189
+	{
190
+		if(!isset($_REQUEST['id'])) {
191
+			return false;
192
+		}
193
+
194
+		$idWithPrefix = explode(',', $_REQUEST['id']);
195
+
196
+		$ids = array();
197
+
198
+		foreach ($idWithPrefix as $k) {
199
+			$decodeId = $this->DecodeItemId($k);
200
+			if (false === $decodeId) {
201
+				return false;
202
+			}
203
+			$ids[$decodeId] = 1;
204
+		}
205
+
206
+		$user = $this->GetUser();
207
+
208
+		if (!$user) {
209
+			return false;
210
+		}
211
+
212
+		$params = array(
213
+			'id' => array_keys($ids),
214
+			'user' => $user
215
+		);
216
+		return $params;
217
+	}
218
+
219
+	protected function CheckPermission()
220
+	{
221
+		$res = true;
222
+		$check = array(
223
+			"list" => $this->operationPemission,
224
+			"copy" => $this->operationPemission,
225
+			"copymusic" => $this->operationPemission
226
+		);
227
+		if (!array_key_exists($this->method, $check)) {
228
+			goto End;
229
+		}
230
+
231
+		$funcName = "check_".$check[$this->method];
232
+
233
+		if (!method_exists($this, $funcName)) {
234
+			$res = false;
235
+			goto End;
236
+		}
237
+
238
+		$res = $this->$funcName();
239
+
240
+	End:
241
+		if (!$res) {
242
+			$this->SetError(PHOTOSTATION_DSMSHARE_ACCESS_DENY);
243
+		}
244
+		return $res;
245
+	}
246
+
247
+	private function check_dsm_account()
248
+	{
249
+		csSYNOPhotoDB::GetDBInstance()->SetSessionCache();
250
+		csSYNOPhotoMisc::CheckSessionTimeOut(true);
251
+		return "1" === csSYNOPhotoMisc::GetConfigDB("global", "account_system", "photo_config") || isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']);
252
+	}
253
+}
254
+$api = new DsmShareAPI();
255
+$api->Run();
256
+
257
+?>

+ 84
- 0
PhotoStation API/webapi/embed.php 查看文件

@@ -0,0 +1,84 @@
1
+<?
2
+require_once('albumutil.php');
3
+require_once('photoutil.php');
4
+
5
+$id_str = $_GET['id'];
6
+$id_arr = explode('_', $id_str);
7
+if (3 !== count($id_arr) || ('photo' !== $id_arr[0] && 'video' !== $id_arr[0])) {
8
+	$error = true;
9
+}
10
+$albumName = @pack('H*', $id_arr[1]);
11
+$fileName = @pack('H*', $id_arr[2]);
12
+$path = SYNOPHOTO_SERVICE_REAL_DIR . "/" . ("/" === $albumName ? "" : $albumName . "/") . $fileName;
13
+
14
+$item = checkVideoAvailable($albumName, $path, $id_arr[0]);
15
+
16
+if ($item) {
17
+	$quality = findQualityObject($item['additional']['video_quality'], $_GET['quality']);
18
+	$quality_id = $quality['id'];
19
+	$videoType = $quality['container'];
20
+	$videoProfile = $quality['profile_name'];
21
+	$video = SYNOPHOTO_URL_PREFIX . "/photo/webapi/download.php/1.mp4?api=SYNO.PhotoStation.Download&method=getvideo&version=1&id=$id_str&quality_id=$quality_id";
22
+	$image = SYNOPHOTO_URL_PREFIX . "/photo/webapi/thumb.php?api=SYNO.PhotoStation.Thumb&method=get&version=1&id=$id_str&size=large";
23
+	$title = $item['info']['title'].' - '. __(photo_str_ap_name_6);
24
+} else {
25
+	$title = __(photo_str_ap_name_6);
26
+}
27
+
28
+function findQualityObject ($videoQuality, $qualityProfileName)
29
+{
30
+	if (!$videoQuality) {
31
+		return null;
32
+	}
33
+	foreach ($videoQuality as $i) {
34
+		if ($qualityProfileName == $i['profile_name']) {
35
+			return $i;
36
+		}
37
+	}
38
+	usort($videoQuality, function($a, $b) {
39
+		$profileOrder = Array('high'=>0, 'medium'=>1, 'low'=>2, 'orig_h264'=>3, 'mobile'=>4, 'flv'=>5);
40
+		return $profileOrder[$a['profile_name']] > $profileOrder[$b['profile_name']] ? 1 : -1;
41
+	});
42
+	return $videoQuality[0];
43
+}
44
+
45
+function checkVideoAvailable ($albumName, $path, $type)
46
+{
47
+	if (!csSynoPhotoMisc::CheckPathValid($path)) {
48
+		return false;
49
+	}
50
+
51
+	csSYNOPhotoDB::GetDBInstance()->SetSessionCache();
52
+	if (!csSYNOPhotoMisc::CheckAlbumAccessible($albumName)) {
53
+		return false;
54
+	}
55
+
56
+	if (@file_exists($path)) {
57
+		if (!($item = PhotoAPIUtil::getItemByPath($path, array('video_quality'), $type, false))) {
58
+			return false;
59
+		}
60
+	}
61
+	return $item;
62
+}
63
+
64
+?>
65
+<!DOCTYPE html>
66
+<html><head><title><?= $title ?></title>
67
+	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
68
+	<meta http-equiv="X-UA-Compatible" content="IE=11" />
69
+	<script type="text/javascript" src="../jwplayer/jwplayer.js"></script>
70
+	<script type="text/javascript" src="../photo_new/syno_embed.js"></script>
71
+	<link rel="shortcut icon" href="../favicon.ico">
72
+</head><body>
73
+<? if ($error) {?>
74
+<div style=text-align:center;margin-top:100px><?=__(photo_str_video_unavailable)?></div>
75
+<? } else {?>
76
+<div id='player'></div>
77
+<script>
78
+var video = "<?= $video?>",
79
+	image = "<?= $image?>",
80
+	videoType = "<?= $videoType?>";
81
+	videoProfile = "<?= $videoProfile?>";
82
+</script>
83
+<?}?>
84
+</body></html>

+ 2
- 0
PhotoStation API/webapi/file.conf.php 查看文件

@@ -0,0 +1,2 @@
1
+<?php
2
+	define('FILE_CONF_WEBAPI_SERVICE_INCLUDE_PATH', dirname(__FILE__) . '/webapi.inc.php');

+ 6
- 0
PhotoStation API/webapi/file.inc.php 查看文件

@@ -0,0 +1,6 @@
1
+<?php
2
+	require_once('file.conf.php');
3
+	require_once("../include/syno_conf.php");
4
+
5
+	require_once(FILE_CONF_WEBAPI_SERVICE_INCLUDE_PATH);
6
+	require_once(SZ_WEBAPI_CLASS_PATH);

+ 204
- 0
PhotoStation API/webapi/file.php 查看文件

@@ -0,0 +1,204 @@
1
+<?PHP
2
+
3
+set_time_limit(0);
4
+
5
+require_once('file.inc.php');
6
+
7
+class FileAPI extends WebAPI
8
+{
9
+	function __construct()
10
+	{
11
+		parent::__construct(SZ_WEBAPI_API_DESCRIPTION_PATH);
12
+	}
13
+
14
+	protected function Process()
15
+	{
16
+		$this->Init();
17
+
18
+		csSYNOPhotoMisc::CheckSessionTimeOut(true);
19
+
20
+		if (!strcasecmp($this->method, "uploadphoto")) {
21
+			$this->UploadFile(true);
22
+		}
23
+		if (!strcasecmp($this->method, "uploadvideo")) {
24
+			$this->UploadFile(false);
25
+		}
26
+	}
27
+
28
+	private function Init()
29
+	{
30
+		csSYNOPhotoMisc::PhotoSessionStart();
31
+
32
+		$_SESSION[SYNOPHOTO_ADMIN_USER]['security_identifier'] = csSYNOPhotoMisc::GetSessionIdentifier();
33
+		if (isset($_REQUEST['session'])) {
34
+			csSYNOPhotoMisc::RestoreSession();
35
+		}
36
+		csSYNOPhotoDB::GetDBInstance()->SetSessionCache(true);
37
+	}
38
+
39
+	private function UploadFile($isPhoto)
40
+	{
41
+		$ret = false;
42
+		/* return when lack of params */
43
+		if (!isset($_REQUEST['dest_folder_path']) || !isset($_REQUEST['duplicate']) || !isset($_REQUEST['filename']) || !isset($_REQUEST['mtime'])) {
44
+				$this->SetError(PHOTOSTATION_FILE_BAD_PARAMS);
45
+				goto End;
46
+		}
47
+
48
+		/* set required params */
49
+		$destShareName = $_REQUEST['dest_folder_path'];
50
+		if ('' === $destShareName) {
51
+			if (!csSYNOPhotoMisc::CheckAlbumUploadable('/')) {
52
+				$this->SetError(PHOTOSTATION_FILE_ACCESS_DENY);
53
+				goto End;
54
+			}
55
+		} elseif (!csSYNOPhotoMisc::CheckAlbumUploadable($destShareName)) {
56
+			$this->SetError(PHOTOSTATION_FILE_ACCESS_DENY);
57
+			goto End;
58
+		}
59
+
60
+		$fileName = $_REQUEST['filename'];
61
+		if ($isPhoto && !csSYNOPhotoMisc::IsPhotoFile($fileName)) {
62
+			$this->SetError(PHOTOSTATION_FILE_FILE_EXT_ERR);
63
+			goto End;
64
+		}
65
+		if (!$isPhoto && !csSYNOPhotoMisc::IsVideoFile($fileName)) {
66
+			$this->SetError(PHOTOSTATION_FILE_FILE_EXT_ERR);
67
+			goto End;
68
+		}
69
+
70
+		$destDir = SYNOPHOTO_SERVICE_REAL_DIR . "/" . $destShareName;
71
+		if (!csSynoPhotoMisc::CheckPathValid($destDir)) {
72
+			$this->SetError(PHOTOSTATION_FILE_BAD_PARAMS);
73
+			goto End;
74
+		}
75
+
76
+		if (!file_exists($destDir)) {
77
+			$this->SetError(PHOTOSTATION_FILE_DIR_NOT_EXISTS);
78
+			goto End;
79
+		}
80
+
81
+		$mtime = $_REQUEST['mtime'];
82
+
83
+		$dup = $_REQUEST['duplicate'];
84
+		$overwrite = ('ignore' === $dup) ? 0 : (('overwrite' === $dup) ? 1 : 2);	// default is rename
85
+
86
+		/* set all uploaded file data */
87
+		$hasFile = false;
88
+		$files = array();
89
+		$allowed_upload_form_names = array('original', 'thumb_small', 'thumb_large', 'high', 'medium', 'low', 'mobile', 'iphone', 'android', 'flv');
90
+
91
+		foreach($allowed_upload_form_names as $name) {
92
+			$files[$name] = null;
93
+
94
+			if (isset($_FILES[$name])) {
95
+				if ($_FILES[$name]['error'] > 0) { // files is not uploaded successfully by form upload
96
+					$this->SetError(PHOTOSTATION_FILE_UPLOAD_ERROR);
97
+					goto End;
98
+				}
99
+
100
+				$files[$name] = $_FILES[$name];
101
+			}
102
+
103
+			$hasFile = true;
104
+		}
105
+
106
+		if (!$hasFile) {
107
+			$this->SetError(PHOTOSTATION_FILE_NO_FILE);
108
+			goto End;
109
+		}
110
+
111
+		$destPath = $destDir . "/" . $fileName;
112
+
113
+		if (@file_exists($destPath)) {
114
+			if (0 === $overwrite) {
115
+				//ignore
116
+				$ret = false;
117
+				goto End;
118
+			} else if (2 === $overwrite) {
119
+				//rename
120
+				$dir = pathinfo($destPath, PATHINFO_DIRNAME);
121
+				$name = pathinfo($destPath, PATHINFO_FILENAME);
122
+				$ext = pathinfo($destPath, PATHINFO_EXTENSION);
123
+
124
+				$rename_suffix = 0;
125
+				$rename_path = $destPath;
126
+
127
+				while (file_exists($rename_path)) {
128
+					$rename_path = "$dir/$name" . "_" . (++$rename_suffix) . ".$ext";
129
+				}
130
+
131
+				$destPath = $rename_path;
132
+			}
133
+		}
134
+
135
+		$IsOldEAFilePrefix = csSYNOPhotoMisc::IsOldEAFilePrefix();
136
+
137
+		/* set all destination file path */
138
+		$destEADir = SYNOPhotoEA::getEADirPath($destPath);
139
+		$smallPath = $destEADir . "/" . SYNOPhotoEA::getFilename(SYNOPhotoEA::FILE_THUMB_M, $IsOldEAFilePrefix);
140
+		$largePath = $destEADir . "/" . SYNOPhotoEA::getFilename(SYNOPhotoEA::FILE_THUMB_XL, $IsOldEAFilePrefix);
141
+		$vHighPath = $destEADir . "/" . SYNOPhotoEA::getFilename(SYNOPhotoEA::FILE_FILM_H_MP4, $IsOldEAFilePrefix);
142
+		$vMediumPath = $destEADir . "/" . SYNOPhotoEA::getFilename(SYNOPhotoEA::FILE_FILM_M_MP4, $IsOldEAFilePrefix);
143
+		$vLowPath = $destEADir . "/" . SYNOPhotoEA::getFilename(SYNOPhotoEA::FILE_FILM_L_MP4, $IsOldEAFilePrefix);
144
+		$vFlvPath = $destEADir . "/" . SYNOPhotoEA::getFilename(SYNOPhotoEA::FILE_FILM_FLV, $IsOldEAFilePrefix);
145
+		$vMobilePath = $destEADir . "/" . SYNOPhotoEA::getFilename(SYNOPhotoEA::FILE_FILM_MOBILE_MP4, $IsOldEAFilePrefix);
146
+		$vIPhonePath = $destEADir . "/" . SYNOPhotoEA::getFilename(SYNOPhotoEA::FILE_FILM_MOBILE_IPHONE, $IsOldEAFilePrefix);
147
+		$vAndroidPath = $destEADir . "/" . SYNOPhotoEA::getFilename(SYNOPhotoEA::FILE_FILM_MOBILE_ANDROID, $IsOldEAFilePrefix);
148
+
149
+		@mkdir($destDir, 0777, true);
150
+		@mkdir($destEADir, 0777, true);
151
+
152
+		/* write all existed files */
153
+		$this->writeFile($files['thumb_small']['tmp_name'], $smallPath);
154
+		$this->writeFile($files['thumb_large']['tmp_name'], $largePath);
155
+		if (!$isPhoto) {
156
+			$this->writeFile($files['high']['tmp_name'], $vHighPath);
157
+			$this->writeFile($files['medium']['tmp_name'], $vMediumPath);
158
+			$this->writeFile($files['low']['tmp_name'], $vLowPath);
159
+			$this->writeFile($files['flv']['tmp_name'], $vFlvPath);
160
+			$this->writeFile($files['mobile']['tmp_name'], $vMobilePath);
161
+			$this->writeFile($files['iphone']['tmp_name'], $vIPhonePath);
162
+			$this->writeFile($files['android']['tmp_name'], $vAndroidPath);
163
+		}
164
+		$ret = $this->writeFile($files['original']['tmp_name'], $destPath);
165
+
166
+		/* trigger index */
167
+		if (!empty($files['original']) && $ret) {
168
+			@touch($destPath, $mtime / 1000);
169
+			$path_part = pathinfo($rename_path ? $rename_path : $fileName);
170
+			if (isset($_REQUEST['title'])) {
171
+				$path_part['title'] = $_REQUEST['title'];
172
+			}
173
+			if (isset($_REQUEST['description'])) {
174
+				$path_part['description'] = $_REQUEST['description'];
175
+			}
176
+
177
+			$dbPath = SYNOPHOTO_SERVICE_REAL_DIR_PATH . trim($destShareName . "/" . $path_part['basename'], "/");
178
+			File::AddFile($dbPath, $destPath, $path_part['title'], $path_part['description']);
179
+		}
180
+
181
+		PhotoLog::Add($fileName." was uploaded to album ".$destShareName." successfully.", true, strtolower(GetLoginUsername()));
182
+		$ret = true;
183
+	End:
184
+		return $ret;
185
+	}
186
+
187
+	private function writeFile($tmpPath, $destPath)
188
+	{
189
+		if (empty($tmpPath)) {
190
+			return false;
191
+		}
192
+		if (!is_uploaded_file($tmpPath)) {
193
+			return false;
194
+		}
195
+		if (!@move_uploaded_file($tmpPath, $destPath)) {
196
+			return false;
197
+		}
198
+		@chmod($destPath, 0777);
199
+		return true;
200
+	}
201
+}
202
+
203
+$api = new FileAPI();
204
+$api->Run();

+ 239
- 0
PhotoStation API/webapi/formulautil.php 查看文件

@@ -0,0 +1,239 @@
1
+<?
2
+
3
+class Decorator {
4
+
5
+	static function CategoryFormula ($dbObject, $config = array())
6
+	{
7
+		$encodedId = $config['id'];
8
+		$categroy = array();
9
+		$category['id'] = $encodedId ? $encodedId : 'category_'.$dbObject['id'];
10
+		$category['name'] = $dbObject['name'];
11
+		$category['hidden'] = $dbObject['hidden'];
12
+		return $category;
13
+	}
14
+
15
+	static function AlbumFormula($dbObject, $config = array())
16
+	{
17
+		$allowComment = $config['allowComment'];
18
+		$showAlbumHit = $config['showAlbumHit'];
19
+		$id = $config['id'];
20
+		$param = $config['param'];
21
+		$needThumbSize = isset($config['needThumbSize']) ? $config['needThumbSize'] : in_array('thumb_size', $param['additional']);
22
+
23
+		$sharepath = $dbObject['sharename'];
24
+		$item_info = AlbumAPIUtil::FormAlbum($dbObject, $allowComment, $showAlbumHit);
25
+		if (!in_array('thumbnail', $param['ignore'])) {
26
+			$result = csSYNOPhotoAlbum::GetAlbumInstance()->GetAlbumCover($sharepath);
27
+			$coverPath = $result['coverPath'];
28
+		}
29
+		list($orig_resolutionx, $orig_resolutiony, $blThumbRotated) = AlbumAPIUtil::GetCoverResolutionRotation($coverPath);
30
+
31
+		// replace conversion with the first layer album's
32
+		$item_info['conversion'] = csSYNOPhotoMisc::IsAlbumAllowConversion($sharepath);
33
+		$item = array(
34
+			'info'	=> $item_info,
35
+			'id'	=> $id ? $id : self::EncodeItemId($sharepath, 'album_'),
36
+			'type'	=> 'album',
37
+			'additional' => AlbumAPIUtil::FormAdditional('album', $dbObject, $param)
38
+		);
39
+
40
+		AlbumAPIUtil::FillThumbStatus($item, $coverPath, $needThumbSize, $orig_resolutionx, $orig_resolutiony, $blThumbRotated, $item_info['conversion']);
41
+		return $item;
42
+	}
43
+
44
+	static function SmartFormula($dbObject, $config = array())
45
+	{
46
+		$param = $config['param'];
47
+		$id = $config['id'];
48
+		$needThumbSize = isset($config['needThumbSize']) ? $config['needThumbSize'] : in_array('thumb_size', $param['additional']);
49
+
50
+        $smartAlbum['id'] = $id ? $id : self::EncodeItemId($dbObject['name'], 'smart_');
51
+        $smartAlbum['type'] = 'smart';
52
+        $album = SmartAlbum::GetSmartAlbumInstance()->GetCoverOfSmartAlbumByName($dbObject['name']);
53
+        $smartAlbum['thumbnail_status'] = 'default';
54
+
55
+		if (false === $album['success'] && !isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user'])) {
56
+			return -1;
57
+		}
58
+
59
+		$coverPath = '';
60
+        if ($album['success'] && isset($album['cover']['dbPath'])) {
61
+			$coverPath = $album['cover']['dbPath'];
62
+			$relativePath = substr($coverPath, strlen(SYNOPHOTO_SERVICE_REAL_DIR_PATH));
63
+			$dirname = dirname($relativePath);
64
+			$sharename = in_array($dirname, array('.', ''), true) ? '/' : $dirname;
65
+        }
66
+
67
+        $smartAlbum['name'] = $dbObject['name'];
68
+        if (in_array('smart_condition', $param['additional'])) {
69
+			$smartAlbumCond = SmartAlbum::GetSmartAlbumInstance()->GetSmartCondition($dbObject['name']);
70
+            $smartAlbum['additional']['smart_condition'] = $smartAlbumCond;
71
+        }
72
+
73
+        $blConversion = csSYNOPhotoMisc::IsAlbumAllowConversion($sharename);
74
+        list($orig_resolutionx, $orig_resolutiony, $blThumbRotated) = AlbumAPIUtil::GetCoverResolutionRotation($coverPath);
75
+        AlbumAPIUtil::FillThumbStatus($smartAlbum, $coverPath, $needThumbSize, $orig_resolutionx, $orig_resolutiony, $blThumbRotated, $blConversion);
76
+
77
+        return $smartAlbum;
78
+	}
79
+
80
+	static function TagFormula($dbObject, $config = array())
81
+	{
82
+		$param = $config['param'];
83
+		$needThumbSize = isset($config['needThumbSize']) ? $config['needThumbSize'] : in_array('thumb_size', $param['additional']);
84
+		$id = $config['id'];
85
+
86
+		$tag['id'] = $id ? $id : 'tag_'.$dbObject['id'];
87
+		$tag['type'] = 'tag';
88
+		$tag['tag_type'] = Label::GetCategoryName($dbObject['category']);
89
+		$tag['name'] = $dbObject['name'];
90
+
91
+		$additional = self::TagAdditional($dbObject, $param);
92
+		if (null !== $additional) {
93
+			$tag['additional'] = $additional;
94
+		}
95
+		if ('true' === $param['thumbnail_status']) {
96
+			$result = csSYNOPhotoAlbum::GetAlbumInstance()->GetLabelAlbumCoverFromDB($dbObject['id']);
97
+			$coverPath = $result['dbPath'];
98
+			$relativePath = substr($coverPath, strlen(SYNOPHOTO_SERVICE_REAL_DIR_PATH));
99
+			$dirname = dirname($relativePath);
100
+			$sharename = in_array($dirname, array('.', ''), true) ? '/' : $dirname;
101
+
102
+			$blConversion = csSYNOPhotoMisc::IsAlbumAllowConversion($sharename);
103
+			list($orig_resolutionx, $orig_resolutiony, $blThumbRotated) = AlbumAPIUtil::GetCoverResolutionRotation($coverPath);
104
+			AlbumAPIUtil::FillThumbStatus($tag, $coverPath, $needThumbSize, $orig_resolutionx, $orig_resolutiony, $blThumbRotated, $blConversion);
105
+		}
106
+		return $tag;
107
+	}
108
+
109
+    private static function TagAdditional($data, $params)
110
+    {
111
+        if (in_array('info', $params['additional'])) {
112
+            if (Label::PEOPLE === $data['category']) {
113
+                $people['name'] = $data['name'];
114
+                $additional['info'] = $people;
115
+            } elseif (Label::GEO === $data['category']) {
116
+                $geo['name'] = $data['name'];
117
+                if (false !== ($extra = json_decode($data['info'], true))) {
118
+                    $geo['lat'] = $extra['lat'];
119
+                    $geo['lng'] = $extra['lng'];
120
+                    $geo['place_id'] = $extra['placeId'];
121
+                    $geo['address'] = (null === $extra['address']) ? '' : $extra['address'];
122
+                }
123
+                $additional['info'] = $geo;
124
+            } elseif (Label::DESC === $data['category']) {
125
+                $desc['name'] = $data['name'];
126
+                $additional['info'] = $desc;
127
+            }
128
+        }
129
+        if (in_array('extra', $params['additional'])) {
130
+            $additional['extra'] = $data['info'];
131
+        }
132
+        if (in_array('count', $params['additional'])) {
133
+            $total = ItemLabel::GetCountByLabelId($data['id']);
134
+            $additional['count'] = (int)$total;
135
+        }
136
+        return $additional;
137
+    }
138
+
139
+	static function VideoFormula($dbObject, $config = array())
140
+	{
141
+		$params = $config['param'];
142
+		$needThumbSize = $config['needThumbSize'] ? $config['needThumbSize'] : in_array('thumb_size', $params['additional']);
143
+		$item = array(
144
+			'id'	=> $config['id'] ? $config['id'] : self::EncodeItemId($dbObject['path'], "video_"),
145
+			'type'	=> 'video'
146
+		);
147
+        $customTitleDesc = csSYNOPhotoDB::GetDBInstance()->GetVideoCustomizedTitleDescription($dbObject['path']);
148
+
149
+		$item_info = array(
150
+			'name'			=> $dbObject['name'],
151
+			'title'			=> empty($customTitleDesc['title']) ? $dbObject['name'] : $customTitleDesc['title'],
152
+			'description'	=> $customTitleDesc['description'],
153
+			'createdate'	=> $dbObject['date'],
154
+			'takendate'		=> $dbObject['mdate'],
155
+			'size'			=> $dbObject['filesize'],
156
+			'duration'		=> (int)$dbObject['duration'],
157
+			'gps'			=> json_decode($dbObject['gps']),
158
+			'rotation'		=> (int)$dbObject['rotation']
159
+		);
160
+		if (isset($dbObject['pos'])) {
161
+			$item['pos'] = (int) $dbObject['pos'];
162
+		}
163
+
164
+		$coverPath = $dbObject['path'];
165
+		$sharepath = substr($coverPath, strlen(SYNOPHOTO_SERVICE_REAL_DIR_PATH));
166
+		$dir = in_array(dirname($sharepath), array('.', ''), true) ? '/' : dirname($sharepath);
167
+		// additional handler
168
+		$item['info'] = $item_info;
169
+		$item['additional'] = AlbumAPIUtil::FormAdditional('video', $dbObject, $params);
170
+
171
+		$isConversion = csSYNOPhotoMisc::IsAlbumAllowConversion($dir);
172
+
173
+		AlbumAPIUtil::FillThumbStatus($item, $coverPath, $needThumbSize, $dbObject['resolutionx'], $dbObject['resolutiony'], false, $isConversion);
174
+		return $item;
175
+	}
176
+
177
+	static function PhotoFormula($dbObject, $config = array())
178
+	{
179
+		$params = $config['param'];
180
+		$needThumbSize = $config['needThumbSize'] ? $config['needThumbSize'] : in_array('thumb_size', $params['additional']);
181
+		$item = array(
182
+			'id'	=> $config['id'] ? $config['id'] : self::EncodeItemId($dbObject['path'], "photo_"),
183
+			'type'	=> 'photo'
184
+		);
185
+
186
+		$item_info = array(
187
+			'name'			=> $dbObject['name'],
188
+			'title'			=> $dbObject['title'],
189
+			'description'	=> $dbObject['description'],
190
+			'createdate'	=> $dbObject['createdate'],
191
+			'takendate'		=> $dbObject['timetaken'],
192
+			'size'			=> $dbObject['size'],
193
+			'resolutionx'	=> (int)$dbObject['resolutionx'],
194
+			'resolutiony'	=> (int)$dbObject['resolutiony'],
195
+			'rotated'		=> (0 === (int)$dbObject['version'] % 2) ? false : true,
196
+			'rotate_version'=> (int)$dbObject['version'],
197
+			'rotation'		=> (int)$dbObject['rotation'],
198
+			'lat'			=> $dbObject['lat'],
199
+			'lng'			=> $dbObject['lng']
200
+		);
201
+		if (isset($dbObject['pos'])) {
202
+			$item['pos'] = (int) $dbObject['pos'];
203
+		}
204
+
205
+		if (isset($dbObject['gps_idx'])) {
206
+			$item['gps_idx'] = $dbObject['gps_idx'];
207
+		}
208
+
209
+		$coverPath = $dbObject['path'];
210
+		$blThumbRotated = (0 === (int)$dbObject['version'] % 2) ? false : true;
211
+
212
+		$sharepath = substr($coverPath, strlen(SYNOPHOTO_SERVICE_REAL_DIR_PATH));
213
+		$dir = in_array(dirname($sharepath), array('.', ''), true) ? '/' : dirname($sharepath);
214
+		// additional handler
215
+		$item['info'] = $item_info;
216
+		$item['additional'] = AlbumAPIUtil::FormAdditional('photo', $dbObject, $params);
217
+
218
+		$isConversion = csSYNOPhotoMisc::IsAlbumAllowConversion($dir);
219
+
220
+		AlbumAPIUtil::FillThumbStatus($item, $coverPath, $needThumbSize, $dbObject['resolutionx'], $dbObject['resolutiony'], $blThumbRotated, $isConversion);
221
+		return $item;
222
+	}
223
+
224
+	static function EncodeItemId($id, $prefix) {
225
+		if (!in_array($prefix, array('photo_', 'video_'))) {
226
+			return $prefix.bin2hex($id);
227
+		}
228
+
229
+		$sharepath = substr($id, strlen(SYNOPHOTO_SERVICE_REAL_DIR_PATH));
230
+		$fileName = basename($sharepath);
231
+		$sharename = dirname($sharepath);
232
+		if ('.' === $sharename || '' === $sharename) {
233
+			$sharename = '/';
234
+		}
235
+		return $prefix.bin2hex($sharename)."_".bin2hex($fileName);
236
+	}
237
+}
238
+
239
+?>

+ 2
- 0
PhotoStation API/webapi/group.conf.php 查看文件

@@ -0,0 +1,2 @@
1
+<?php
2
+	define('GROUP_CONF_WEBAPI_SERVICE_INCLUDE_PATH', dirname(__FILE__) . '/webapi.inc.php');

+ 6
- 0
PhotoStation API/webapi/group.inc.php 查看文件

@@ -0,0 +1,6 @@
1
+<?php
2
+	require_once('group.conf.php');
3
+	require_once("../include/syno_conf.php");
4
+
5
+	require_once(GROUP_CONF_WEBAPI_SERVICE_INCLUDE_PATH);
6
+	require_once(SZ_WEBAPI_CLASS_PATH);

+ 407
- 0
PhotoStation API/webapi/group.php 查看文件

@@ -0,0 +1,407 @@
1
+<?PHP
2
+
3
+require_once('group.inc.php');
4
+
5
+class GroupAPI extends WebAPI
6
+{
7
+	function __construct()
8
+	{
9
+		parent::__construct(SZ_WEBAPI_API_DESCRIPTION_PATH);
10
+	}
11
+
12
+	protected function Process()
13
+	{
14
+		/* if not admin, returns directly */
15
+		csSYNOPhotoMisc::CheckSessionTimeOut();
16
+
17
+		if (!strcasecmp($this->method, "list")) {
18
+			$this->ListGroup();
19
+		}
20
+		if (!strcasecmp($this->method, "get")) {
21
+			$this->GetGroup();
22
+		}
23
+		if (!strcasecmp($this->method, "get_dsm_group")) {
24
+			$this->GetDSMGroup();
25
+		}
26
+		if (!strcasecmp($this->method, "getmember")) {
27
+			$this->GetGroupMember();
28
+		}
29
+		if (!strcasecmp($this->method, "create")) {
30
+			$this->CreateGroup();
31
+		}
32
+		if (!strcasecmp($this->method, "delete")) {
33
+			$this->DeleteGroup();
34
+		}
35
+		if (!strcasecmp($this->method, "editmember")) {
36
+			$this->EditMember();
37
+		}
38
+		if (!strcasecmp($this->method, "edit")) {
39
+			$this->EditGroup();
40
+		}
41
+	}
42
+
43
+	private function ListGroup()
44
+	{
45
+		$ret = false;
46
+		$resp = array();
47
+
48
+		/* set params */
49
+		$offset = isset($_REQUEST['offset']) ? $_REQUEST['offset'] : 0;
50
+		$limit = isset($_REQUEST['limit']) ? $_REQUEST['limit'] : 15;
51
+		$search = isset($_REQUEST['query']) ? $_REQUEST['query'] : '';
52
+
53
+		$groups = $this->GetAllGroups($offset, $limit, $search);
54
+
55
+		$resp['totalCount'] = $groups['totalCount'];
56
+		$resp['groups'] = $groups['all_groups'];
57
+		$this->SetResponse($resp);
58
+
59
+		$ret = true;
60
+	End:
61
+		return $ret;
62
+
63
+	}
64
+
65
+	private function GetGroupPublicShareRight($gid)
66
+	{
67
+		$query = "SELECT * FROM " . SHARED_ALBUM_GROUP_PRIVILEGE_TABLE_NAME . " WHERE groupid = ?";
68
+		$sqlParam = array($gid);
69
+		$db_result = PHOTO_DB_Query($query, $sqlParam);
70
+		if (($row = PHOTO_DB_FetchRow($db_result))) {
71
+			return 'on';
72
+		} else {
73
+			return 'off';
74
+		}
75
+	}
76
+	private function UpdateGroupPublicShareRight($gid, $public_share) {
77
+		if ($public_share === 'true') {
78
+			$query = "INSERT INTO " . SHARED_ALBUM_GROUP_PRIVILEGE_TABLE_NAME . " VALUES (?)";
79
+		} else {
80
+			$query = "DELETE FROM " . SHARED_ALBUM_GROUP_PRIVILEGE_TABLE_NAME . " WHERE groupid = (?)";
81
+		}
82
+		$sqlParam = array($gid);
83
+		$db_result = PHOTO_DB_Query($query, $sqlParam);
84
+	}
85
+
86
+	private function GetGroup()
87
+	{
88
+		$ret = false;
89
+		$resp = array();
90
+
91
+		if (!isset($_REQUEST['id'])) {
92
+			$this->SetError(PHOTOSTATION_PERMISSION_BAD_PARAMS);
93
+			goto End;
94
+		}
95
+		if ($_SESSION[SYNOPHOTO_ADMIN_USER]['use_dsm_account']) {
96
+			$this->SetError(PHOTOSTATION_PERMISSION_BAD_PARAMS);
97
+			goto End;
98
+		}
99
+
100
+		/* set params */
101
+		$id = $_REQUEST['id'];
102
+
103
+		$query = "SELECT * FROM photo_group WHERE groupid = ?";
104
+		$sqlParam = array($id);
105
+		$db_result = PHOTO_DB_Query($query, $sqlParam);
106
+
107
+		if (false === ($row = PHOTO_DB_FetchRow($db_result))) {
108
+			$this->SetError(PHOTOSTATION_PERMISSION_BAD_PARAMS);
109
+			goto End;
110
+		}
111
+		$resp['groupid'] = $row['groupid'];
112
+		$resp['groupname'] = $row['groupname'];
113
+		$resp['description'] = $row['description'];
114
+		$resp['public_share'] = $this->GetGroupPublicShareRight($id);
115
+
116
+		$this->SetResponse($resp);
117
+
118
+		$ret = true;
119
+	End:
120
+		return $ret;
121
+	}
122
+
123
+	private function GetDSMGroup() {
124
+		$ret = false;
125
+		$resp = array();
126
+
127
+		if (!isset($_REQUEST['id'])) {
128
+			$this->SetError(PHOTOSTATION_PERMISSION_BAD_PARAMS);
129
+			goto End;
130
+		}
131
+
132
+		/* set params */
133
+		$id = $_REQUEST['id'];
134
+		$resp['public_share'] = $this->GetGroupPublicShareRight($id);
135
+
136
+		$this->SetResponse($resp);
137
+
138
+		$ret = true;
139
+	End:
140
+		return $ret;
141
+	}
142
+
143
+	private function GetGroupMember()
144
+	{
145
+		$ret = false;
146
+		$resp = array();
147
+
148
+		if (!isset($_REQUEST['id'])) {
149
+			$this->SetError(PHOTOSTATION_PERMISSION_BAD_PARAMS);
150
+			goto End;
151
+		}
152
+		if ($_SESSION[SYNOPHOTO_ADMIN_USER]['use_dsm_account']) {
153
+			$this->SetError(PHOTOSTATION_PERMISSION_BAD_PARAMS);
154
+			goto End;
155
+		}
156
+
157
+		/* set params */
158
+		$id = $_REQUEST['id'];
159
+		$start = isset($_REQUEST['offset']) ? $_REQUEST['offset'] : 0;
160
+		$limit = isset($_REQUEST['limit']) ? $_REQUEST['limit'] : 15;
161
+
162
+		$limitOffsetString = PHOTO_DB_GetLimitOffsetString($limit, $start);
163
+		$query = "SELECT A.userid, A.username, A.description, B.groupid FROM photo_user A LEFT JOIN photo_user_group B ON A.userid = B.userid AND B.groupid = ? $limitOffsetString";
164
+		$sqlParam = array($id);
165
+		$db_result = PHOTO_DB_Query($query, $sqlParam);
166
+
167
+		$resp['users'] = array();
168
+		while($row = PHOTO_DB_FetchRow($db_result)) {
169
+			$item = array();
170
+			$item['id'] = $row['userid'];
171
+			$item['username'] = $row['username'];
172
+			$item['description'] = $row['description'];
173
+			$item['add'] = $id == $row['groupid'];
174
+			$resp['users'][] = $item;
175
+		}
176
+
177
+		$query = "SELECT count(*) FROM photo_user";
178
+		$db_result = PHOTO_DB_Query($query);
179
+		$row = PHOTO_DB_FetchRow($db_result);
180
+
181
+		$resp['totalCount'] = $row[0];
182
+		$this->SetResponse($resp);
183
+
184
+		$ret = true;
185
+	End:
186
+		return $ret;
187
+	}
188
+
189
+	private function CreateGroup()
190
+	{
191
+		$ret = false;
192
+		$resp = array();
193
+
194
+		if (!isset($_REQUEST['groupname'])) {
195
+			$this->SetError(PHOTOSTATION_PERMISSION_BAD_PARAMS);
196
+			goto End;
197
+		}
198
+
199
+		if ($_SESSION[SYNOPHOTO_ADMIN_USER]['use_dsm_account']) {
200
+			$this->SetError(PHOTOSTATION_PERMISSION_BAD_PARAMS);
201
+			goto End;
202
+		}
203
+
204
+		/* set params */
205
+		$groupname = $_REQUEST['groupname'];
206
+		$description = isset($_REQUEST['description']) ? $_REQUEST['description'] : '';
207
+
208
+		$query = "SELECT MAX(groupid) FROM photo_group";
209
+		$db_result = PHOTO_DB_Query($query);
210
+		$max_gid = PHOTO_DB_FetchRow($db_result);
211
+		$new_gid = $max_gid[0] + 1;
212
+		$query = "INSERT INTO photo_group (groupid, groupname, description) VALUES (?, ?, ?)";
213
+		$sqlParam = array($new_gid, $groupname, $description);
214
+		PHOTO_DB_Query($query, $sqlParam);
215
+
216
+		$this->UpdateGroupPublicShareRight($new_id, $_REQUEST['public_share']);
217
+		$resp['id'] = $new_gid;
218
+		$this->SetResponse($resp);
219
+
220
+		$ret = true;
221
+	End:
222
+		return $ret;
223
+	}
224
+
225
+	private function EditMember()
226
+	{
227
+		$ret = false;
228
+		$resp = array();
229
+
230
+		if (!isset($_REQUEST['id'])) {
231
+			$this->SetError(PHOTOSTATION_PERMISSION_BAD_PARAMS);
232
+			goto End;
233
+		}
234
+
235
+		if ($_SESSION[SYNOPHOTO_ADMIN_USER]['use_dsm_account']) {
236
+			$this->SetError(PHOTOSTATION_PERMISSION_BAD_PARAMS);
237
+			goto End;
238
+		}
239
+
240
+		/* set params */
241
+		$gid = $_REQUEST['id'];
242
+		$addList = isset($_REQUEST['group_user_add']) ? $_REQUEST['group_user_add'] : '';
243
+		$deleteList = isset($_REQUEST['group_user_delete']) ? $_REQUEST['group_user_delete'] : '';
244
+
245
+		$this->DeleteUserFromGroup($gid, $deleteList);
246
+		$this->AddUserToGroup($gid, $addList);
247
+
248
+		$this->SetResponse($resp);
249
+
250
+		$ret = true;
251
+	End:
252
+		return $ret;
253
+	}
254
+
255
+	private function DeleteUserFromGroup($gid, $users)
256
+	{
257
+		if ($gid == null || $gid == "" || $users == null || $users == "") {
258
+			return;
259
+		}
260
+
261
+		$ids = explode(',', $users);
262
+		foreach ($ids as $id) {
263
+			if ('' === $id) {
264
+				continue;
265
+			}
266
+			$query = "DELETE FROM photo_user_group WHERE userid = ? AND groupid = ?";
267
+			$sqlParam = array($id, $gid);
268
+			$db_result = PHOTO_DB_Query($query, $sqlParam);
269
+		}
270
+	}
271
+
272
+	private function AddUserToGroup($gid, $users)
273
+	{
274
+		if ($gid == null || $gid == "" || $users == null || $users == "") {
275
+			return;
276
+		}
277
+
278
+		$ids = explode(',', $users);
279
+		foreach ($ids as $id) {
280
+			if ('' === $id) {
281
+				continue;
282
+			}
283
+			$query = "SELECT MAX(id) FROM photo_user_group";
284
+			$db_result = PHOTO_DB_Query($query);
285
+			$max_id = PHOTO_DB_FetchRow($db_result);
286
+			$new_id = $max_id[0] + 1;
287
+			$query = "INSERT INTO photo_user_group (id, userid, groupid) VALUES (?, ?, ?)";
288
+			$sqlParam = array($new_id, $id, $gid);
289
+			$db_result = PHOTO_DB_Query($query, $sqlParam);
290
+		}
291
+	}
292
+
293
+	private function EditGroup()
294
+	{
295
+		$ret = false;
296
+		$resp = array();
297
+
298
+		if (!isset($_REQUEST['id'])) {
299
+			$this->SetError(PHOTOSTATION_PERMISSION_BAD_PARAMS);
300
+			goto End;
301
+		}
302
+
303
+		/* set params */
304
+		$id = $_REQUEST['id'];
305
+		$description = isset($_REQUEST['description']) ? $_REQUEST['description'] : null;
306
+
307
+		$this->UpdateGroupPublicShareRight($id, $_REQUEST['public_share']);
308
+		if ($_SESSION[SYNOPHOTO_ADMIN_USER]['use_dsm_account']) {
309
+			$this->SetResponse($resp);
310
+			goto End;
311
+		}
312
+
313
+		if (isset($_REQUEST['description'])) {
314
+			$query = "UPDATE photo_group set description = ? where groupid = ?";
315
+			$sqlParam = array($description, $id);
316
+			$db_result = PHOTO_DB_Query($query, $sqlParam);
317
+		}
318
+
319
+		$this->SetResponse($resp);
320
+
321
+		$ret = true;
322
+	End:
323
+		return $ret;
324
+	}
325
+
326
+	private function DeleteGroup() {
327
+		$ret = false;
328
+		$resp = array();
329
+
330
+		if (!isset($_REQUEST['id'])) {
331
+			$this->SetError(PHOTOSTATION_PERMISSION_BAD_PARAMS);
332
+			goto End;
333
+		}
334
+
335
+		/* set params */
336
+		$gid = $_REQUEST['id'];
337
+
338
+		$query = "DELETE FROM photo_group WHERE groupid = ?";
339
+		$sqlParam = array($gid);
340
+		PHOTO_DB_Query($query, $sqlParam);
341
+
342
+		$this->SetResponse($resp);
343
+
344
+		$ret = true;
345
+	End:
346
+		return $ret;
347
+	}
348
+
349
+	private function GetAllGroups($start, $limit, $search)
350
+	{
351
+		if ($_SESSION[SYNOPHOTO_ADMIN_USER]['use_dsm_account']) {
352
+			return $this->GetAllDSMGroup($start, $limit, $search);
353
+		}
354
+
355
+		$i = 0;
356
+		$limitOffsetString = PHOTO_DB_GetLimitOffsetString($limit, $start);
357
+		$query = "SELECT * FROM photo_group WHERE groupname LIKE ? ORDER BY groupname ASC $limitOffsetString";
358
+		$db_result = PHOTO_DB_Query($query, array("%$search%"));
359
+		$result = array('all_groups' => array());
360
+		while ($row = PHOTO_DB_FetchRow($db_result)) {
361
+			$result['all_groups'][$i]['groupid'] = $row['groupid'];
362
+			$result['all_groups'][$i]['groupname'] = $row['groupname'];
363
+			$result['all_groups'][$i]['description'] = $row['description'];
364
+			$i ++;
365
+		}
366
+		$query = "SELECT count(*) FROM photo_group WHERE groupname LIKE ?";
367
+		$db_result = PHOTO_DB_Query($query, array("%$search%"));
368
+		$row = PHOTO_DB_FetchRow($db_result);
369
+		$result['totalCount'] = $row[0];
370
+		return $result;
371
+	}
372
+
373
+	private function GetAllDSMGroup($start, $limit, $query = '')
374
+	{
375
+		$command = "/usr/syno/bin/synophoto_dsm_user --group " . escapeshellarg($query);
376
+		@exec($command, $pListGroupCount, $retval);
377
+		if (0 > $retval) {
378
+			$result['totalCount'] = 0;
379
+			return $result;
380
+		}
381
+		$result['totalCount'] = $pListGroupCount[0];
382
+
383
+		$command = "/usr/syno/bin/synophoto_dsm_user --group " . escapeshellarg($start) . " " . escapeshellarg($limit) . " ASC:" . escapeshellarg($query);
384
+		@exec($command, $pListGroupName, $retval);
385
+
386
+		if (0 !== $retval) {
387
+			$result['totalCount'] = 0;
388
+			return $result;
389
+		}
390
+
391
+		$i = 0;
392
+		$result['all_groups'] = array();
393
+		foreach ($pListGroupName as $group_str) {
394
+			$group_info = split(',', $group_str);
395
+			$result['all_groups'][$i]['groupid'] = $group_info[0];
396
+			$result['all_groups'][$i]['groupname'] = $group_info[1];
397
+			$result['all_groups'][$i]['description'] = htmlspecialchars($group_info[2], ENT_QUOTES);
398
+			$result['all_groups'][$i]['disable'] = 'true' === $group_info[3] ? 't' : 'f';
399
+			$i ++;
400
+		}
401
+
402
+		return $result;
403
+	}
404
+}
405
+
406
+$api = new GroupAPI();
407
+$api->Run();

+ 3
- 0
PhotoStation API/webapi/info.conf.php 查看文件

@@ -0,0 +1,3 @@
1
+<?php
2
+    define('INFO_CONF_WEBAPI_SERVICE_INCLUDE_PATH', dirname(__FILE__).'/webapi.inc.php');
3
+?>

+ 7
- 0
PhotoStation API/webapi/info.inc.php 查看文件

@@ -0,0 +1,7 @@
1
+<?php
2
+    require_once('info.conf.php');
3
+    require_once("../include/syno_conf.php");
4
+
5
+    require_once(INFO_CONF_WEBAPI_SERVICE_INCLUDE_PATH);
6
+    require_once(SZ_WEBAPI_CLASS_PATH);
7
+?>

+ 168
- 0
PhotoStation API/webapi/info.php 查看文件

@@ -0,0 +1,168 @@
1
+<?PHP
2
+
3
+require_once('info.inc.php');
4
+
5
+class InfoAPI extends WebAPI {
6
+    function __construct() {
7
+        parent::__construct(SZ_WEBAPI_API_DESCRIPTION_PATH);
8
+    }
9
+
10
+    protected function Process() {
11
+        if (!strcasecmp($this->method, "getinfo")) {
12
+            $this->GetInfo();
13
+
14
+//			$this->CheckShareId();
15
+        }
16
+    }
17
+
18
+	private function CheckShareId()
19
+	{
20
+		$PHP_BIN = "/usr/bin/php";
21
+		$PHP_PARAMETER = "-n -d extension=pdo.so -d extension=pdo_pgsql.so -d extension=pdo_sqlite.so";
22
+		$SHARE_ID_UPDATE_PHP = SYNO_PKG_DIR."/target/photo_scripts/update_photo_share.php";
23
+		$cmd = sprintf("%s %s %s '%s' >/dev/null &", $PHP_BIN, $PHP_PARAMETER, $SHARE_ID_UPDATE_PHP, SYNOPHOTO_ADMIN_NAME);
24
+
25
+		system($cmd);
26
+	}
27
+
28
+    private function GetInfo() {
29
+        $resp = array();
30
+
31
+        // get version
32
+        $resp['version'] = '2965';
33
+
34
+        // get PhotoStation title
35
+        $resp['title'] = trim(csSYNOPhotoMisc::GetConfigDB("photo", "photo_page_title", "photo_config"));
36
+
37
+        // get about_me title
38
+        $value = csSYNOPhotoMisc::GetConfigDB("photo", "about_me_title", "photo_config");
39
+        $resp['about_me_title'] = $value ? $value : __(photo_str_category_about_me);
40
+
41
+        // get root album sort_by
42
+        $value = csSYNOPhotoMisc::GetConfigDB("album", "thumb_sort_type", "photo_config");
43
+        $resp['sort_by'] = 'filename';
44
+        if ('0' === $value) {
45
+            $resp['sort_by'] = 'filename';
46
+        } elseif ('1' === $value) {
47
+            $resp['sort_by'] = 'takendate';
48
+        } elseif ('2' === $value) {
49
+            $resp['sort_by'] = 'createdate';
50
+        }
51
+
52
+        // get root album sort_direction
53
+        $value = csSYNOPhotoMisc::GetConfigDB("album", "thumb_sort_order", "photo_config");
54
+        $resp['sort_direction'] = ('1' === $value) ? 'desc' : 'asc';
55
+
56
+        // get use album explorer setting
57
+        $value = csSYNOPhotoMisc::GetConfigDB("album", "use_album_explorer", "photo_config");
58
+        $resp['use_album_explorer'] = 'off' !== $value; //return "on" by default
59
+
60
+        // get paging setting
61
+        $value = csSYNOPhotoMisc::GetConfigDB("album", "paging_use_bar", "photo_config");
62
+        $resp['paging_use_bar'] = 'off' !== $value; //return "on" by default
63
+        $value = intval(csSYNOPhotoMisc::GetConfigDB("album", "paging_item_count", "photo_config"));
64
+        $resp['paging_item_count'] = ($value >= 50 && $value <= 500) ? $value : 50;
65
+
66
+        // get folder sort direction
67
+        $value = csSYNOPhotoMisc::GetConfigDB("album", "album_order_type_is_desc", "photo_config");
68
+        $resp['folder_sort_direction'] = ("1" === $value) ? 'desc' : 'asc';
69
+
70
+        // get allow downoload original photo
71
+		/*
72
+        $value = csSYNOPhotoMisc::GetConfigDB("photo", "allow_orig", "photo_config");
73
+        $resp['allow_download_orig'] = ('on' === $value) ? true : false;
74
+		*/
75
+
76
+		// get allow download album
77
+		$value = csSYNOPhotoDB::GetDBInstance()->GetConfig('photo', 'allow_album_download');
78
+		$resp['allow_download_album'] = ('on' === $value) ? true : false;
79
+		// #2253 - reserve these two key for compatibility with DS photo+ which is using webapi already
80
+		$resp['allow_download_orig'] = $resp['allow_download_album'];
81
+		$resp['allow_download_video'] = $resp['allow_download_album'];
82
+
83
+		// get allow download video
84
+		/*
85
+		$value = csSYNOPhotoMisc::GetConfigDB('photo', 'allow_video_download', 'photo_config');
86
+		$resp['allow_download_video'] = ('on' === $value) ? true : false;
87
+		*/
88
+
89
+		// get disable right button
90
+		$value = csSYNOPhotoMisc::GetConfigDB('photo', 'disable_right_button', 'photo_config');
91
+		$resp['disable_right_button'] = ('on' === $value) ? true : false;
92
+
93
+        // get allow search
94
+        $value = csSYNOPhotoMisc::GetConfigDB("photo", "hide_search", "photo_config");
95
+        $resp['hide_search'] = ('on' === $value) ? true : false;
96
+
97
+        // get hide gps info from normal user
98
+        $value = csSYNOPhotoMisc::GetConfigDB("photo", "hide_gps_from_normal_user", "photo_config");
99
+        $resp['hide_gps_from_normal_user'] = ('on' === $value) ? true : false;
100
+
101
+        // get allow rss feed
102
+        $value = csSYNOPhotoMisc::GetConfigDB("photo", "hide_rss_feed", "photo_config");
103
+        $resp['hide_rss_feed'] = ('on' === $value) ? true : false;
104
+
105
+        // get enable blog
106
+        $resp['enable_blog'] = csSYNOPhotoMisc::IsBlogEnabled();
107
+ 
108
+		$resp['external_host'] = csSYNOPhotoMisc::GetServerHost();
109
+		$resp['external_host_quickconnect'] = csSYNOPhotoMisc::GetServerHost(true);
110
+		$resp['external_host_external_ip'] = csSYNOPhotoMisc::GetServerHost(true, true);
111
+		$resp['allow_social_share'] = !("on" == csSYNOPhotoDB::GetDBInstance()->GetConfig("photo", "disable_share"));
112
+		$resp['allow_social_upload'] = ("on" == csSYNOPhotoMisc::GetConfigDB("photo", "allow_guest_fb_upload", "photo_config")); // means user actually...
113
+		$resp['allow_social_upload_guest'] = $resp['allow_social_upload'] && ("on" == csSYNOPhotoMisc::GetConfigDB("photo", "allow_social_network_upload_guest", "photo_config")); // real guest...
114
+		$resp['social_network_list'] = SocialNetwork::ReadList();
115
+
116
+        // get virtual album (people, geo, desc tag)
117
+        $tag['people_tag'] = $tag['geo_tag'] = $tag['desc_tag'] = false;
118
+        if ('on' === ($value = csSYNOPhotoMisc::GetConfigDB("photo", "person_label_album", "photo_config"))) {
119
+            $tag['people_tag'] = true;
120
+        }
121
+        if ('on' === ($value = csSYNOPhotoMisc::GetConfigDB("photo", "place_label_album", "photo_config"))) {
122
+            $tag['geo_tag'] = true;
123
+        }
124
+        if ('on' === ($value = csSYNOPhotoMisc::GetConfigDB("photo", "general_label_album", "photo_config"))) {
125
+            $tag['desc_tag'] = true;
126
+        }
127
+        $resp['virtual_tag'] = $tag;
128
+
129
+        // get default get location
130
+        $value = csSYNOPhotoMisc::GetConfigDB("gmap", "gps_default", "photo_config");
131
+        $location['lat'] = $location['lng'] = '';
132
+        $arr = explode(',', $value);
133
+        if (2 === count($arr)) {
134
+            $location['lat'] = $arr[0];
135
+            $location['lng'] = $arr[1];
136
+        }
137
+        $resp['default_geo_location'] = $location;
138
+
139
+        // get home category
140
+        $resp['home_category'] = csSYNOPhotoMisc::GetConfigDB("album", "home_category", "photo_config");
141
+
142
+        // get default album type
143
+        $resp['default_album_public'] = false;
144
+        if (isSet($_SESSION[SYNOPHOTO_ADMIN_USER]['system_config']['pkgCfg']['albumdefpublic']) &&
145
+            'yes' == $_SESSION[SYNOPHOTO_ADMIN_USER]['system_config']['pkgCfg']['albumdefpublic']) {
146
+            $resp['default_album_public'] = true;
147
+        }
148
+
149
+        // get disable_aboutme
150
+        $resp['disable_aboutme'] = 'on' === csSYNOPhotoMisc::GetConfigDB("photo", "disable_aboutme", "photo_config") ? true : false;
151
+
152
+        $resp['show_album_hit'] = "off" === csSYNOPhotoMisc::GetConfigDB("photo", "show_album_hit", "photo_config") ? false : true;
153
+
154
+        $resp['use_dsm_account'] = ("1" === csSYNOPhotoMisc::GetConfigDB("global", "account_system", "photo_config"));
155
+
156
+        $resp['collapse_left_panel'] = 'on' === csSYNOPhotoMisc::GetConfigDB("photo", "collapse_left_panel", "photo_config") ? true : false;
157
+        $resp['hide_lightbox_information'] = 'on' === csSYNOPhotoMisc::GetConfigDB("photo", "hide_lightbox_information", "photo_config") ? true : false;
158
+		$resp['use_pop_window_to_edit_desc'] = 'on' === csSYNOPhotoMisc::GetConfigDB("photo", "use_pop_window_to_edit_desc", "photo_config") ? true : false;
159
+		$resp['def_album_disable_conversion'] = 'on' === csSYNOPhotoMisc::GetConfigDB("photo", "def_album_disable_conversion", "photo_config") ? true : false;
160
+
161
+        $this->SetResponse($resp);
162
+    }
163
+}
164
+
165
+$api = new InfoAPI();
166
+$api->Run();
167
+
168
+?>

+ 6
- 0
PhotoStation API/webapi/log.inc.php 查看文件

@@ -0,0 +1,6 @@
1
+<?
2
+	require_once("../include/syno_conf.php");
3
+	require_once(dirname(__FILE__).'/webapi.inc.php');
4
+	require_once('../include/log.php');
5
+	require_once(SZ_WEBAPI_CLASS_PATH);
6
+?>

+ 154
- 0
PhotoStation API/webapi/log.php 查看文件

@@ -0,0 +1,154 @@
1
+<?php
2
+require_once('log.inc.php');
3
+
4
+class PhotoLogAPI extends WebAPI {
5
+	private $operationPemission = "admin";
6
+
7
+	function __construct() {
8
+		parent::__construct(SZ_WEBAPI_API_DESCRIPTION_PATH);
9
+	}
10
+
11
+	protected function Process()
12
+	{
13
+		if (!strcasecmp($this->method, "list")) {
14
+			$this->LogList();
15
+		} else if (!strcasecmp($this->method, "clear")) {
16
+			$this->Clear();
17
+		} else if (!strcasecmp($this->method, "export")) {
18
+			$this->Export();
19
+		}
20
+	}
21
+
22
+	private function LogList()
23
+	{
24
+		$resp = array();
25
+
26
+		$params = $this->GetParams_List();
27
+		if (!$params) {
28
+			$this->SetError(WEBAPI_ERR_BAD_REQUEST);
29
+			goto End;
30
+		}
31
+
32
+		$res = PhotoLog::ListItem($params['offset'], $params['limit']);
33
+
34
+		$resp['items'] = $res['data'];
35
+		$resp['total'] = $res['total'];
36
+        $offset = (0 > (int)$params['limit']) ? $resp['total'] : $params['offset'] + $params['limit'];
37
+        $resp['offset'] = ($offset > $resp['total']) ?  $resp['total'] : $offset;
38
+
39
+		$this->SetResponse($resp);
40
+	End:
41
+		return;
42
+	}
43
+
44
+	private function Clear()
45
+	{
46
+		$params = $this->GetParams_Clear();
47
+		if (!$params) {
48
+			$this->SetError(WEBAPI_ERR_BAD_REQUEST);
49
+			goto End;
50
+		}
51
+
52
+		PhotoLog::Clear($params['user']);
53
+
54
+	End:
55
+		return;
56
+	}
57
+
58
+	private function Export()
59
+	{
60
+		$params = $this->GetParams_Export();
61
+		if (!$params) {
62
+			$this->SetError(WEBAPI_ERR_BAD_REQUEST);
63
+			goto End;
64
+		}
65
+
66
+		PhotoLog::ExportFile($params['format']);
67
+
68
+	End:
69
+		return;
70
+	}
71
+
72
+	private function GetParams_List()
73
+	{
74
+		// $variable + 0 => convert to integer
75
+		$params = array(
76
+			'offset' => !isset($_REQUEST['offset']) || $_REQUEST['offset'] < 0 ? 0 : $_REQUEST['offset'] + 0,
77
+			'limit' => !isset($_REQUEST['limit']) || $_REQUEST['limit'] < 0 ? NULL : $_REQUEST['limit'] + 0
78
+		);
79
+		return $params;
80
+	}
81
+
82
+	private function GetParams_Export()
83
+	{
84
+		$format = strtolower($_REQUEST['format']);
85
+		if (!in_array($format, PhotoLog::$SupportFormat)) {
86
+			return false;
87
+		}
88
+
89
+		return array("format" => $format);
90
+	}
91
+
92
+	private function GetParams_Clear()
93
+	{
94
+		$user = $this->GetUser();
95
+
96
+		if (!$user) {
97
+			return false;
98
+		}
99
+
100
+		return array("user" => $user);
101
+	}
102
+
103
+	private function GetUser()
104
+	{
105
+		$user = false;
106
+
107
+		if (isset($_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_user'])) {
108
+			$user = $_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_user'];
109
+		} else if (isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user'])) {
110
+			$user = ("root" === SYNOPHOTO_ADMIN_USER)? "admin" : SYNOPHOTO_ADMIN_NAME;
111
+		}
112
+		return $user;
113
+	}
114
+
115
+	protected function CheckPermission()
116
+	{
117
+		$res = true;
118
+		$check = array(
119
+			"list" => $this->operationPemission,
120
+			"clear" => $this->operationPemission,
121
+			"export" => $this->operationPemission
122
+		);
123
+
124
+		if (!array_key_exists($this->method, $check)) {
125
+			goto End;
126
+		}
127
+
128
+		$funcName = "check_".$check[$this->method];
129
+
130
+		if (!method_exists($this, $funcName)) {
131
+			$res = false;
132
+			goto End;
133
+		}
134
+
135
+		$res = $this->$funcName();
136
+
137
+	End:
138
+		if (!$res) {
139
+			$this->SetError(PHOTOSTATION_LOG_ACCESS_DENY);
140
+		}
141
+		return $res;
142
+	}
143
+
144
+	private function check_admin()
145
+	{
146
+		csSYNOPhotoDB::GetDBInstance()->SetSessionCache();
147
+		csSYNOPhotoMisc::CheckSessionTimeOut();
148
+		return isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']);
149
+	}
150
+}
151
+$api = new PhotoLogAPI();
152
+$api->Run();
153
+
154
+?>

+ 7
- 0
PhotoStation API/webapi/path.inc.php 查看文件

@@ -0,0 +1,7 @@
1
+<?
2
+    require_once("../include/syno_conf.php");
3
+	require_once(dirname(__FILE__).'/webapi.inc.php');
4
+	require_once(SZ_WEBAPI_CLASS_PATH);
5
+	require_once('albumutil.php');
6
+	require_once('../include/item_label.php');
7
+	require_once('../include/label.php');

+ 383
- 0
PhotoStation API/webapi/path.php 查看文件

@@ -0,0 +1,383 @@
1
+<?
2
+require_once('path.inc.php');
3
+
4
+class PathAPI extends WebAPI {
5
+	private $isAdmin = false;
6
+	private $validIdPrefix = array(
7
+						// array( getIDFunction, GetDBObjectFunc, CheckPermission, HasItemFunc, Formula)
8
+		'defaultsmart' => array('GetId', null, null, null, null),
9
+		'smart' => array('GetName', 'SmartAlbum::GetByNames', null, 'SmartHasItem', 'Decorator::SmartFormula'),
10
+		'tag' => array('GetId', 'Label::GetByIds', null, 'TagHasItem', 'Decorator::TagFormula'),
11
+		'album' => array('GetName', 'Album::GetBySharename', 'AlbumAccessible', 'AlbumHasItem', 'Decorator::AlbumFormula'),
12
+		'category' => array('GetId', 'Category::GetByIds', 'CategoryAccessible', 'CategoryHasItem', 'Decorator::CategoryFormula'),
13
+		'photo' => array('GetItemName', 'File::GetPhotoByDBPath', 'ItemAccessible', 'HasNoItem', 'Decorator::PhotoFormula'),
14
+		'video' => array('GetItemName', 'File::GetVideoByDBPath', 'ItemAccessible', 'HasNoItem', 'Decorator::VideoFormula')
15
+	);
16
+
17
+	function __construct() {
18
+		parent::__construct(SZ_WEBAPI_API_DESCRIPTION_PATH);
19
+	}
20
+
21
+	protected function Process()
22
+	{
23
+		if (!strcasecmp($this->method, "getpath")) {
24
+			$this->GetPath();
25
+		} else if (!strcasecmp($this->method, "checkpath")) {
26
+			$this->CheckPath();
27
+		}
28
+	}
29
+
30
+	private function GetPath()
31
+	{
32
+		$param = $this->GetParam_GetPath();
33
+		$items = array();
34
+		if (!$param) {
35
+			$this->SetError(WEBAPI_ERR_BAD_REQUEST);
36
+			goto End;
37
+		}
38
+		$items[] = AlbumAPIUtil::GetRootAlbumInfo($param);
39
+		if (0 !== count($param['sharepath'])) {
40
+			$result = Album::GetBySharename($param['sharepath']);
41
+
42
+			$allowComment = AlbumAPIUtil::IsAlbumCommentalbGlobal();
43
+			$showAlbumHit = AlbumAPIUtil::IsShowAlbumHit();
44
+			$needThumbSize = in_array('thumb_size', $param['additional']);
45
+
46
+			foreach ($param['sharepath'] as $sharepath) {
47
+				$item = array();
48
+
49
+				$item = Decorator::AlbumFormula($result[$sharepath], array(
50
+					'allowComment' => $allowComment,
51
+					'showAlbumHit' => $showAlbumHit,
52
+					'param'		   => $param,
53
+					'needThumbSize' => $needThumbSize
54
+				));
55
+
56
+				$items[] = $item;
57
+			}
58
+		}
59
+
60
+		$resp = array(
61
+			'items' => $items,
62
+			'total' => count($items)
63
+		);
64
+		$this->SetResponse($resp);
65
+	End:
66
+		return;
67
+	}
68
+
69
+	private function GetParam_GetPath()
70
+	{
71
+		if (!isset($_REQUEST['id'])){
72
+			return false;
73
+		}
74
+		if (preg_match('/^(photo|video)_/', $_REQUEST['id'])) {
75
+			$ids = explode("_", $_REQUEST['id']);
76
+			$path = @pack("H*", $ids[1]) .'/'. @pack("H*", $ids[2]);
77
+		} else {
78
+			$path = $this->DecodeItemId($_REQUEST['id'], '_');
79
+		}
80
+
81
+		if (false === $path) {
82
+			return false;
83
+		}
84
+		$dir = pathinfo($path, PATHINFO_DIRNAME);;
85
+		$dirs = array();
86
+		while (!in_array($dir, array('.', '', '/'), true)) {
87
+			$dirs[] = $dir;
88
+			$dir = pathinfo($dir, PATHINFO_DIRNAME);
89
+		}
90
+		$param = array(
91
+			'additional' => explode(',', $_REQUEST['additional']),
92
+			'sharepath' => array_reverse($dirs),
93
+			'ignore' => explode(',', $_REQUEST['ignore'])
94
+		);
95
+
96
+		return $param;
97
+	}
98
+
99
+	private function CheckPath()
100
+	{
101
+		$param = $this->GetParam_CheckPath();
102
+		if (!$param) {
103
+			$this->SetError(WEBAPI_ERR_BAD_REQUEST);
104
+			goto End;
105
+		}
106
+
107
+		$allowComment = AlbumAPIUtil::IsAlbumCommentalbGlobal();
108
+		$showAlbumHit = AlbumAPIUtil::IsShowAlbumHit();
109
+		$needThumbSize = in_array('thumb_size', $param['additional']);
110
+
111
+		$previous = false;
112
+		$items = array();
113
+		$formedItem = array();
114
+		$last_token = end($param['validToken']);
115
+
116
+		foreach ($param['validToken'] as $token => $value) {
117
+			$type = $value['type'];
118
+			$id = $value['id'];
119
+			list($idProcessFunc, $GetObjFunc, $permissionFunc, $hasItemFunc, $formula) = $this->validIdPrefix[$type];
120
+			if (!$GetObjFunc) {
121
+				continue;
122
+			}
123
+			$id = $this->$idProcessFunc($id);
124
+			if (in_array($type, array('photo', 'video'))) {
125
+				$objs = call_user_func($GetObjFunc, array($id['dbPath']));
126
+				$item = $objs[$id['dbPath']];
127
+			} else {
128
+				$objs = call_user_func($GetObjFunc, array($id));
129
+				$item = $objs[$id];
130
+			}
131
+
132
+			if (!$item) {
133
+				$this->SetError(array(PHOTOSTATION_PATH_ACCESS_DENY));
134
+				goto End;
135
+			}
136
+
137
+			$items[$token] = array(
138
+				'id' => $token,
139
+				'type' => $type,
140
+				'info' => $item
141
+			);
142
+
143
+			// check current permission
144
+			if ($permissionFunc) {
145
+				$code = $this->$permissionFunc($item);
146
+				if ($type === 'album') {
147
+					if ($code === WEBAPI_ERR_NONE && !$previousCode) {
148
+						$previousToken = $previousCode = null;
149
+					} else if ($last_token != $value && in_array($code, array(PHOTOSTATION_ALBUM_NO_ACCESS_RIGHT, PHOTOSTATION_ALBUM_PASSWORD_ERROR)) && !$previousCode) {
150
+						$previousCode = $code;
151
+						$previousToken = $token;
152
+
153
+					// password -> public / private
154
+					} else if ($code === PHOTOSTATION_ALBUM_NO_ACCESS_RIGHT && $previousCode === PHOTOSTATION_ALBUM_PASSWORD_ERROR) {
155
+						// password -> public
156
+						if ($item['public']) {
157
+							$this->SetError(array($previousCode, $previousToken));
158
+							goto End;
159
+
160
+						// password -> private
161
+						} else {
162
+							$this->SetError(array($code, $token));
163
+							goto End;
164
+						}
165
+
166
+					// private/password -> password (error)
167
+					} else if ($code === PHOTOSTATION_ALBUM_PASSWORD_ERROR &&
168
+						in_array($previousCode, array(PHOTOSTATION_ALBUM_NO_ACCESS_RIGHT, PHOTOSTATION_ALBUM_PASSWORD_ERROR))) {
169
+						$this->SetError(array($code, $token));
170
+						goto End;
171
+
172
+					// private/password -> password (passed)
173
+					} else if ($code == WEBAPI_ERR_NONE && $previousCode && $item['password']) {
174
+						$previousCode = $previousToken = null;
175
+
176
+					// private -> public
177
+					} else if ($code == WEBAPI_ERR_NONE && $previousCode && $this->NotPasswordAlbum($item)) {
178
+						$this->SetError(array($previousCode, $previousToken));
179
+						goto End;
180
+					} else if ($code !== WEBAPI_ERR_NONE) {
181
+						$this->SetError(array($code, $token));
182
+						goto End;
183
+					}
184
+				} else {
185
+					if ($previousCode) {
186
+						$this->SetError(array($previousCode, $previousToken));
187
+						goto End;
188
+					}
189
+					if ($code !== WEBAPI_ERR_NONE) {
190
+						$this->SetError(array($code, $token));
191
+						goto End;
192
+					}
193
+				}
194
+			}
195
+			
196
+			// check previous and current valid
197
+			if ($previous) {
198
+				$parentObj = $items[$previous];
199
+				$parentFunc = $this->validIdPrefix[$parentObj['type']];
200
+
201
+				// check previous has current
202
+				$hasItemFunc = $parentFunc[3];
203
+				if ($hasItemFunc && !$this->$hasItemFunc($parentObj['info'], $type, $id, $item)) {
204
+					$this->SetError(array(PHOTOSTATION_PATH_ACCESS_DENY));
205
+					goto End;
206
+				}
207
+			}
208
+			if ($formula) {
209
+				$formedItem[$token] = call_user_func($formula, $item, array(
210
+						'allowComment' => $allowComment,
211
+						'showAlbumHit' => $showAlbumHit,
212
+						'param'		   => $param,
213
+						'id'		   => $token,
214
+						'needThumbSize' => $needThumbSize
215
+					)
216
+				);
217
+			} else {
218
+				$formedItem[$token] = $items[$token];
219
+			}
220
+			$previous = $token;
221
+		}
222
+
223
+		$result = array();
224
+		foreach ($param['tokens'] as $token) {
225
+			if ($formedItem[$token]) {
226
+				$result[] = $formedItem[$token];
227
+			} else {
228
+				$result[] = array(
229
+					"id" => $token
230
+				);
231
+			}
232
+		}
233
+        $resp['total'] = count($result);
234
+		$resp['items'] = $result;
235
+        $this->SetResponse($resp);
236
+	End:
237
+		return;
238
+	}
239
+	private function ItemAccessible($obj)
240
+	{
241
+		$relativePath = substr($obj['path'], strlen(SYNOPHOTO_SERVICE_REAL_DIR_PATH));
242
+		$dirname = pathinfo($relativePath, PATHINFO_DIRNAME);
243
+		$sharename = '.' === $dirname ? '/' : $dirname;
244
+		$accessible = csSYNOPhotoMisc::CheckAlbumAccessible($sharename);
245
+		return $accessible ? WEBAPI_ERR_NONE : PHOTOSTATION_PHOTO_ACCESS_DENY;
246
+	}
247
+
248
+	private function AlbumAccessible($obj)
249
+	{
250
+		$sharename = $obj['sharename'];
251
+		$accessible = csSYNOPhotoMisc::CheckAlbumAccessible($sharename);
252
+		if ($obj['password'] && !$accessible) {
253
+			return PHOTOSTATION_ALBUM_PASSWORD_ERROR;
254
+		} else if (!$accessible) {
255
+			return PHOTOSTATION_ALBUM_NO_ACCESS_RIGHT;
256
+		}
257
+		return WEBAPI_ERR_NONE;
258
+	}
259
+
260
+	private function CategoryAccessible($obj)
261
+	{
262
+		if ($obj['hidden'] && !$this->isAdmin) {
263
+			return PHOTOSTATION_CATEGORY_ACCESS_DENY;
264
+		}
265
+		return WEBAPI_ERR_NONE;
266
+	}
267
+
268
+	private function SmartHasItem($parentObj, $itemType, $itemId, $itemObj)
269
+	{
270
+		$itemlist = SmartAlbum::GetSmartAlbumInstance()->GetItemsInSmartAlbum($parentObj, "filename", "asc", 0, -1);
271
+		foreach ($itemlist as $item) {
272
+			if ($item['id'] === $itemObj['id']) {
273
+				return true;
274
+			}
275
+		}
276
+		return false;
277
+	}
278
+
279
+	private function TagHasItem($parentObj, $itemType, $itemId, $itemObj)
280
+	{
281
+		if ('photo' === $itemType) {
282
+			$item = ItemLabel::GetByItemKeyLabelId($itemObj['id'], $parentObj['id'], true);
283
+		} else if ('video' === $itemType) {
284
+			$item = ItemLabel::GetByItemKeyLabelId($itemObj['path'], $parentObj['id'], false);
285
+		} else {
286
+			return false;
287
+		}
288
+
289
+		return $item ? true : false;
290
+	}
291
+
292
+	private function AlbumHasItem($parentObj, $itemType, $itemId, $itemObj)
293
+	{
294
+		if (in_array($itemType, array('photo', 'video'))) {
295
+			return $itemObj['shareid'] === $parentObj['shareid'];
296
+		}
297
+		return Album::HasItem($parentObj['sharename'], $itemObj['sharename']);
298
+	}
299
+
300
+	private function CategoryHasItem($parentObj, $itemType, $itemId, $itemObj)
301
+	{
302
+		if ('album' === $itemType) {
303
+			return Category::HasItem($parentObj['id'], $itemType, $itemObj['shareid']);
304
+		}
305
+		return Category::HasItem($parentObj['id'], $itemType, $itemId);
306
+	}
307
+
308
+	private function HasNoItem($parentObj, $itemType, $itemId, $itemObj)
309
+	{
310
+		return false;
311
+	}
312
+
313
+	private function GetParam_CheckPath()
314
+	{
315
+		$param = array();
316
+
317
+		if(!isset($_REQUEST['token'])) {
318
+			return false;
319
+		}
320
+
321
+		$tokens = split('/', $_REQUEST['token']);
322
+
323
+		$validTokens = array();
324
+		foreach($tokens as $token) {
325
+			$token = trim($token);
326
+			if (!$token) {
327
+				continue;
328
+			}
329
+
330
+			$parts = explode('_', $token, 2);
331
+			if (2 > count($parts) || !array_key_exists($parts[0], $this->validIdPrefix)) {
332
+				continue;
333
+			}
334
+			$validTokens[$token] = array(
335
+				'type' => $parts[0],
336
+				'id' => $parts[1]
337
+			);
338
+		}
339
+
340
+		$param = array(
341
+			'tokens' => $tokens,
342
+			'validToken' => $validTokens,
343
+			'ignore' => explode(',', $_REQUEST['ignore']),
344
+			'additional' => explode(',', $_REQUEST['additional'])
345
+		);
346
+
347
+		return $param;
348
+	}
349
+
350
+
351
+	private function GetId ($id) {
352
+		return $id;
353
+	}
354
+
355
+	private function GetName ($id) {
356
+		return @pack('H*', $id);
357
+	}
358
+
359
+	private function GetItemName ($id) {
360
+		$parts = split('_', $id);
361
+		$sharename = $this->GetName($parts[0]);
362
+		$filename = $this->GetName($parts[1]);
363
+		return array(
364
+			'sharename' => $sharename,
365
+			'filename'	=> $filename,
366
+			'dbPath' => SYNOPHOTO_SERVICE_REAL_DIR_PATH. ('/' === $sharename ? '': "$sharename/"). $filename
367
+		);
368
+	}
369
+
370
+	private function NotPasswordAlbum ($item) {
371
+		return !$item['password'];
372
+	}
373
+
374
+	protected function CheckPermission()
375
+	{
376
+		$this->isAdmin = isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']);
377
+		return true;
378
+	}
379
+}
380
+
381
+$api = new PathAPI();
382
+$api->Run();
383
+

+ 2
- 0
PhotoStation API/webapi/permission.conf.php 查看文件

@@ -0,0 +1,2 @@
1
+<?php
2
+	define('PERMISSION_CONF_WEBAPI_SERVICE_INCLUDE_PATH', dirname(__FILE__) . '/webapi.inc.php');

+ 7
- 0
PhotoStation API/webapi/permission.inc.php 查看文件

@@ -0,0 +1,7 @@
1
+<?php
2
+	require_once('permission.conf.php');
3
+	require_once("../include/syno_conf.php");
4
+
5
+	require_once(SYNOPHOTO_INCLUDE_ALBUM_UTIL);
6
+	require_once(PERMISSION_CONF_WEBAPI_SERVICE_INCLUDE_PATH);
7
+	require_once(SZ_WEBAPI_CLASS_PATH);

+ 872
- 0
PhotoStation API/webapi/permission.php 查看文件

@@ -0,0 +1,872 @@
1
+<?PHP
2
+
3
+require_once('permission.inc.php');
4
+
5
+class PermissionAPI extends WebAPI
6
+{
7
+	function __construct()
8
+	{
9
+		parent::__construct(SZ_WEBAPI_API_DESCRIPTION_PATH);
10
+	}
11
+
12
+	protected function Process()
13
+	{
14
+		/* if not admin, returns directly */
15
+		csSYNOPhotoMisc::CheckSessionTimeOut();
16
+
17
+		if (!strcasecmp($this->method, "getalbum")) {
18
+			$this->GetAlbum();
19
+		}
20
+		if (!strcasecmp($this->method, "editalbum")) {
21
+			$this->EditAlbum();
22
+		}
23
+		if (!strcasecmp($this->method, "editgroup")) {
24
+			$this->EditGroup();
25
+		}
26
+		if (!strcasecmp($this->method, "list_public_share")) {
27
+			$this->ListPublicShare();
28
+		}
29
+		if (!strcasecmp($this->method, "edit_public_share")) {
30
+			$this->EditPublicShare();
31
+		}
32
+	}
33
+
34
+	private function GetAlbum()
35
+	{
36
+		$ret = false;
37
+		$resp = array();
38
+
39
+		if (!isset($_REQUEST['type'])) {
40
+			$this->SetError(PHOTOSTATION_PERMISSION_BAD_PARAMS);
41
+			goto End;
42
+		}
43
+		/* set params */
44
+		if (!isset($_REQUEST['id']) || '' === $_REQUEST['id'] || 'album_' === $_REQUEST['id']) {
45
+			$albumName = '';
46
+		} else {
47
+			$id_arr = explode('_', $_REQUEST['id']);
48
+			if ('album' == $id_arr[0] && 2 === count($id_arr)) {
49
+				$albumName = @pack('H*', $id_arr[1]);
50
+			} else {
51
+				$this->SetError(PHOTOSTATION_PERMISSION_BAD_PARAMS);
52
+				goto End;
53
+			}
54
+		}
55
+
56
+		$path = SYNOPHOTO_SERVICE_REAL_DIR . "/" . ("/" === $albumName ? "" : $albumName);
57
+
58
+		if (!csSynoPhotoMisc::CheckPathValid($path)) {
59
+			$this->SetError(PHOTOSTATION_PERMISSION_BAD_PARAMS);
60
+			goto End;
61
+		}
62
+
63
+		if (!file_exists($path)) {
64
+			$this->SetError(PHOTOSTATION_PERMISSION_BAD_PARAMS);
65
+			goto End;
66
+		}
67
+
68
+		$start = isset($_REQUEST['offset']) ? $_REQUEST['offset'] : 0;
69
+		$limit = isset($_REQUEST['limit']) ? $_REQUEST['limit'] : 15;
70
+		$search = isset($_REQUEST['query']) ? $_REQUEST['query'] : '';
71
+
72
+		$needUsr = strstr($_REQUEST['type'], 'user_permission') ? true : false;
73
+		$needGrp = strstr($_REQUEST['type'], 'group_permission') ? true : false;
74
+		if (!$needUsr && !$needGrp) {
75
+			$this->SetError(PHOTOSTATION_PERMISSION_BAD_PARAMS);
76
+			goto End;
77
+		}
78
+
79
+		if ($needUsr) {
80
+			$resp['user_permission'] = array();
81
+			if (false === ($data = $this->GetAlbumUserData($albumName, $start, $limit, $search))) {
82
+				$this->SetError(PHOTOSTATION_PERMISSION_BAD_PARAMS);
83
+				goto End;
84
+			}
85
+			$users = $data['all'];
86
+			foreach ($users as $row) {
87
+				$user = null;
88
+				$user['id'] = $row['userid'];
89
+				$user['name'] = $row['username'];
90
+				$user['description'] = $row['description'];
91
+				$user['disabled'] = $row['disabled'];
92
+
93
+				$pObj = null;
94
+				$pObj['browse'] = $row['browse'];
95
+				$pObj['upload'] = $row['upload'];
96
+				$pObj['manage'] = $row['manage'];
97
+
98
+				$user['permission'] = $pObj;
99
+				$resp['user_permission'][] = $user;
100
+			}
101
+			$resp['total_user_count'] = $data['totalCount'];
102
+		}
103
+
104
+		if ($needGrp) {
105
+			$resp['group_permission'] = array();
106
+			if (false === ($data = $this->GetAlbumGroupData($albumName, $start, $limit, $search))) {
107
+				$this->SetError(PHOTOSTATION_PERMISSION_BAD_PARAMS);
108
+				goto End;
109
+			}
110
+			$groups = $data['all'];
111
+			foreach ($groups as $row) {
112
+				$group = null;
113
+				$group['id'] = $row['groupid'];
114
+				$group['name'] = $row['groupname'];
115
+				$group['description'] = $row['description'];
116
+				$group['disabled'] = $row['disabled'];
117
+
118
+				$pObj = null;
119
+				$pObj['browse'] = $row['browse'];
120
+				$pObj['upload'] = $row['upload'];
121
+				$pObj['manage'] = $row['manage'];
122
+
123
+				$group['permission'] = $pObj;
124
+				$resp['group_permission'][] = $group;
125
+			}
126
+			$resp['total_group_count'] = $data['totalCount'];
127
+		}
128
+
129
+		$this->SetResponse($resp);
130
+
131
+		$ret = true;
132
+	End:
133
+		return $ret;
134
+	}
135
+
136
+	private function EditAlbum()
137
+	{
138
+		$ret = false;
139
+
140
+		/* set params */
141
+		if (!isset($_REQUEST['id']) || '' === $_REQUEST['id'] || 'album_' === $_REQUEST['id']) {
142
+			$albumName = '/';
143
+		} else {
144
+			$id_arr = explode('_', $_REQUEST['id']);
145
+			if ('album' == $id_arr[0] && 2 === count($id_arr)) {
146
+				$albumName = @pack('H*', $id_arr[1]);
147
+			} else {
148
+				$this->SetError(PHOTOSTATION_PERMISSION_BAD_PARAMS);
149
+				goto End;
150
+			}
151
+		}
152
+
153
+		/* set user modified list */
154
+		$userBrowseDelete = isset($_REQUEST['user_browse_delete']) ? $_REQUEST['user_browse_delete'] : '';
155
+		$userUploadDelete = isset($_REQUEST['user_upload_delete']) ? $_REQUEST['user_upload_delete'] : '';
156
+		$userManageDelete = isset($_REQUEST['user_manage_delete']) ? $_REQUEST['user_manage_delete'] : '';
157
+		$userBrowseAdd = isset($_REQUEST['user_browse_add']) ? $_REQUEST['user_browse_add'] : '';
158
+		$userUploadAdd = isset($_REQUEST['user_upload_add']) ? $_REQUEST['user_upload_add'] : '';
159
+		$userManageAdd = isset($_REQUEST['user_manage_add']) ? $_REQUEST['user_manage_add'] : '';
160
+		/* set group modified list */
161
+		$groupBrowseDelete = isset($_REQUEST['group_browse_delete']) ? $_REQUEST['group_browse_delete'] : '';
162
+		$groupUploadDelete = isset($_REQUEST['group_upload_delete']) ? $_REQUEST['group_upload_delete'] : '';
163
+		$groupManageDelete = isset($_REQUEST['group_manage_delete']) ? $_REQUEST['group_manage_delete'] : '';
164
+		$groupBrowseAdd = isset($_REQUEST['group_browse_add']) ? $_REQUEST['group_browse_add'] : '';
165
+		$groupUploadAdd = isset($_REQUEST['group_upload_add']) ? $_REQUEST['group_upload_add'] : '';
166
+		$groupManageAdd = isset($_REQUEST['group_manage_add']) ? $_REQUEST['group_manage_add'] : '';
167
+
168
+		/* get the album info first */
169
+		$path = SYNOPHOTO_SERVICE_REAL_DIR . "/" . $albumName;
170
+
171
+		if (!csSynoPhotoMisc::CheckPathValid($path)) {
172
+			$this->SetError(PHOTOSTATION_PERMISSION_BAD_PARAMS);
173
+			goto End;
174
+		}
175
+
176
+		if (!file_exists($path)) {
177
+			$this->SetError(PHOTOSTATION_PERMISSION_BAD_PARAMS);
178
+			goto End;
179
+		}
180
+		$query = "SELECT shareid, sharename, public, password FROM photo_share WHERE sharename = ?";
181
+		$db_result = PHOTO_DB_Query($query, array($albumName));
182
+		if (false === ($row = PHOTO_DB_FetchRow($db_result))) {
183
+			$this->SetError(PHOTOSTATION_PERMISSION_BAD_PARAMS);
184
+			goto End;
185
+		}
186
+
187
+		/* get album type, 0 for public, 1 for private, 2 for password */
188
+		if (PHOTO_DB_IsTrue($row['public'])) {
189
+			$type = 0;
190
+		} elseif ('' === $row['password']) {
191
+			$type = 1;
192
+		} else {
193
+			$type = 2;
194
+		}
195
+		$shareid = $row['shareid'];
196
+		$sharename = $row['sharename'];
197
+
198
+		switch ($type) {
199
+			case 0:
200
+				/* update for user permission */
201
+				/* add/delete self upload right */
202
+				SYNOPHOTO_ADMIN_AddAccessRightByShareid($shareid, $userUploadAdd, PHOTO_UPLOAD_RIGHT_TABLE);
203
+				SYNOPHOTO_ADMIN_DeleteAccessRightByShareid($shareid, $userUploadDelete, PHOTO_UPLOAD_RIGHT_TABLE);
204
+				/* add/delete self manage right */
205
+				SYNOPHOTO_ADMIN_AddAccessRightByShareid($shareid, $userManageAdd, PHOTO_MANAGE_RIGHT_TABLE);
206
+				SYNOPHOTO_ADMIN_DeleteAccessRightByShareid($shareid, $userManageDelete, PHOTO_MANAGE_RIGHT_TABLE);
207
+				/* add child browse, upload, manage rights for manager */
208
+				SYNOPHOTO_ADMIN_AddChildRightBySharename($sharename, $userManageAdd, PHOTO_MANAGE_RIGHT_TABLE);
209
+				SYNOPHOTO_ADMIN_AddChildRightBySharename($sharename, $userManageAdd, PHOTO_UPLOAD_RIGHT_TABLE);
210
+				SYNOPHOTO_ADMIN_AddChildRightBySharename($sharename, $userManageAdd, PHOTO_ACCESS_RIGHT_TABLE);
211
+				/* delete parent's manage right */
212
+				SYNOPHOTO_ADMIN_DeleteParentRightBySharename($sharename, $userManageDelete, PHOTO_MANAGE_RIGHT_TABLE);
213
+
214
+				/* update for group permission */
215
+				$this->AddGroupPermission($shareid, $groupBrowseAdd, 1);
216
+				$this->AddGroupPermission($shareid, $groupUploadAdd, 2);
217
+				$this->AddGroupPermission($shareid, $groupManageAdd, 4);
218
+				$this->DeleteGroupPermission($shareid, $groupBrowseDelete, 1);
219
+				$this->DeleteGroupPermission($shareid, $groupUploadDelete, 2);
220
+				$this->DeleteGroupPermission($shareid, $groupManageDelete, 4);
221
+
222
+				$this->AddChildGroupRightBySharename($sharename, $groupManageAdd, 1);
223
+				$this->AddChildGroupRightBySharename($sharename, $groupManageAdd, 2);
224
+				$this->AddChildGroupRightBySharename($sharename, $groupManageAdd, 4);
225
+				$this->DeleteParentGroupRightBySharename($sharename, $groupManageDelete, 4);
226
+
227
+				break;
228
+			case 1:
229
+				/* add/delete self browse right */
230
+				SYNOPHOTO_ADMIN_AddAccessRightByShareid($shareid, $userBrowseAdd, PHOTO_ACCESS_RIGHT_TABLE);
231
+				SYNOPHOTO_ADMIN_DeleteAccessRightByShareid($shareid, $userBrowseDelete, PHOTO_ACCESS_RIGHT_TABLE);
232
+				/* add/delete self upload right */
233
+				SYNOPHOTO_ADMIN_AddAccessRightByShareid($shareid, $userUploadAdd, PHOTO_UPLOAD_RIGHT_TABLE);
234
+				SYNOPHOTO_ADMIN_DeleteAccessRightByShareid($shareid, $userUploadDelete, PHOTO_UPLOAD_RIGHT_TABLE);
235
+				/* add/delete self manage right */
236
+				SYNOPHOTO_ADMIN_AddAccessRightByShareid($shareid, $userManageAdd, PHOTO_MANAGE_RIGHT_TABLE);
237
+				SYNOPHOTO_ADMIN_DeleteAccessRightByShareid($shareid, $userManageDelete, PHOTO_MANAGE_RIGHT_TABLE);
238
+				/* add child browse, upload, manage rights for manager */
239
+				SYNOPHOTO_ADMIN_AddChildRightBySharename($sharename, $userManageAdd, PHOTO_MANAGE_RIGHT_TABLE);
240
+				SYNOPHOTO_ADMIN_AddChildRightBySharename($sharename, $userManageAdd, PHOTO_UPLOAD_RIGHT_TABLE);
241
+				SYNOPHOTO_ADMIN_AddChildRightBySharename($sharename, $userManageAdd, PHOTO_ACCESS_RIGHT_TABLE);
242
+				/* delete parent's manage right */
243
+				SYNOPHOTO_ADMIN_DeleteParentRightBySharename($sharename, $userManageDelete, PHOTO_MANAGE_RIGHT_TABLE);
244
+				/* delete child's browse right */
245
+				SYNOPHOTO_ADMIN_DeleteChildRightBySharename($sharename, $userBrowseDelete, PHOTO_ACCESS_RIGHT_TABLE);
246
+				SYNOPHOTO_ADMIN_DeleteChildRightBySharename($sharename, $userBrowseDelete, PHOTO_UPLOAD_RIGHT_TABLE);
247
+				SYNOPHOTO_ADMIN_DeleteChildRightBySharename($sharename, $userBrowseDelete, PHOTO_MANAGE_RIGHT_TABLE);
248
+
249
+				/* update for group permission */
250
+				$this->AddGroupPermission($shareid, $groupBrowseAdd, 1);
251
+				$this->AddGroupPermission($shareid, $groupUploadAdd, 2);
252
+				$this->AddGroupPermission($shareid, $groupManageAdd, 4);
253
+				$this->DeleteGroupPermission($shareid, $groupBrowseDelete, 1);
254
+				$this->DeleteGroupPermission($shareid, $groupUploadDelete, 2);
255
+				$this->DeleteGroupPermission($shareid, $groupManageDelete, 4);
256
+
257
+				$this->AddChildGroupRightBySharename($sharename, $groupManageAdd, 1);
258
+				$this->AddChildGroupRightBySharename($sharename, $groupManageAdd, 2);
259
+				$this->AddChildGroupRightBySharename($sharename, $groupManageAdd, 4);
260
+				$this->DeleteParentGroupRightBySharename($sharename, $groupManageDelete, 4);
261
+				$this->DeleteChildGroupRightBySharename($sharename, $groupBrowseDelete, 1);
262
+				$this->DeleteChildGroupRightBySharename($sharename, $groupBrowseDelete, 2);
263
+				$this->DeleteChildGroupRightBySharename($sharename, $groupBrowseDelete, 4);
264
+
265
+				break;
266
+			case 2:
267
+				/* remove all permissions if album is password */
268
+				$query = "Delete from " . PHOTO_ACCESS_RIGHT_TABLE . " where shareid = ".$shareid;
269
+				PHOTO_DB_Query($query);
270
+				$query = "Delete from " . PHOTO_UPLOAD_RIGHT_TABLE . " where shareid = ".$shareid;
271
+				PHOTO_DB_Query($query);
272
+				$query = "Delete from " . PHOTO_MANAGE_RIGHT_TABLE . " where shareid = ".$shareid;
273
+				PHOTO_DB_Query($query);
274
+
275
+				/* update for group permission */
276
+				$query = "DELETE FROM " . PHOTO_GROUP_PERMISSION_TABLE . " WHERE shareid = " . $shareid;
277
+				PHOTO_DB_Query($query);
278
+
279
+				break;
280
+		}
281
+
282
+		$ret = true;
283
+	End:
284
+		return $ret;
285
+	}
286
+
287
+	private function EditGroup()
288
+	{
289
+		$ret = false;
290
+
291
+		if (!isset($_REQUEST['id'])) {
292
+			$this->SetError(PHOTOSTATION_PERMISSION_BAD_PARAMS);
293
+			goto End;
294
+		}
295
+		$gid = $_REQUEST['id'];
296
+
297
+		/* set album modified list */
298
+		$albumBrowseDelete = isset($_REQUEST['album_browse_delete']) ? $_REQUEST['album_browse_delete'] : '';
299
+		$albumUploadDelete = isset($_REQUEST['album_upload_delete']) ? $_REQUEST['album_upload_delete'] : '';
300
+		$albumManageDelete = isset($_REQUEST['album_manage_delete']) ? $_REQUEST['album_manage_delete'] : '';
301
+		$albumBrowseAdd = isset($_REQUEST['album_browse_add']) ? $_REQUEST['album_browse_add'] : '';
302
+		$albumUploadAdd = isset($_REQUEST['album_upload_add']) ? $_REQUEST['album_upload_add'] : '';
303
+		$albumManageAdd = isset($_REQUEST['album_manage_add']) ? $_REQUEST['album_manage_add'] : '';
304
+
305
+		$this->AddAlbumPermission($gid, $albumBrowseAdd, 1);
306
+		$this->AddAlbumPermission($gid, $albumUploadAdd, 2);
307
+		$this->AddAlbumPermission($gid, $albumManageAdd, 4);
308
+		$this->DeleteAlbumPermission($gid, $albumManageDelete, 4);
309
+		$this->DeleteAlbumPermission($gid, $albumUploadDelete, 2);
310
+		$this->DeleteAlbumPermission($gid, $albumBrowseDelete, 1);
311
+
312
+		$ret = true;
313
+	End:
314
+		return $ret;
315
+	}
316
+
317
+	private function GetAlbumUserData($sharename, $start = 0, $limit = 15, $search = '')
318
+	{
319
+		/* edit sharename for root album */
320
+		if ('' === $sharename) {
321
+			$sharename = '/';
322
+		}
323
+		$query = "SELECT * FROM photo_share WHERE sharename = ?";
324
+		$db_result = PHOTO_DB_Query($query, array($sharename));
325
+		if ($row = PHOTO_DB_FetchRow($db_result)) {
326
+			$shareid = $row['shareid'];
327
+			$isPublic = ('t' == $row['public']);
328
+		} else {
329
+			return false;
330
+		}
331
+
332
+		$parentid = null;
333
+		/* find the nearest private parent */
334
+		if (false !== strstr($sharename, '/')) {
335
+			$parent = substr($sharename, 0, strpos($sharename, "/"));
336
+			$query = "SELECT * FROM photo_share WHERE sharename = ?";
337
+			$db_result = PHOTO_DB_Query($query, array($parent));
338
+			$row = PHOTO_DB_FetchRow($db_result);
339
+			if ('' == $row['password'] && PHOTO_DB_ConvertBool($row['public']) == 'f') {
340
+				// For parent albunm be privated
341
+				// Only users who have the permission of the parent album can be candidates of the second album.
342
+				// So we must filter them out
343
+				$parentid = $row['shareid'];
344
+			}
345
+		}
346
+
347
+		$users = $this->GetAllUsers($start, $limit, $search);
348
+		$result['totalCount'] = $users['totalCount'];
349
+		$result['all'] = array();
350
+
351
+		for ($i = $start; $i < $users['totalCount'] && $i < $start + $limit; $i++) {
352
+			$idx = $i - $start;
353
+			$result['all'][$idx]['disabled'] = 0;
354
+			if (null != $parentid) {
355
+				$query = "SELECT * FROM " . PHOTO_ACCESS_RIGHT_TABLE . " WHERE shareid = " . $parentid;
356
+				$query .= " AND userid = " . $users['all_users'][$idx]['userid'];
357
+				$db_result_parent = PHOTO_DB_Query($query);
358
+
359
+				// Only users who have the permission of the parent album can be candidates of the second album.
360
+				if (false === ($row_parent = PHOTO_DB_FetchRow($db_result_parent))) {
361
+					$result['all'][$idx]['disabled'] = 1;
362
+				}
363
+			}
364
+
365
+			$result['all'][$idx]['userid'] = $users['all_users'][$idx]['userid'];
366
+			$result['all'][$idx]['username'] = $users['all_users'][$idx]['username'];
367
+			$result['all'][$idx]['description'] = $users['all_users'][$idx]['description'];
368
+
369
+			/* check album access right */
370
+			$query = "SELECT * FROM " . PHOTO_ACCESS_RIGHT_TABLE . " WHERE shareid = " . $shareid;
371
+			$query .= " AND userid = " . $users['all_users'][$idx]['userid'];
372
+			$db_result = PHOTO_DB_Query($query);
373
+
374
+			$row = PHOTO_DB_FetchRow($db_result);
375
+			if (($row != null || $isPublic) && (0 == $result['all'][$idx]['disabled'])) {
376
+				$result['all'][$idx]['browse'] = 1;
377
+				$result['all'][$idx]['browse_orig'] = 1;
378
+			} else {
379
+				$result['all'][$idx]['browse'] = 0;
380
+				$result['all'][$idx]['browse_orig'] = 0;
381
+			}
382
+			/* check album upload right */
383
+			$query_2 = "Select * from " . PHOTO_UPLOAD_RIGHT_TABLE . " where shareid = " . $shareid;
384
+			$query_2 .= " and userid = " . $users['all_users'][$idx]['userid'];
385
+			$db_result_2 = PHOTO_DB_Query($query_2);
386
+
387
+			$row_2 = PHOTO_DB_FetchRow($db_result_2);
388
+			if ($row_2 != null) {
389
+				$result['all'][$idx]['upload'] = 1;
390
+				$result['all'][$idx]['upload_orig'] = 1;
391
+			} else {
392
+				$result['all'][$idx]['upload'] = 0;
393
+				$result['all'][$idx]['upload_orig'] = 0;
394
+			}
395
+			/* check album manage right */
396
+			$query_3 = "Select * from " . PHOTO_MANAGE_RIGHT_TABLE . " where shareid = " . $shareid;
397
+			$query_3 .= " and userid = " . $users['all_users'][$idx]['userid'];
398
+			$db_result_3 = PHOTO_DB_Query($query_3);
399
+
400
+			$row_3 = PHOTO_DB_FetchRow($db_result_3);
401
+			if ($row_3 != null) {
402
+				$result['all'][$idx]['manage'] = 1;
403
+				$result['all'][$idx]['manage_orig'] = 1;
404
+			} else {
405
+				$result['all'][$idx]['manage'] = 0;
406
+				$result['all'][$idx]['manage_orig'] = 0;
407
+			}
408
+		}
409
+		return $result;
410
+	}
411
+
412
+	private function GetAllUsers($start, $limit, $search)
413
+	{
414
+		if ($_SESSION[SYNOPHOTO_ADMIN_USER]['use_dsm_account']) {
415
+			return $this->GetAllDSMUser($start, $limit, $search);
416
+		}
417
+
418
+		$i = 0;
419
+		$limitOffsetString = PHOTO_DB_GetLimitOffsetString($limit, $start);
420
+		$query = "SELECT * FROM photo_user WHERE admin = 'f' AND username LIKE ? ORDER BY username ASC $limitOffsetString";
421
+		$db_result = PHOTO_DB_Query($query, array("%$search%"));
422
+		while ($row = PHOTO_DB_FetchRow($db_result)) {
423
+			$result['all_users'][$i]['userid'] = $row['userid'];
424
+			$result['all_users'][$i]['username'] = $row['username'];
425
+			$result['all_users'][$i]['description'] = $row['description'];
426
+			$i++;
427
+		}
428
+		$query = "SELECT count(*) FROM photo_user WHERE admin = 'f' AND username LIKE ?";
429
+		$db_result = PHOTO_DB_Query($query, array("%$search%"));
430
+		$row = PHOTO_DB_FetchRow($db_result);
431
+		$result['totalCount'] = $row[0];
432
+		return $result;
433
+	}
434
+
435
+	private function GetAllDSMUser($start, $limit, $query = '')
436
+	{
437
+		$command = "/usr/syno/bin/synophoto_dsm_user --enum " . escapeshellarg($query);
438
+		@exec($command, $pListUserCount, $retval);
439
+		if (0 > $retval) {
440
+			$result['totalCount'] = 0;
441
+			return json_encode($result);
442
+		}
443
+		$result['totalCount'] = $pListUserCount[0];
444
+
445
+		/* get the order of user info */
446
+		$dir = "ASC";
447
+
448
+		/* modify start value to skip admin and guest */
449
+		if (isset($pListUserCount[1])) {
450
+			$result['totalCount'] --;
451
+			if ($pListUserCount[1] < $start) {
452
+				$start += 1;
453
+			}
454
+		}
455
+		if (isset($pListUserCount[2])) {
456
+			$result['totalCount'] --;
457
+			if ($pListUserCount[2] < $start) {
458
+				$start += 1;
459
+			}
460
+		}
461
+		/* modify limit value accoring to start value */
462
+		if (isset($pListUserCount[1])) {
463
+			if ($start <= $pListUserCount[1] && $pListUserCount[1] < $start + $limit) {
464
+				$limit += 1;
465
+			}
466
+		}
467
+		if (isset($pListUserCount[2])) {
468
+			if ($start <= $pListUserCount[2] && $pListUserCount[2] < $start + $limit) {
469
+				$limit += 1;
470
+			}
471
+		}
472
+
473
+		$command = "/usr/syno/bin/synophoto_dsm_user --enum " . escapeshellarg($start) . " " . escapeshellarg($limit) . " " . $dir . ":" . escapeshellarg($query);
474
+		@exec($command, $pListUserName, $retval);
475
+
476
+		if (0 !== $retval) {
477
+			$result['totalCount'] = 0;
478
+			return json_encode($result);
479
+		}
480
+
481
+		$i = 0;
482
+		$result['all_users'] = array();
483
+		foreach ($pListUserName as $user_str) {
484
+			$user_info = split(',', $user_str);
485
+			if ('guest' == $user_info[1] || 'admin' == $user_info[1]) {
486
+				continue;
487
+			}
488
+
489
+			$result['all_users'][$i]['userid'] = $user_info[0];
490
+			$result['all_users'][$i]['username'] = $user_info[1];
491
+			$result['all_users'][$i]['description'] = $user_info[2];
492
+			$result['all_users'][$i]['disable'] = ($user_info[3] == 'true') ? "t" : "f";
493
+			$result['all_users'][$i]['admin'] = ($user_info[5] == 1) ? "t" : "f";
494
+
495
+			$i++;
496
+		}
497
+
498
+		return $result;
499
+	}
500
+
501
+	private function GetAlbumGroupData($sharename, $start = 0, $limit = 15, $search = '')
502
+	{
503
+		/* edit sharename for root album */
504
+		if ('' === $sharename) {
505
+			$sharename = '/';
506
+		}
507
+		$query = "SELECT * FROM photo_share WHERE sharename = ?";
508
+		$db_result = PHOTO_DB_Query($query, array($sharename));
509
+		if ($row = PHOTO_DB_FetchRow($db_result)) {
510
+			$shareid = $row['shareid'];
511
+			$isPublic = ('t' == $row['public']);
512
+		} else {
513
+			return false;
514
+		}
515
+
516
+		$parentid = null;
517
+		/* find the nearest private parent */
518
+		if (false !== strstr($sharename, '/')) {
519
+			$parent = substr($sharename, 0, strpos($sharename, "/"));
520
+			$query = "SELECT * FROM photo_share WHERE sharename = ?";
521
+			$db_result = PHOTO_DB_Query($query, array($parent));
522
+			$row = PHOTO_DB_FetchRow($db_result);
523
+			if ('' == $row['password'] && PHOTO_DB_ConvertBool($row['public']) == 'f') {
524
+				// For parent albunm be privated
525
+				// Only users who have the permission of the parent album can be candidates of the second album.
526
+				// So we must filter them out
527
+				$parentid = $row['shareid'];
528
+			}
529
+		}
530
+
531
+		$groups = $this->GetAllGroups($start, $limit, $search);
532
+		$result['totalCount'] = $groups['totalCount'];
533
+		$result['all'] = array();
534
+		for ($i = $start; $i < $groups['totalCount'] && $i < $start + $limit; $i ++) {
535
+			$idx = $i - $start;
536
+			$result['all'][$idx]['disabled'] = 0;
537
+			if (null != $parentid) {
538
+				$query = "SELECT * FROM " . PHOTO_GROUP_PERMISSION_TABLE . " WHERE shareid = " . $parentid;
539
+				$query .= " AND groupid = " . $groups['all_groups'][$idx]['groupid'];
540
+				$db_result_parent = PHOTO_DB_Query($query);
541
+
542
+				// Only groups which have the permission of the parent album can be candidates of the second album.
543
+				if (false === ($row_parent = PHOTO_DB_FetchRow($db_result_parent))) {
544
+					$result['all'][$idx]['disabled'] = 1;
545
+				}
546
+			}
547
+
548
+			$result['all'][$idx]['groupid'] = $groups['all_groups'][$idx]['groupid'];
549
+			$result['all'][$idx]['groupname'] = $groups['all_groups'][$idx]['groupname'];
550
+			$result['all'][$idx]['description'] = $groups['all_groups'][$idx]['description'];
551
+
552
+			$query = "SELECT * FROM " . PHOTO_GROUP_PERMISSION_TABLE . " WHERE shareid = " . $shareid;
553
+			$query .= " AND groupid = " . $groups['all_groups'][$idx]['groupid'];
554
+			$db_result = PHOTO_DB_Query($query);
555
+
556
+			$row = PHOTO_DB_FetchRow($db_result);
557
+			if ((($row != null && (1 & $row['permission'])) || $isPublic) && (0 == $result['all'][$idx]['disabled'])) {
558
+				$result['all'][$idx]['browse'] = 1;
559
+				$result['all'][$idx]['browse_orig'] = 1;
560
+			} else {
561
+				$result['all'][$idx]['browse'] = 0;
562
+				$result['all'][$idx]['browse_orig'] = 0;
563
+			}
564
+			if ($row != null && (2 & $row['permission'])) {
565
+				$result['all'][$idx]['upload'] = 1;
566
+				$result['all'][$idx]['upload_orig'] = 1;
567
+			} else {
568
+				$result['all'][$idx]['upload'] = 0;
569
+				$result['all'][$idx]['upload_orig'] = 0;
570
+			}
571
+			if ($row != null && (4 & $row['permission'])) {
572
+				$result['all'][$idx]['manage'] = 1;
573
+				$result['all'][$idx]['manage_orig'] = 1;
574
+			} else {
575
+				$result['all'][$idx]['manage'] = 0;
576
+				$result['all'][$idx]['manage_orig'] = 0;
577
+			}
578
+		}
579
+		return $result;
580
+	}
581
+
582
+	private function GetAllGroups($start, $limit, $search)
583
+	{
584
+		if ($_SESSION[SYNOPHOTO_ADMIN_USER]['use_dsm_account']) {
585
+			return $this->GetAllDSMGroup($start, $limit, $search);
586
+		}
587
+
588
+		$i = 0;
589
+		$limitOffsetString = PHOTO_DB_GetLimitOffsetString($limit, $start);
590
+		$query = "SELECT * FROM photo_group WHERE groupname LIKE ? ORDER BY groupname ASC $limitOffsetString";
591
+		$db_result = PHOTO_DB_Query($query, array("%$search%"));
592
+		while ($row = PHOTO_DB_FetchRow($db_result)) {
593
+			$result['all_groups'][$i]['groupid'] = $row['groupid'];
594
+			$result['all_groups'][$i]['groupname'] = $row['groupname'];
595
+			$result['all_groups'][$i]['description'] = $row['description'];
596
+			$i ++;
597
+		}
598
+		$query = "SELECT count(*) FROM photo_group WHERE groupname LIKE ?";
599
+		$db_result = PHOTO_DB_Query($query, array("%$search%"));
600
+		$row = PHOTO_DB_FetchRow($db_result);
601
+		$result['totalCount'] = $row[0];
602
+		return $result;
603
+	}
604
+
605
+	private function GetAllDSMGroup($start, $limit, $query = '')
606
+	{
607
+		$command = "/usr/syno/bin/synophoto_dsm_user --group " . escapeshellarg($query);
608
+		@exec($command, $pListGroupCount, $retval);
609
+		if (0 > $retval) {
610
+			$result['totalCount'] = 0;
611
+			return $result;
612
+		}
613
+		$result['totalCount'] = $pListGroupCount[0];
614
+
615
+		$command = "/usr/syno/bin/synophoto_dsm_user --group " . escapeshellarg($start) . " " . escapeshellarg($limit) . " ASC:" . escapeshellarg($query);
616
+		@exec($command, $pListGroupName, $retval);
617
+
618
+		if (0 !== $retval) {
619
+			$result['totalCount'] = 0;
620
+			return $result;
621
+		}
622
+
623
+		$i = 0;
624
+		$result['all_groups'] = array();
625
+		foreach ($pListGroupName as $group_str) {
626
+			$group_info = split(',', $group_str);
627
+			$result['all_groups'][$i]['groupid'] = $group_info[0];
628
+			$result['all_groups'][$i]['groupname'] = $group_info[1];
629
+			$result['all_groups'][$i]['description'] = $group_info[2];
630
+
631
+			$i ++;
632
+		}
633
+
634
+		return $result;
635
+	}
636
+
637
+	private function AddGroupPermission($shareid, $groups, $type)
638
+	{
639
+		if ($shareid == null || $shareid == "" || $groups == null || $groups == "") {
640
+			return;
641
+		}
642
+		$ids = explode(',', $groups);
643
+		foreach ($ids as $id) {
644
+			if ('' === $id) {
645
+				continue;
646
+			}
647
+			$query = "UPDATE " . PHOTO_GROUP_PERMISSION_TABLE . " SET permission = permission | $type WHERE shareid = " . $shareid . " AND groupid = " . $id;
648
+			$db_result = PHOTO_DB_Query($query);
649
+			if (false === ($row = PHOTO_DB_FetchRow($db_result))) {
650
+				$date = date('Y-m-d H:i:s');
651
+				$query = "INSERT INTO " . PHOTO_GROUP_PERMISSION_TABLE . " (groupid, shareid, permission, create_time) ";
652
+				$query = $query . "VALUES (" . $id . ", " . $shareid . ", " . $type . ", '" . $date . "')";
653
+				$db_result = PHOTO_DB_Query($query);
654
+			}
655
+		}
656
+	}
657
+
658
+	private function AddAlbumPermission($gid, $albums, $type)
659
+	{
660
+		if ($gid == null || $gid == "" || $albums == null || $albums == "") {
661
+			return;
662
+		}
663
+		$ids = explode(',', $albums);
664
+		foreach ($ids as $id) {
665
+			if ('' === $id) {
666
+				continue;
667
+			}
668
+			$query = "UPDATE " . PHOTO_GROUP_PERMISSION_TABLE . " SET permission = permission | $type WHERE shareid = " . $id . " AND groupid = " . $gid;
669
+			$db_result = PHOTO_DB_Query($query);
670
+			if (false === ($row = PHOTO_DB_FetchRow($db_result))) {
671
+				$date = date('Y-m-d H:i:s');
672
+				$query = "INSERT INTO " . PHOTO_GROUP_PERMISSION_TABLE . " (groupid, shareid, permission, create_time) ";
673
+				$query = $query . "VALUES (" . $gid . ", " . $id . ", " . $type . ", '" . $date . "')";
674
+				$db_result = PHOTO_DB_Query($query);
675
+			}
676
+		}
677
+	}
678
+
679
+	private function DeleteGroupPermission($shareid, $groups, $type)
680
+	{
681
+		if ($shareid == null || $shareid == "" || $groups == null || $groups == "") {
682
+			return;
683
+		}
684
+		$type = -1 - $type;
685
+		$ids = explode(',', $groups);
686
+		foreach ($ids as $id) {
687
+			if ('' === $id) {
688
+				continue;
689
+			}
690
+			$query = "UPDATE " . PHOTO_GROUP_PERMISSION_TABLE . " SET permission = permission & $type WHERE shareid = " . $shareid . " AND groupid = " . $id;
691
+			$db_result = PHOTO_DB_Query($query);
692
+		}
693
+		$query = "DELETE FROM " . PHOTO_GROUP_PERMISSION_TABLE . " WHERE permission = 0";
694
+		$db_result = PHOTO_DB_Query($query);
695
+	}
696
+
697
+	private function DeleteAlbumPermission($gid, $albums, $type)
698
+	{
699
+		if ($gid == null || $gid == "" || $albums == null || $albums == "") {
700
+			return;
701
+		}
702
+		$type = -1 - $type;
703
+		$ids = explode(',', $albums);
704
+		foreach ($ids as $id) {
705
+			if ('' === $id) {
706
+				continue;
707
+			}
708
+			$query = "UPDATE " . PHOTO_GROUP_PERMISSION_TABLE . " SET permission = permission & $type WHERE shareid = " . $id . " AND groupid = " . $gid;
709
+			$db_result = PHOTO_DB_Query($query);
710
+		}
711
+		$query = "DELETE FROM " . PHOTO_GROUP_PERMISSION_TABLE . " WHERE permission = 0";
712
+		$db_result = PHOTO_DB_Query($query);
713
+	}
714
+
715
+	private function AddChildGroupRightBySharename($sharename, $groups, $type)
716
+	{
717
+		if($sharename == null || $sharename == "" || $groups == null || $groups == "") {
718
+			return;
719
+		}
720
+		$queryParam = array();
721
+		if ("/" === $sharename) {
722
+			$query = "SELECT shareid FROM photo_share WHERE sharename <> '/' AND sharename NOT LIKE '%/%/%'";
723
+		} else {
724
+			$query = "SELECT shareid FROM photo_share WHERE sharename LIKE ? AND sharename NOT LIKE ?";
725
+			$queryParam = array("$sharename/%", "$sharename/%/%");
726
+		}
727
+		if ($table === PHOTO_ACCESS_RIGHT_TABLE) {
728
+			$query .= " AND public='f'";
729
+		}
730
+		$query .= " AND password=''";
731
+		$db_result = PHOTO_DB_Query($query, $queryParam);
732
+
733
+		while ($row = PHOTO_DB_FetchRow($db_result)) {
734
+			$shareid = $row['shareid'];
735
+			$this->AddGroupPermission($shareid, $groups, $type);
736
+		}
737
+	}
738
+
739
+	private function DeleteChildGroupRightBySharename($sharename, $groups, $type)
740
+	{
741
+		if($sharename == null || $sharename == "" || $groups == null || $groups == "") {
742
+			return;
743
+		}
744
+		$escape = PHOTO_DB_GetEscape();
745
+		$query = "SELECT shareid FROM photo_share WHERE sharename LIKE ?";
746
+		$db_result = PHOTO_DB_Query($query, array("$sharename/%"));
747
+		while ($row = PHOTO_DB_FetchRow($db_result)) {
748
+			$shareid = $row['shareid'];
749
+			$this->DeleteGroupPermission($shareid, $groups, $type);
750
+		}
751
+	}
752
+
753
+	private function DeleteParentGroupRightBySharename($sharename, $groups, $type)
754
+	{
755
+		if($sharename == null || $sharename == "" || $groups == null || $groups == "") {
756
+			return;
757
+		}
758
+		if ('/' === $sharename || '' === $sharename) {
759
+			return;
760
+		}
761
+		$parentName = substr($sharename, 0, strrpos($sharename, '/'));
762
+		if ('' === $parentName) {
763
+			$parentName = '/';
764
+		}
765
+		$escape = PHOTO_DB_GetEscape();
766
+		$query = "SELECT shareid FROM photo_share WHERE sharename=?";
767
+		$db_result = PHOTO_DB_Query($query, array($parentName));
768
+		if ($row = PHOTO_DB_FetchRow($db_result)) {
769
+			$shareid = $row['shareid'];
770
+			$this->DeleteGroupPermission($shareid, $groups, $type);
771
+		}
772
+		$this->DeleteParentGroupRightBySharename($parentName, $groups, $type);
773
+	}
774
+
775
+	private function ListPublicShare()
776
+	{
777
+		$ret = false;
778
+
779
+		if (!isset($_REQUEST['type']) || !isset($_REQUEST['offset']) || !isset($_REQUEST['limit'])) {
780
+			$this->SetError(PHOTOSTATION_PERMISSION_BAD_PARAMS);
781
+			goto End;
782
+		}
783
+		$type = $_REQUEST['type'];
784
+
785
+		// fetch list
786
+		if ($type === 'user') {
787
+			$list_result = $this->GetAllUsers($_REQUEST['offset'], $_REQUEST['limit'], "");
788
+		} else if ($type === 'group') {
789
+			$list_result = $this->GetAllGroups($_REQUEST['offset'], $_REQUEST['limit'], "");
790
+		} else {
791
+			$this->SetError(PHOTOSTATION_PERMISSION_BAD_PARAMS);
792
+			goto End;
793
+		}
794
+
795
+		// fetch all public_share permission
796
+		$table_name = "photo_" . ($type === 'group' ? 'group_' : '') . "public_share_right";
797
+		if ($_SESSION[SYNOPHOTO_ADMIN_USER]['use_dsm_account']) {
798
+			$table_name .= '_for_dsm_account';
799
+		}
800
+		$query = "SELECT * FROM $table_name";
801
+		$db_result = PHOTO_DB_Query($query);
802
+		$permission_list = array();
803
+		while ($row = PHOTO_DB_FetchRow($db_result)) {
804
+			$permission_list[$row[0]] = 1;
805
+		}
806
+
807
+		// combine list and permission
808
+		$result = array();
809
+		$result['total'] = $list_result['totalCount'];
810
+		$result['items'] = array();
811
+		$item['type'] = $type;
812
+		foreach($list_result['all_' .$type. 's'] as $list_item) {
813
+			$item['id'] = $list_item[$type . 'id'];
814
+			$item['name'] = $list_item[$type . 'name'];
815
+			$item['allow_public_share'] = isset($permission_list[$item['id']]);
816
+			$result['items'][] = $item;
817
+		}
818
+
819
+		$ret = true;
820
+		$this->SetResponse($result);
821
+	End:
822
+		return $ret;
823
+	}
824
+
825
+	private function EditPublicShare()
826
+	{
827
+		$table_name = "photo_public_share_right";
828
+		if ($_SESSION[SYNOPHOTO_ADMIN_USER]['use_dsm_account']) {
829
+			$table_name .= '_for_dsm_account';
830
+		}
831
+
832
+		if (isset($_REQUEST['enable_user'])) {
833
+			$this->EnablePublicShare($table_name, explode(",", $_REQUEST['enable_user']));
834
+		}
835
+
836
+		if (isset($_REQUEST['disable_user'])) {
837
+			$this->DisablePublicShare($table_name, explode(",", $_REQUEST['disable_user']), 'user');
838
+		}
839
+
840
+		$table_name = "photo_group_public_share_right";
841
+		if ($_SESSION[SYNOPHOTO_ADMIN_USER]['use_dsm_account']) {
842
+			$table_name .= '_for_dsm_account';
843
+		}
844
+
845
+		if (isset($_REQUEST['enable_group'])) {
846
+			$this->EnablePublicShare($table_name, explode(",", $_REQUEST['enable_group']));
847
+		}
848
+
849
+		if (isset($_REQUEST['disable_group'])) {
850
+			$this->DisablePublicShare($table_name, explode(",", $_REQUEST['disable_group']), 'group');
851
+		}
852
+	}
853
+
854
+	private function EnablePublicShare($table, $ids)
855
+	{
856
+		$query = "INSERT INTO $table VALUES (?)";
857
+		foreach ($ids as $id) {
858
+			PHOTO_DB_Query($query, array($id));
859
+		}
860
+	}
861
+
862
+	private function DisablePublicShare($table, $ids, $type)
863
+	{
864
+		$query = "DELETE FROM $table WHERE " . $type ."id = (?)";
865
+		foreach ($ids as $id) {
866
+			PHOTO_DB_Query($query, array($id));
867
+		}
868
+	}
869
+}
870
+
871
+$api = new PermissionAPI();
872
+$api->Run();

+ 3
- 0
PhotoStation API/webapi/photo.conf.php 查看文件

@@ -0,0 +1,3 @@
1
+<?php
2
+	define('PHOTO_CONF_WEBAPI_SERVICE_INCLUDE_PATH', dirname(__FILE__) . '/webapi.inc.php');
3
+	$SYNOPHOTO_ALLOW_SORT_TYPE = array('filename', 'takendate', 'createdate');

+ 8
- 0
PhotoStation API/webapi/photo.inc.php 查看文件

@@ -0,0 +1,8 @@
1
+<?php
2
+	require_once('photo.conf.php');
3
+	require_once("../include/syno_conf.php");
4
+	require_once(SYNOPHOTO_INCLUDE_ALBUM_UTIL);
5
+	require_once(SYNOPHOTO_INCLUDE_GPS_UTIL);
6
+
7
+	require_once(PHOTO_CONF_WEBAPI_SERVICE_INCLUDE_PATH);
8
+	require_once(SZ_WEBAPI_CLASS_PATH);

+ 1187
- 0
PhotoStation API/webapi/photo.php
文件差異過大導致無法顯示
查看文件


+ 3
- 0
PhotoStation API/webapi/photo_tag.conf.php 查看文件

@@ -0,0 +1,3 @@
1
+<?php
2
+    define('PHOTO_TAG_CONF_WEBAPI_SERVICE_INCLUDE_PATH', dirname(__FILE__).'/webapi.inc.php');
3
+?>

+ 9
- 0
PhotoStation API/webapi/photo_tag.inc.php 查看文件

@@ -0,0 +1,9 @@
1
+<?php
2
+    require_once('photo_tag.conf.php');
3
+    require_once("../include/syno_conf.php");
4
+    require_once("../include/label.php");
5
+    require_once("../include/item_label.php");
6
+
7
+    require_once(PHOTO_TAG_CONF_WEBAPI_SERVICE_INCLUDE_PATH);
8
+    require_once(SZ_WEBAPI_CLASS_PATH);
9
+?>

+ 644
- 0
PhotoStation API/webapi/photo_tag.php 查看文件

@@ -0,0 +1,644 @@
1
+<?php
2
+
3
+set_time_limit(0);
4
+
5
+require_once('photo_tag.inc.php');
6
+
7
+class PhotoTagAPI extends WebAPI {
8
+    function __construct() {
9
+        parent::__construct(SZ_WEBAPI_API_DESCRIPTION_PATH);
10
+    }
11
+
12
+    protected function Process() {
13
+        if (!strcasecmp($this->method, "list")) {
14
+            csSYNOPhotoDB::GetDBInstance()->SetSessionCache();
15
+            $this->PhotoTagList();
16
+        } else {
17
+            csSYNOPhotoDB::GetDBInstance()->SetSessionCache(true);
18
+            csSYNOPhotoMisc::CheckSessionTimeOut(true);
19
+
20
+            if (!strcasecmp($this->method, "people_tag")) {
21
+                $this->AddPeopleTag();
22
+            } elseif (!strcasecmp($this->method, "geo_tag")) {
23
+                $this->AddGeoDescTag(1);
24
+            } elseif (!strcasecmp($this->method, "desc_tag")) {
25
+                $this->AddGeoDescTag(2);
26
+            } elseif (!strcasecmp($this->method, "delete")) {
27
+                $this->Delete();
28
+            } elseif (!strcasecmp($this->method, "people_tag_confirm")) {
29
+                $this->PeopleTagConfirm();
30
+            }
31
+        }
32
+    }
33
+
34
+    /**
35
+     *  @params - $id like: photo_dir_name, video_dir_name
36
+     */
37
+    private function GetItemInfo($id)
38
+    {
39
+        $arr = explode('_', $id);
40
+        if (3 !== count($arr)) {
41
+            return false;
42
+        }
43
+        if (!in_array($arr[0], array('photo', 'video'))) {
44
+            return false;
45
+        }
46
+        // get id
47
+        $dirName = @pack('H*', $arr[1]);
48
+        $fileName = @pack('H*', $arr[2]);
49
+        $filePath = $dirName === '/' ? $fileName : $dirName.'/'.$fileName;
50
+        $realPath = SYNOPHOTO_SERVICE_REAL_DIR.'/'.$filePath;
51
+		if (!csSYNOPhotoMisc::CheckPathValid($realPath)) {
52
+            return false;
53
+        }
54
+        $table = ('photo' === $arr[0]) ? 'photo_image' : 'video';
55
+        if ('root' === SYNOPHOTO_ADMIN_USER) {
56
+            $row = csSYNOPhotoDB::GetDBInstance()->GetFieldByKeyValue('id', $table, 'path', $realPath);
57
+        } else {
58
+            $row = csSYNOPhotoDB::GetDBInstance()->GetFieldByKeyValue('id', $table, 'path', $filePath);
59
+        }
60
+        $data['id'] = $row['id'];
61
+		$data['path'] = $realPath;
62
+		$data['dbPath'] = SYNOPHOTO_SERVICE_REAL_DIR_PATH.$filePath;
63
+        $data['sharePath'] = $filePath;
64
+        $data['isPhoto'] = ('photo' === $arr[0]) ? true : false;
65
+
66
+        return $data;
67
+    }
68
+
69
+    private function GetParams_PeopleTagConfirm()
70
+    {
71
+        if (!isset($_REQUEST['id']) || !isset($_REQUEST['tag_id']) || !isset($_REQUEST['confirm'])) {
72
+            return false;
73
+        }
74
+        if (!in_array($_REQUEST['confirm'], array('true', 'false'))) {
75
+            return false;
76
+        }
77
+        $params['confirm'] = $_REQUEST['confirm'];
78
+        if (false === ($data = $this->GetItemInfo($_REQUEST['id']))) {
79
+            return false;
80
+        }
81
+        if (false === $data['isPhoto']) {
82
+            return false;
83
+        }
84
+        $params['id'] = $data['id'];
85
+        $params['sharePath'] = $data['sharePath'];
86
+        $params['isPhoto'] = $data['isPhoto'];
87
+        $arr = explode('tag_', $_REQUEST['tag_id']);
88
+        if (2 !== count($arr)) {
89
+            return false;
90
+        }
91
+        $params['tag_id'] = (int)$arr[1];
92
+        return $params;
93
+    }
94
+
95
+    private function GetParams_PeopleTag()
96
+    {
97
+        if (!isset($_REQUEST['id']) || !isset($_REQUEST['tag_id']) ||
98
+            !isset($_REQUEST['x']) || !isset($_REQUEST['y']) ||
99
+            !isset($_REQUEST['width']) || !isset($_REQUEST['height'])) {
100
+            return false;
101
+        }
102
+        if (!is_numeric($_REQUEST['x']) || !is_numeric ($_REQUEST['y']) || !is_numeric ($_REQUEST['width']) || !is_numeric($_REQUEST['height'])) {
103
+            return false;
104
+        }
105
+        $params['x'] = floatval($_REQUEST['x']);
106
+        $params['y'] = floatval($_REQUEST['y']);
107
+        $params['width'] = floatval($_REQUEST['width']);
108
+        $params['height'] = floatval($_REQUEST['height']);
109
+
110
+        if (1 < $params['x'] || 0 > $params['x'] ||
111
+            1 < $params['y'] || 0 > $params['y'] ||
112
+            1 < $params['width'] || 0 >= $params['width'] ||
113
+            1 < $params['height'] || 0 >= $params['height']) {
114
+            return false;
115
+        }
116
+
117
+        if (false === ($data = $this->GetItemInfo($_REQUEST['id']))) {
118
+            return false;
119
+        }
120
+        $params['id'] = $data['id'];
121
+        $params['path'] = $data['path'];
122
+        $params['sharePath'] = $data['sharePath'];
123
+        if (false === $data['isPhoto']) {
124
+            return false;
125
+        }
126
+        $params['isPhoto'] = $data['isPhoto'];
127
+
128
+        $arr = explode('tag_', $_REQUEST['tag_id']);
129
+        if (2 !== count($arr)) {
130
+            return false;
131
+        }
132
+        $params['tag_id'] = (int) $arr[1];
133
+        return $params;
134
+    }
135
+
136
+    private function GetParams_Tag()
137
+    {
138
+        if (!isset($_REQUEST['id']) || !isset($_REQUEST['tag_id'])) {
139
+            return false;
140
+        }
141
+        $params['ids'] = array();
142
+        $ids = explode(',', $_REQUEST['id']);
143
+        foreach ($ids as $id) {
144
+            if (false === ($data = $this->GetItemInfo($id))) {
145
+                return false;
146
+            }
147
+            $info['id'] = $data['id'];
148
+            $info['path'] = $data['path'];
149
+            $info['dbPath'] = $data['dbPath'];
150
+            $info['sharePath'] = $data['sharePath'];
151
+            $info['isPhoto'] = $data['isPhoto'];
152
+            array_push($params['ids'], $info);
153
+        }
154
+
155
+        // get tag id
156
+        $params['tag_ids'] = array();
157
+        $arr = explode(',', $_REQUEST['tag_id']);
158
+        foreach ($arr as $tagID) {
159
+            $split = explode('tag_', $tagID);
160
+            if (2 !== count($split)) {
161
+                return false;
162
+            }
163
+            array_push($params['tag_ids'], $split[1]);
164
+        }
165
+        return $params;
166
+    }
167
+
168
+	private function GetParams_Delete()
169
+	{
170
+		if (!isset($_REQUEST['id']) ||
171
+			(!isset($_REQUEST['tag_id']) && !isset($_REQUEST['item_tag_id'])) ||
172
+			(isset($_REQUEST['tag_id']) && isset($_REQUEST['item_tag_id']))) {
173
+			return false;
174
+		}
175
+		$params['ids'] = array();
176
+		$ids = explode(',', $_REQUEST['id']);
177
+		foreach ($ids as $id) {
178
+			if (false === ($data = $this->GetItemInfo($id))) {
179
+				return false;
180
+			}
181
+			$info['id'] = $data['id'];
182
+			$info['path'] = $data['path'];
183
+			$info['sharePath'] = $data['sharePath'];
184
+			$info['dbPath'] = $data['dbPath'];
185
+			$info['isPhoto'] = $data['isPhoto'];
186
+			array_push($params['ids'], $info);
187
+		}
188
+
189
+		// get tag id
190
+		$params['tag_ids'] = array();
191
+		if (isset($_REQUEST['tag_id'])) {
192
+			$arr = explode(',', $_REQUEST['tag_id']);
193
+			foreach ($arr as $tagID) {
194
+				$split = explode('tag_', $tagID);
195
+				if (2 !== count($split)) {
196
+					return false;
197
+				}
198
+				array_push($params['tag_ids'], $split[1]);
199
+			}
200
+		}
201
+		// get item_tag_id
202
+		$params['item_tag_ids'] = array();
203
+		if (isset($_REQUEST['item_tag_id'])) {
204
+			$arr = explode(',', $_REQUEST['item_tag_id']);
205
+			foreach ($arr as $photoTagID) {
206
+				$split = explode('item_tag_', $photoTagID);
207
+				if (2 !== count($split)) {
208
+					return false;
209
+				}
210
+				array_push($params['item_tag_ids'], $split[1]);
211
+			}
212
+		}
213
+        return $params;
214
+	}
215
+
216
+    private function GetParams_List($id, $type)
217
+    {
218
+        if (false === ($data = $this->GetItemInfo($id))) {
219
+            return false;
220
+        }
221
+        $params['id'] = $data['id'];
222
+        $params['path'] = $data['path'];
223
+        $params['isPhoto'] = $data['isPhoto'];
224
+
225
+        $params['type'] = explode(',', $type);
226
+        foreach ($params['type'] as $type) {
227
+            if (!in_array($type, array('people', 'geo', 'desc'))) {
228
+                return false;
229
+            }
230
+        }
231
+        $params['additional'] = explode(',', $_REQUEST['additional']);
232
+        return $params;
233
+    }
234
+
235
+    private function CheckVideoExist($videoPath)
236
+    {
237
+        $query = 'SELECT count(id) FROM video WHERE path=?';
238
+        $sqlParam = array($videoPath);
239
+        if (false === ($db_result = PHOTO_DB_Query($query, $sqlParam))) {
240
+            return false;
241
+        }
242
+       	if (false === ($row = PHOTO_DB_FetchRow($db_result))) {
243
+            return false;
244
+        }
245
+        if (1 !== (int)$row[0]) {
246
+            return false;
247
+        }
248
+        return true;
249
+    }
250
+
251
+    private function GetPhotoPeopleTagInfo($data)
252
+    {
253
+        if (!empty($data['info'])) {
254
+            $info = json_decode($data['info'], true);
255
+            $people['x'] = $info['x'];
256
+            $people['y'] = $info['y'];
257
+            $people['width'] = $info['width'];
258
+            $people['height'] = $info['height'];
259
+        }
260
+        return $people;
261
+    }
262
+
263
+    private function FormAdditional($data, $params, $type)
264
+    {
265
+        if (in_array('info', $params['additional'])) {
266
+            if ('people' === $type) {
267
+                $people = $this->GetPhotoPeopleTagInfo($data);
268
+                $people['name'] = $data['name'];
269
+                $people['is_confirmed'] = $data['status'];
270
+                $additional['info'] = $people;
271
+            } elseif ('geo' === $type) {
272
+                $geo['name'] = $data['name'];
273
+                $info = json_decode($data['label_info'], true);
274
+                $geo['lat'] = $info['lat'];
275
+                $geo['lng'] = $info['lng'];
276
+		$geo['place_id'] = $info['placeId'];
277
+                $geo['address'] = (null === $info['address']) ? '' : $info['address'];
278
+                $additional['info'] = $geo;
279
+            } elseif ('desc' === $type) {
280
+                $desc['name'] = $data['name'];
281
+                $additional['info'] = $desc;
282
+            }
283
+        }
284
+        if (in_array('extra', $params['additional'])) {
285
+            if ('people' === $type) {
286
+                $additional['extra'] = $data['info'];
287
+            } elseif ('geo' === $type) {
288
+                $additional['extra'] = $data['label_info'];
289
+            } elseif ('desc' === $type) {
290
+                $additional['extra'] = $data['info'];
291
+            }
292
+        }
293
+        if (in_array('count', $params['additional'])) {
294
+            $total = ItemLabel::GetCountByLabelId($data['label_id']);
295
+            $additional['count'] = (int)$total;
296
+        }
297
+        return $additional;
298
+    }
299
+
300
+    private function FromTags($result, $params)
301
+    {
302
+        $tags = array();
303
+        if (in_array('people', $params['type'])) {
304
+            foreach ($result['person'] as $people) {
305
+                unset($tag);
306
+                $tag['id'] = 'tag_'.$people['label_id'];
307
+				$tag['item_tag_id'] = 'item_tag_'.$people['id'];
308
+                $tag['type'] = 'people';
309
+                $tag['name'] = $people['name'];
310
+                $additional = $this->FormAdditional($people, $params, 'people');
311
+                if (null !== $additional) {
312
+                    $tag['additional'] = $additional;
313
+                }
314
+                array_push($tags, $tag);
315
+            }
316
+        }
317
+
318
+        if (in_array('geo', $params['type'])) {
319
+            foreach ($result['place'] as $geo) {
320
+                unset($tag);
321
+                $tag['id'] = 'tag_'.$geo['label_id'];
322
+				$tag['item_tag_id'] = 'item_tag_'.$geo['id'];
323
+                $tag['type'] = 'geo';
324
+                $tag['name'] = $geo['name'];
325
+                $additional = $this->FormAdditional($geo, $params, 'geo');
326
+                if (null !== $additional) {
327
+                    $tag['additional'] = $additional;
328
+                }
329
+                array_push($tags, $tag);
330
+            }
331
+        }
332
+        if (in_array('desc', $params['type'])) {
333
+            foreach ($result['general'] as $desc) {
334
+                unset($tag);
335
+                $tag['id'] = 'tag_'.$desc['label_id'];
336
+				$tag['item_tag_id'] = 'item_tag_'.$desc['id'];
337
+                $tag['type'] = 'desc';
338
+                $tag['name'] = $desc['name'];
339
+                $additional = $this->FormAdditional($desc, $params, 'desc');
340
+                if (null !== $additional) {
341
+                    $tag['additional'] = $additional;
342
+                }
343
+                array_push($tags, $tag);
344
+            }
345
+        }
346
+        return $tags;
347
+    }
348
+
349
+    private function PhotoTagList()
350
+    {
351
+	$tags = array();
352
+	if (!isset($_REQUEST['id']) || !isset($_REQUEST['type'])) {
353
+		$this->SetError(WEBAPI_ERR_BAD_REQUEST);
354
+		goto End;
355
+	}
356
+
357
+        $ids = explode(',', $_REQUEST['id']);
358
+        foreach ($ids as $id) {
359
+		if (false === ($params = $this->GetParams_List($id, $_REQUEST['type']))) {
360
+		    $this->SetError(WEBAPI_ERR_BAD_REQUEST);
361
+		    goto End;
362
+		}
363
+		if (count($ids) > 1) {
364
+			$tag = array();
365
+			$tag['photoId'] = $id;
366
+			$tag['tags'] = $this->GetPhotoTagList($params);
367
+		} else {
368
+			$tag = $this->GetPhotoTagList($params);
369
+		}
370
+		array_push($tags, $tag);
371
+	}
372
+
373
+	if (count($ids) > 1) {
374
+		$resp['tags'] = $tags;
375
+	} else {
376
+		$resp['tags'] = $tags[0];
377
+	}
378
+
379
+        $this->SetResponse($resp);
380
+    End:
381
+        return;
382
+    }
383
+
384
+    private function GetPhotoTagList($params)
385
+    {
386
+	// database query
387
+        if ($params['isPhoto']) {
388
+            $result = ItemLabel::GetLabelByImageId($params['id']);
389
+        } else {
390
+            $result = ItemLabel::GetLabelByVideoPath($params['path']);
391
+        }
392
+
393
+        // form to webapi format
394
+        $tags = $this->FromTags($result, $params);
395
+
396
+        return $tags;
397
+    }
398
+
399
+    private function AddGeoDescTag($category)
400
+    {
401
+        if (false === ($params = $this->GetParams_Tag())) {
402
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
403
+            goto End;
404
+        }
405
+
406
+        // check permmision
407
+		$albums = array();
408
+        foreach ($params['ids'] as $idData) {
409
+            $albumName = dirname($idData['sharePath']);
410
+			if ('.' === $albumName) {
411
+				$albumName = '/';
412
+			}
413
+			$albums[$albumName] = 1;
414
+        }
415
+		foreach ($albums as $name => $v) {
416
+            if (!csSYNOPhotoMisc::CheckAlbumManageable($name)) {
417
+                $this->SetError(PHOTOSTATION_PHOTO_TAG_ACCESS_DENY);
418
+                goto End;
419
+            }
420
+		}
421
+
422
+        // check tag exist
423
+        foreach ($params['tag_ids'] as $tagID) {
424
+            if (false === Label::CheckLabelExistById($tagID)) {
425
+                $this->SetError(PHOTOSTATION_PHOTO_TAG_NOT_EXIST);
426
+                goto End;
427
+            }
428
+        }
429
+
430
+        $failCount = 0;
431
+		$duplicateCount = 0;
432
+		$item_tag_ids = array();
433
+        foreach ($params['ids'] as $idData) {
434
+            if ($idData['isPhoto']) {
435
+                foreach ($params['tag_ids'] as $tagID) {
436
+                    if (true === ItemLabel::CheckPhotoLabelExist($tagID, $idData['id'])) {
437
+                        $failCount++;
438
+                        $duplicateCount++;
439
+                        continue;
440
+                    }
441
+                    if (false === ItemLabel::AddImageLabel($idData['id'], $tagID)) {
442
+                        $failCount++;
443
+                        continue;
444
+					}
445
+					$row = ItemLabel::GetByItemKeyLabelId($idData['id'], $tagID, true);
446
+					if (!$row) {
447
+						$failCount++;
448
+						continue;
449
+					}
450
+					$photo_label_id = $row['id'];
451
+					array_push($item_tag_ids, 'item_tag_'.$photo_label_id);
452
+					self::OutputSingleSpace();
453
+                }
454
+            } else {
455
+                foreach ($params['tag_ids'] as $tagID) {
456
+                    if (false === $this->CheckVideoExist(('root' === SYNOPHOTO_ADMIN_USER) ? $idData['path'] : $idData['sharePath'])) {
457
+                        $failCount++;
458
+                        continue;
459
+                    }
460
+
461
+                    if (true === ItemLabel::CheckVideoLabelExist($tagID, $idData['dbPath'])) {
462
+                        $failCount++;
463
+                        $duplicateCount++;
464
+                        continue;
465
+                    }
466
+                    if (false === ItemLabel::AddVideoLabel($idData['dbPath'], $tagID)) {
467
+                        $failCount++;
468
+                        continue;
469
+					}
470
+
471
+					$row = ItemLabel::GetByItemKeyLabelId($idData['dbPath'], $tagID, false);
472
+					if (!$row) {
473
+						$failCount++;
474
+						continue;
475
+					}
476
+					$photo_label_id = $row['id'];
477
+					array_push($item_tag_ids, 'item_tag_'.$photo_label_id);
478
+					self::OutputSingleSpace();
479
+                }
480
+            }
481
+        }
482
+        if ($failCount === count($params['ids']) * count($params['tag_ids'])) {
483
+            if (count($params['ids']) == $duplicateCount) {
484
+                $this->SetError(PHOTOSTATION_PHOTO_TAG_DUPLICATE);
485
+            } else {
486
+                $this->SetError(PHOTOSTATION_PHOTO_TAG_ADD_GEO_DESC_FAIL);
487
+            }
488
+            goto End;
489
+		}
490
+		$resp['item_tag_ids'] = $item_tag_ids;
491
+		$this->SetResponse($resp);
492
+    End:
493
+        return;
494
+    }
495
+
496
+    private function PeopleTagConfirm()
497
+    {
498
+        if (false === ($params = $this->GetParams_PeopleTagConfirm())) {
499
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
500
+            goto End;
501
+        }
502
+
503
+        // check permmision
504
+        $albumName = dirname($params['sharePath']);
505
+		if ('.' === $albumName) {
506
+			$albumName = '/';
507
+		}
508
+        if (!csSYNOPhotoMisc::CheckAlbumManageable($albumName)) {
509
+            $this->SetError(PHOTOSTATION_PHOTO_TAG_ACCESS_DENY);
510
+            goto End;
511
+        }
512
+
513
+        // check tag exist
514
+        if (false === Label::CheckLabelExistById($params['tag_id'])) {
515
+            $this->SetError(PHOTOSTATION_PHOTO_TAG_NOT_EXIST);
516
+            goto End;
517
+        }
518
+
519
+        // update confirm
520
+        if (false === ItemLabel::UpdateLabelStatus($params['id'], $params['tag_id'], $params['confirm'])) {
521
+            $this->SetError(PHOTOSTATION_PHOTO_TAG_PEOPLE_TAG_CONFIRM_FAIL);
522
+            goto End;
523
+        }
524
+    End:
525
+        return;
526
+    }
527
+
528
+    private function AddPeopleTag()
529
+    {
530
+        if (false === ($params = $this->GetParams_PeopleTag())) {
531
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
532
+            goto End;
533
+        }
534
+
535
+        // check permmision
536
+        $albumName = dirname($params['sharePath']);
537
+		if ('.' === $albumName) {
538
+			$albumName = '/';
539
+		}
540
+        if (!csSYNOPhotoMisc::CheckAlbumManageable($albumName)) {
541
+            $this->SetError(PHOTOSTATION_PHOTO_TAG_ACCESS_DENY);
542
+            goto End;
543
+        }
544
+
545
+        // check tag exist
546
+        if (false === Label::CheckLabelExistById($params['tag_id'])) {
547
+            $this->SetError(PHOTOSTATION_PHOTO_TAG_NOT_EXIST);
548
+            goto End;
549
+        }
550
+
551
+        // tag can't duplicate in the same photo
552
+        if (true === ItemLabel::CheckPhotoLabelExist($params['tag_id'], $params['id'])) {
553
+            $this->SetError(PHOTOSTATION_PHOTO_TAG_DUPLICATE);
554
+            goto End;
555
+        }
556
+
557
+        // add people tag
558
+        $info_new = json_encode(
559
+            array(
560
+                'x' => $params['x'],
561
+                'y' => $params['y'],
562
+                'width' => $params['width'],
563
+                'height' => $params['height']
564
+            )
565
+        );
566
+        if (false === ItemLabel::AddPeopleLabel($params['id'], $params['tag_id'], $info_new)) {
567
+            $this->SetError(PHOTOSTATION_PHOTO_TAG_ADD_PEOPLE_FAIL);
568
+            goto End;
569
+		}
570
+
571
+		$item_tag_ids = array();
572
+		$row = ItemLabel::GetByItemKeyLabelId($params['id'], $params['tag_id'], true);
573
+		if (!$row) {
574
+			goto End;
575
+		}
576
+		$photo_label_id = $row['id'];
577
+		array_push($item_tag_ids, 'item_tag_'.$photo_label_id);
578
+		$resp['item_tag_ids'] = $item_tag_ids;
579
+		$this->SetResponse($resp);
580
+
581
+		// perform face detect for tag area
582
+        if ('on' === csSYNOPhotoMisc::GetConfigDB("photo", "enable_face_recognition", "photo_config")) {
583
+            $cmd = sprintf("%s -T %s %s", SYNOPHOTO_FACE_RECOG_TOOL, $photo_label_id, escapeshellarg($params['path']));
584
+            @exec($cmd);
585
+		}
586
+    End:
587
+        return;
588
+    }
589
+
590
+    private function Delete()
591
+    {
592
+        if (false === ($params = $this->GetParams_Delete())) {
593
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
594
+            goto End;
595
+        }
596
+
597
+        // check permmision
598
+        foreach ($params['ids'] as $idData) {
599
+            $albumName = dirname($idData['sharePath']);
600
+			if ('.' === $albumName) {
601
+				$albumName = '/';
602
+			}
603
+            if (!csSYNOPhotoMisc::CheckAlbumManageable($albumName)) {
604
+                $this->SetError(PHOTOSTATION_PHOTO_TAG_ACCESS_DENY);
605
+                goto End;
606
+            }
607
+        }
608
+
609
+        foreach ($params['ids'] as $idData) {
610
+            if (!empty($params['item_tag_ids'])) {
611
+                foreach ($params['item_tag_ids'] as $photoTagID) {
612
+                    ItemLabel::DeleteAllById($photoTagID, $idData['isPhoto']);
613
+                    self::OutputSingleSpace();
614
+                }
615
+            }
616
+
617
+            if (!empty($params['tag_ids'])) {
618
+				ItemLabel::DeleteByLabelIdItemId($params['tag_ids'], $idData['isPhoto'] ? $idData['id'] : $idData['dbPath'], $idData['isPhoto']);
619
+				self::OutputSingleSpace();
620
+            }
621
+		}
622
+
623
+		// Delete non-used label
624
+		if (!empty($params['tag_ids'])) {
625
+			$label_ids = array();
626
+			foreach ($params['tag_ids'] as $tagID) {
627
+				$count = ItemLabel::GetCountByLabelId($tagID);
628
+				if ($count === 0) {
629
+					$label_ids[] = $tagID;
630
+				}
631
+			}
632
+			Label::DeleteByIds($label_ids);
633
+		}
634
+
635
+    End:
636
+        return;
637
+    }
638
+}
639
+
640
+$api = new PhotoTagAPI();
641
+$api->Run();
642
+
643
+
644
+?>

+ 146
- 0
PhotoStation API/webapi/photoutil.php 查看文件

@@ -0,0 +1,146 @@
1
+<?php
2
+require_once('albumutil.php');
3
+
4
+class PhotoAPIUtil {
5
+	static function getLabelIDConstraint($idList)
6
+	{
7
+		$ret = '';
8
+		$ids = explode(',', $idList);
9
+		$isFirst = true;
10
+		foreach ($ids as $id) {
11
+			$arr = explode('_', $id);
12
+			if ('tag' !== $arr[0] || !is_numeric($arr[1])) {
13
+				continue;
14
+			}
15
+			if (!$isFirst) {
16
+				$ret .= " OR ";
17
+			}
18
+			$ret .= "label_id = $arr[1]";
19
+			$isFirst = false;
20
+		}
21
+		return $ret;
22
+	}
23
+
24
+	static function getItemByPath($fullpath, $additional, $type, $getRealPath)
25
+	{
26
+		if ($getRealPath) {
27
+			if (false === csSYNOPhotoMisc::CheckPathValid($fullpath = SYNOPHOTO_SERVICE_REAL_DIR . "/" . $fullpath)) {
28
+				return false;
29
+			}
30
+		}
31
+
32
+		$dbPath = substr($fullpath, strlen(SYNOPHOTO_SERVICE_REAL_DIR_PREFIX));
33
+		$sharename = substr(pathinfo($dbPath, PATHINFO_DIRNAME), strlen(SYNOPHOTO_SERVICE_REAL_DIR_PATH));
34
+
35
+		if (in_array($sharename, array('.', ''))) {
36
+			$sharename = '/';
37
+		}
38
+
39
+		$path = $fullpath;
40
+
41
+		$needExif = in_array('photo_exif', $additional);
42
+		$needCodec = in_array('video_codec', $additional);
43
+		$needQuality = in_array('video_quality', $additional);
44
+		$needThumbSize = in_array('thumb_size', $additional);
45
+
46
+		$item = null;
47
+		if ('photo' === $type) {
48
+			$items = File::GetPhotoByDBPath(array($dbPath));
49
+			$row = $items[$dbPath];
50
+			if (!$row) {
51
+				return false;
52
+			}
53
+
54
+			$obj = null;
55
+			$obj['name'] = $row['name'];
56
+			$obj['title'] = $row['title'];
57
+			$obj['createdate'] = $row['create_time'];
58
+			$obj['size'] = (int)$row['size'];
59
+			$obj['takendate'] = $row['timetaken'];
60
+			$obj['resolutionx'] = (int)$row['resolutionx'];
61
+			$obj['resolutiony'] = (int)$row['resolutiony'];
62
+			$obj['description'] = $row['description'];
63
+			$obj['rotated'] = (0 === (int)$row['version'] %2) ? false : true;
64
+			$obj['rotate_version'] = (int)$row['version'];
65
+
66
+			/* set photo exif if required */
67
+			if ($needExif) {
68
+				$addObj = null;
69
+				$addObj['takendate'] = $row['timetaken'];
70
+				$addObj['camera'] = $row['camera_make'];
71
+				$addObj['camera_model'] = $row['camera_model'];
72
+				$addObj['exposure'] = $row['exposure'];
73
+				$addObj['aperture'] = $row['aperture'];
74
+				$addObj['iso'] = (int)$row['iso'];
75
+				$addObj['gps'] = json_decode($row['gps'], true);
76
+				$addObj['version'] = $row['version'];
77
+				$addObj['focal_length'] = $row['focal_length_v2'];
78
+				$addObj['lens'] = $row['lens_v2'];
79
+				$addObj['flash'] = $row['flash_v2'];
80
+				$item['additional']['photo_exif'] = $addObj;
81
+			}
82
+
83
+			$orig_resolutionx = $row['resolutionx'];
84
+			$orig_resolutiony = $row['resolutiony'];
85
+			$blThumbRotated = (0 === (int)$row['version'] %2) ? false : true;
86
+
87
+		} elseif ('video' === $type) {
88
+			$items = File::GetVideoByDBPath(array($dbPath));
89
+			$row = $items[$dbPath];
90
+			if (false === $row) {
91
+				return false;
92
+			}
93
+			$title_desc = csSYNOPhotoDB::GetDBInstance()->GetVideoCustomizedTitleDescription($dbPath);
94
+
95
+			$obj = null;
96
+			$obj['name'] = basename($path);
97
+			$obj['createdate'] = $row['date'];
98
+			$obj['takendate'] = $row['mdate'];
99
+			$obj['size'] = (int)$row['filesize'];
100
+			$obj['title'] = empty($title_desc['title']) ? $row['title'] : $title_desc['title'];
101
+			$obj['description'] = $title_desc['description'];
102
+			$obj['duration'] = (int)$row['duration'];
103
+			$obj['gps'] = json_decode($row['gps']);
104
+
105
+			/* set video codec if required */
106
+			if ($needCodec) {
107
+				$addObj = null;
108
+				$addObj['container'] = $row['container_type'];
109
+				$addObj['vcodec'] = $row['video_codec'];
110
+				$addObj['acodec'] = $row['audio_codec'];
111
+				$addObj['resolutionx'] = (int)$row['resolutionx'];
112
+				$addObj['resolutiony'] = (int)$row['resolutiony'];
113
+				$addObj['frame_bitrate'] = (int)$row['frame_bitrate'];
114
+				$addObj['video_bitrate'] = (int)$row['video_bitrate'];
115
+				$addObj['audio_bitrate'] = (int)$row['audio_bitrate'];
116
+				$item['additional']['video_codec'] = $addObj;
117
+			}
118
+			/* set video quality if required */
119
+			if ($needQuality) {
120
+				$item['additional']['video_quality'] = AlbumAPIUtil::GetConvertedVideoInfo($dbPath);
121
+			}
122
+
123
+			$orig_resolutionx = $row['resolutionx'];
124
+			$orig_resolutiony = $row['resolutiony'];
125
+			$blThumbRotated = false;
126
+		}
127
+
128
+		$blConversion = csSYNOPhotoMisc::IsAlbumAllowConversion($sharename);
129
+		$thumbnail = AlbumAPIUtil::GetThumbStatus($dbPath, $needThumbSize, $orig_resolutionx, $orig_resolutiony, $blThumbRotated, $blConversion);
130
+		$item['thumbnail_status'] = $thumbnail['status'];
131
+		if ($needThumbSize) {
132
+			$item['additional']['thumb_size'] = $thumbnail['size'];
133
+		}
134
+
135
+        if (SYNOPHOTO_SERVICE_REAL_DIR === dirname($fullpath)) {
136
+            $item['id'] = $type . '_' . bin2hex('/') . '_' . bin2hex(basename($path));
137
+        } else {
138
+            $item['id'] = $type . '_' . bin2hex($sharename) . '_' . bin2hex(basename($path));
139
+        }
140
+		$item['type'] = $type;
141
+		$item['info'] = $obj;
142
+		return $item;
143
+	}
144
+}
145
+
146
+?>

+ 1707
- 0
PhotoStation API/webapi/project.wpr
文件差異過大導致無法顯示
查看文件


+ 758
- 0
PhotoStation API/webapi/psutils.lua 查看文件

@@ -0,0 +1,758 @@
1
+--[[----------------------------------------------------------------------------
2
+
3
+PSPhotoStationUtils.lua
4
+This file is part of Photo StatLr - Lightroom plugin.
5
+Copyright(c) 2017, Martin Messmer
6
+
7
+Photo Station utilities:
8
+	- getErrorMsg
9
+
10
+	- getAlbumId
11
+	- getPhotoId
12
+
13
+	- isSharedAlbumPublic
14
+	- getSharedAlbumId
15
+	- getSharedAlbumShareId
16
+
17
+	- getAlbumUrl
18
+	- getPhotoUrl
19
+
20
+	- getPhotoInfo
21
+
22
+	- createAndAddPhotoTag
23
+	- createAndAddPhotoTagList
24
+
25
+	- createAndAddPhotosToSharedAlbum
26
+	- removePhotosFromSharedAlbumIfExists
27
+
28
+	- deleteEmptyAlbumAndParents
29
+
30
+	- rating2Stars
31
+
32
+Photo StatLr is free software: you can redistribute it and/or modify
33
+it under the terms of the GNU General Public License as published by
34
+the Free Software Foundation, either version 3 of the License, or
35
+(at your option) any later version.
36
+
37
+Photo StatLr is distributed in the hope that it will be useful,
38
+but WITHOUT ANY WARRANTY; without even the implied warranty of
39
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
40
+GNU General Public License for more details.
41
+
42
+You should have received a copy of the GNU General Public License
43
+along with Photo StatLr.  If not, see <http://www.gnu.org/licenses/>.
44
+
45
+]]
46
+--------------------------------------------------------------------------------
47
+
48
+-- Lightroom API
49
+local LrFileUtils = import 'LrFileUtils'
50
+local LrPathUtils = import 'LrPathUtils'
51
+local LrHttp = import 'LrHttp'
52
+local LrDate = import 'LrDate'
53
+
54
+require "PSUtilities"
55
+
56
+--====== local functions =====================================================--
57
+
58
+local PSAPIerrorMsgs = {
59
+	[0]   = 'No error',
60
+	[100] = 'Unknown error',
61
+    [101] = 'No parameter of API, method or version',		-- PS 6.6: no such directory
62
+    [102] = 'The requested API does not exist',
63
+    [103] = 'The requested method does not exist',
64
+    [104] = 'The requested version does not support the functionality',
65
+    [105] = 'The logged in session does not have permission',
66
+    [106] = 'Session timeout',
67
+    [107] = 'Session interrupted by duplicate login',
68
+	[400] = 'Invalid parameter',
69
+	[401] = 'Unknown error of file operation',
70
+	[402] = 'System is too busy',
71
+	[403] = 'Invalid user does this file operation',
72
+	[404] = 'Invalid group does this file operation',
73
+	[405] = 'Invalid user and group does this file operation',
74
+	[406] = 'Can�t get user/group information from the account server',
75
+	[407] = 'Operation not permitted',
76
+	[408] = 'No such file or directory',
77
+	[409] = 'Non-supported file system',
78
+	[410] = 'Failed to connect internet-based file system (ex: CIFS)',
79
+	[411] = 'Read-only file system',
80
+	[412] = 'Filename too long in the non-encrypted file system',
81
+	[413] = 'Filename too long in the encrypted file system',
82
+	[414] = 'File already exists',
83
+	[415] = 'Disk quota exceeded',
84
+	[416] = 'No space left on device',
85
+	[417] = 'Input/output error',
86
+	[418] = 'Illegal name or path',
87
+	[419] = 'Illegal file name',
88
+	[420] = 'Illegal file name on FAT filesystem',
89
+	[421] = 'Device or resource busy',
90
+	[467] = 'No such tag',
91
+	[468] = 'Duplicate tag',
92
+	[470] = 'No such file',
93
+	[555] = 'No such shared album',
94
+	[599] = 'No such task of the file operation',
95
+	[1001]  = 'Http error: no response body, no response header',
96
+	[1002]  = 'Http error: no response data, no errorcode in response header',
97
+	[1003]  = 'Http error: No JSON response data',
98
+	[12007] = 'Http error: cannotFindHost',
99
+	[12029] = 'Http error: cannotConnectToHost',
100
+	[12038] = 'Http error: serverCertificateHasUnknownRoot',
101
+}
102
+
103
+-- ========================================== Album Content cache ==============================================
104
+-- the Album Content cache holds Album contents for the least recently read albums
105
+
106
+local albumContentCache = {}
107
+local albumContentCacheTimeout = 60	-- 60 seconds cache time
108
+
109
+---------------------------------------------------------------------------------------------------------
110
+-- albumContentCacheCleanup: remove old entries from cache
111
+local function albumContentCacheCleanup()
112
+	for i = #albumContentCache, 1, -1 do
113
+		local cachedAlbum = albumContentCache[i]
114
+		if cachedAlbum.validUntil < LrDate.currentTime() then
115
+			writeLogfile(3, string.format("albumContentCacheCleanup(); removing %s\n", cachedAlbum.albumPath))
116
+			table.remove(albumContentCache, i)
117
+		end
118
+	end
119
+end
120
+
121
+---------------------------------------------------------------------------------------------------------
122
+-- albumContentCacheList: returns all photos/videos and optionally albums in a given album via album cache
123
+-- returns
124
+--		albumItems:		table of photo infos, if success, otherwise nil
125
+--		errorcode:		errorcode, if not success
126
+local function albumContentCacheList(h, dstDir, listItems)
127
+	albumContentCacheCleanup()
128
+
129
+	for i = 1, #albumContentCache do
130
+		local cachedAlbum = albumContentCache[i]
131
+		if cachedAlbum.albumPath == dstDir then
132
+			return cachedAlbum.albumItems
133
+		end
134
+	end
135
+
136
+	-- not found in cache: get it from Photo Station
137
+	local albumItems, errorCode = PSPhotoStationAPI.listAlbum(h, dstDir, listItems)
138
+	if not albumItems then
139
+		if 	errorCode ~= 408  	-- no such file or dir
140
+		and errorCode ~= 417	-- no such dir for non-administrative users , see GitHub issue 17
141
+		and errorCode ~= 101	-- no such dir in PS 6.6
142
+		then
143
+			writeLogfile(2, string.format('albumContentCacheList: Error on listAlbum: %d\n', errorCode))
144
+		   	return nil, errorCode
145
+		end
146
+		albumItems = {} -- avoid re-requesting non-existing album
147
+	end
148
+
149
+	local cacheEntry = {}
150
+	cacheEntry.albumPath = dstDir
151
+	cacheEntry.albumItems = albumItems
152
+	cacheEntry.validUntil = LrDate.currentTime() + albumContentCacheTimeout
153
+	table.insert(albumContentCache, 1, cacheEntry)
154
+
155
+	writeLogfile(3, string.format("albumContentCacheList(%s): added to cache with %d items\n", dstDir, #albumItems))
156
+
157
+	return albumItems
158
+end
159
+
160
+-- ==================================== Shared Album content cache ==============================================
161
+-- the Shared Album content cache holds Shared Album contents for the least recently read albums
162
+
163
+local sharedAlbumContentCache = {}
164
+local sharedAlbumContentCacheTimeout = 60	-- 60 seconds cache time
165
+
166
+---------------------------------------------------------------------------------------------------------
167
+-- sharedAlbumContentCacheCleanup: remove old entries from cache
168
+local function sharedAlbumContentCacheCleanup()
169
+	for i = #sharedAlbumContentCache, 1, -1 do
170
+		local cachedAlbum = sharedAlbumContentCache[i]
171
+		if cachedAlbum.validUntil < LrDate.currentTime() then
172
+			writeLogfile(3, string.format("sharedAlbumContentCacheCleanup(); removing %s\n", cachedAlbum.albumPath))
173
+			table.remove(sharedAlbumContentCache, i)
174
+		end
175
+	end
176
+end
177
+
178
+---------------------------------------------------------------------------------------------------------
179
+-- sharedAlbumContentCacheList: returns all photos/videos and optionally albums in a given album via album cache
180
+-- returns
181
+--		albumItems:		table of photo infos, if success, otherwise nil
182
+--		errorcode:		errorcode, if not success
183
+local function sharedAlbumContentCacheList(h, dstDir, listItems)
184
+	sharedAlbumContentCacheCleanup()
185
+
186
+	for i = 1, #sharedAlbumContentCache do
187
+		local cachedAlbum = sharedAlbumContentCache[i]
188
+		if cachedAlbum.albumPath == dstDir then
189
+			return cachedAlbum.albumItems
190
+		end
191
+	end
192
+
193
+	-- not found in cache: get it from Photo Station
194
+	local albumItems, errorCode = PSPhotoStationAPI.listSharedAlbum(h, dstDir, listItems)
195
+	if not albumItems then
196
+		if 	errorCode ~= 408  	-- no such file or dir
197
+		and errorCode ~= 417	-- no such dir for non-administrative users , see GitHub issue 17
198
+		and errorCode ~= 101	-- no such dir in PS 6.6
199
+		then
200
+			writeLogfile(2, string.format('sharedAlbumContentCacheList: Error on listSharedAlbum: %d\n', errorCode))
201
+		   	return nil, errorCode
202
+		end
203
+		albumItems = {} -- avoid re-requesting non-existing album
204
+	end
205
+
206
+	local cacheEntry = {}
207
+	cacheEntry.albumPath = dstDir
208
+	cacheEntry.albumItems = albumItems
209
+	cacheEntry.validUntil = LrDate.currentTime() + sharedAlbumContentCacheTimeout
210
+	table.insert(sharedAlbumContentCache, 1, cacheEntry)
211
+
212
+	writeLogfile(3, string.format("sharedAlbumContentCacheList(%s): added to cache with %d items\n", dstDir, #albumItems))
213
+
214
+	return albumItems
215
+end
216
+
217
+-- ===================================== Shared Albums cache ==============================================
218
+-- the Shared Album List cache holds the list of shared album infos
219
+
220
+local sharedAlbumsCache 				= {}
221
+local sharedAlbumsCacheTimeout 		= 60	-- 60 seconds cache time
222
+local sharedAlbumsCacheValidUntil
223
+
224
+---------------------------------------------------------------------------------------------------------
225
+-- sharedAlbumsCacheCleanup: cleanup cache if cache is too old
226
+local function sharedAlbumsCacheCleanup()
227
+	if ifnil(sharedAlbumsCacheValidUntil, LrDate.currentTime()) <= LrDate.currentTime() then
228
+		sharedAlbumsCache = {}
229
+	end
230
+	return true
231
+end
232
+
233
+---------------------------------------------------------------------------------------------------------
234
+-- sharedAlbumsCacheUpdate(h)
235
+local function sharedAlbumsCacheUpdate(h)
236
+	writeLogfile(3, string.format('sharedAlbumsCacheUpdate().\n'))
237
+	sharedAlbumsCache = PSPhotoStationAPI.getSharedAlbums(h)
238
+	sharedAlbumsCacheValidUntil = LrDate.currentTime() + sharedAlbumsCacheTimeout
239
+	return sharedAlbumsCache
240
+end
241
+
242
+---------------------------------------------------------------------------------------------------------
243
+-- sharedAlbumsCacheFind(h, name)
244
+local function sharedAlbumsCacheFind(h, name)
245
+	sharedAlbumsCacheCleanup()
246
+	if (#sharedAlbumsCache == 0) and not sharedAlbumsCacheUpdate(h) then
247
+		return nil
248
+	end
249
+
250
+	for i = 1, #sharedAlbumsCache do
251
+		if sharedAlbumsCache[i].name == name then
252
+			writeLogfile(4, string.format('sharedAlbumsCacheFind(%s) found  %s.\n', name, sharedAlbumsCache[i].id))
253
+			return sharedAlbumsCache[i]
254
+		end
255
+	end
256
+
257
+	writeLogfile(4, string.format('sharedAlbumsCacheFind(%s) not found.\n', name))
258
+	return nil
259
+end
260
+
261
+-- ======================================= tagMapping ==============================================
262
+-- the tagMapping holds the list of tag name / tag id mappings
263
+
264
+local tagMapping = {
265
+	['desc']	= {},
266
+	['person']	= {},
267
+	['geo']		= {},
268
+}
269
+
270
+---------------------------------------------------------------------------------------------------------
271
+-- tagMappingUpdate(h, type)
272
+local function tagMappingUpdate(h, type)
273
+	writeLogfile(3, string.format('tagMappingUpdate(%s).\n', type))
274
+	tagMapping[type] = PSPhotoStationAPI.getTags(h, type)
275
+	return tagMapping[type]
276
+end
277
+
278
+---------------------------------------------------------------------------------------------------------
279
+-- tagMappingFind(h, type, name)
280
+local function tagMappingFind(h, type, name)
281
+	local tagsOfType = tagMapping[type]
282
+
283
+	if (#tagsOfType == 0) and not tagMappingUpdate(h, type) then
284
+		return nil
285
+	end
286
+	tagsOfType = tagMapping[type]
287
+
288
+	for i = 1, #tagsOfType do
289
+		if tagsOfType[i].name == name then
290
+			writeLogfile(3, string.format('tagMappingFind(%s, %s) found  %s.\n', type, name, tagsOfType[i].id))
291
+			return tagsOfType[i].id
292
+		end
293
+	end
294
+
295
+	writeLogfile(3, string.format('tagMappingFind(%s, %s) not found.\n', type, name))
296
+	return nil
297
+end
298
+
299
+--================================= global functions ====================================================--
300
+
301
+PSPhotoStationUtils = {}
302
+
303
+---------------------------------------------------------------------------------------------------------
304
+-- getErrorMsg(errorCode)
305
+-- translates errorCode to ErrorMsg
306
+function PSPhotoStationUtils.getErrorMsg(errorCode)
307
+	if PSAPIerrorMsgs[errorCode] == nil then
308
+		-- we don't have a documented  message for that code
309
+		return string.format("ErrorCode: %d", errorCode)
310
+	end
311
+	return PSAPIerrorMsgs[errorCode]
312
+end
313
+
314
+
315
+--[[
316
+PSPhotoStationUtils.getAlbumId(albumPath)
317
+	returns the AlbumId of a given Album path (not leading and trailing slashes) in Photo Station
318
+	AlbumId looks like:
319
+	album_<AlbumPathInHex>
320
+	E.g. Album Path:
321
+		Albums-->Test/2007
322
+	yields AlbumId:
323
+		album_546573742f32303037
324
+]]
325
+function PSPhotoStationUtils.getAlbumId(albumPath)
326
+	local i
327
+	local albumId = 'album_'
328
+
329
+	if ifnil(albumPath, '') == '' then return '' end
330
+
331
+	for i = 1, string.len(albumPath) do
332
+		albumId = albumId .. string.format('%x', string.byte(albumPath,i))
333
+	end
334
+
335
+--	writeLogfile(4, string.format("getAlbumId(%s) returns %s\n", albumPath, albumId))
336
+
337
+	return albumId
338
+end
339
+
340
+--[[
341
+getPhotoId(photoPath, isVideo)
342
+	returns the PhotoId of a given photo path in Photo Station
343
+	PhotoId looks like:
344
+		photo_<AlbumPathInHex>_<PhotoPathInHex> or
345
+		video_<AlbumPathInHex>_<PhotoPathInHex> or
346
+	E.g. Photo Path:
347
+		Albums --> Test/2007/2007_08_13_IMG_7415.JPG
348
+	yields PhotoId:
349
+		photo_546573742f32303037_323030375f30385f31335f494d475f373431352e4a5047
350
+]]
351
+function PSPhotoStationUtils.getPhotoId(photoPath, isVideo)
352
+	local i
353
+	local photoDir, photoFilename = string.match(photoPath , '(.*)\/([^\/]+)')
354
+	if not photoDir then
355
+		photoDir = '/'
356
+		photoFilename = photoPath
357
+	end
358
+	local albumSubId = ''
359
+	local photoSubId = ''
360
+	local photoId = iif(isVideo, 'video_', 'photo_')
361
+
362
+	for i = 1, string.len(photoDir) do
363
+		albumSubId = albumSubId .. string.format('%x', string.byte(photoDir,i))
364
+	end
365
+
366
+	for i = 1, string.len(photoFilename) do
367
+		photoSubId = photoSubId .. string.format('%x', string.byte(photoFilename,i))
368
+	end
369
+
370
+	photoId = photoId .. albumSubId .. '_' .. photoSubId
371
+
372
+--	writeLogfile(4, string.format("getPhotoId(%s) returns %s\n", photoPath, photoId))
373
+
374
+	return photoId
375
+end
376
+
377
+---------------------------------------------------------------------------------------------------------
378
+-- PSPhotoStationUtils.getSharedAlbumId(h, sharedAlbumName)
379
+-- 	returns the shareId of a given SharedAlbum using the Shared Album cache
380
+function PSPhotoStationUtils.getSharedAlbumId(h, sharedAlbumName)
381
+	local sharedAlbumInfo = sharedAlbumsCacheFind(h, sharedAlbumName)
382
+
383
+	if not sharedAlbumInfo then
384
+		writeLogfile(3, string.format('getSharedAlbumId(%s): Shared Album not found.\n', sharedAlbumName))
385
+		return nil
386
+	end
387
+
388
+	return sharedAlbumInfo.id
389
+end
390
+
391
+---------------------------------------------------------------------------------------------------------
392
+-- PSPhotoStationUtils.isSharedAlbumPublic(h, sharedAlbumName)
393
+--  returns the public flage of a given SharedAlbum using the Shared Album cache
394
+function PSPhotoStationUtils.isSharedAlbumPublic(h, sharedAlbumName)
395
+	local sharedAlbumInfo = sharedAlbumsCacheFind(h, sharedAlbumName)
396
+
397
+	if not sharedAlbumInfo then
398
+		writeLogfile(3, string.format('isSharedAlbumPublic(%s): Shared album not found.\n', sharedAlbumName))
399
+		return false
400
+	end
401
+	if not sharedAlbumInfo.additional or not sharedAlbumInfo.additional.public_share or sharedAlbumInfo.additional.public_share.share_status ~= 'valid' then
402
+		return false
403
+	end
404
+
405
+	return true
406
+end
407
+
408
+---------------------------------------------------------------------------------------------------------
409
+-- PSPhotoStationUtils.getSharedAlbumShareId(h, sharedAlbumName)
410
+-- 	returns the shareId of a given SharedAlbum using the Shared Album cache
411
+function PSPhotoStationUtils.getSharedAlbumShareId(h, sharedAlbumName)
412
+	local sharedAlbumInfo = sharedAlbumsCacheFind(h, sharedAlbumName)
413
+
414
+	if not sharedAlbumInfo then
415
+		writeLogfile(3, string.format('getSharedAlbumShareId(%s): Shared Album not found.\n', sharedAlbumName))
416
+		return nil
417
+	end
418
+
419
+	return sharedAlbumInfo.additional.public_share.shareid
420
+end
421
+
422
+---------------------------------------------------------------------------------------------------------
423
+-- getAlbumUrl(h, albumPath)
424
+--	returns the URL of an album in the Photo Station
425
+--	URL of an album in PS is:
426
+--		http(s)://<PS-Server>/<PSBasedir>/#!Albums/<AlbumId_1rstLevelDir>/<AlbumId_1rstLevelAndSecondLevelDir>/.../AlbumId_1rstToLastLevelDir>
427
+--	E.g. Album Path:
428
+--		Server: http://diskstation; Standard Photo Station; Album Breadcrumb: Albums/Test/2007
429
+--	yields PS Photo-URL:
430
+--		http://diskstation/photo/#!Albums/album_54657374/album_546573742f32303037
431
+function PSPhotoStationUtils.getAlbumUrl(h, albumPath)
432
+	local i
433
+	local albumUrl
434
+	local subDirPath = ''
435
+	local subDirUrl  = ''
436
+
437
+	local albumDirname = split(albumPath, '/')
438
+
439
+	albumUrl = h.serverUrl .. h.psAlbumRoot
440
+
441
+	for i = 1, #albumDirname do
442
+		if i > 1 then
443
+			subDirPath = subDirPath .. '/'
444
+		end
445
+		subDirPath = subDirPath .. albumDirname[i]
446
+		subDirUrl = PSPhotoStationUtils.getAlbumId(subDirPath)
447
+		albumUrl = albumUrl .. '/' .. subDirUrl
448
+	end
449
+
450
+	writeLogfile(3, string.format("getAlbumUrl(%s, %s) returns %s\n", h.serverUrl .. h.psAlbumRoot, albumPath, albumUrl))
451
+
452
+	return albumUrl
453
+end
454
+
455
+---------------------------------------------------------------------------------------------------------
456
+-- getPhotoUrl(h, photoPath, isVideo)
457
+--	returns the URL of a photo/video in the Photo Station
458
+--	URL of a photo in PS is:
459
+--		http(s)://<PS-Server>/<PSBasedir>/#!Albums/<AlbumId_1rstLevelDir>/<AlbumId_1rstLevelAndSecondLevelDir>/.../AlbumId_1rstToLastLevelDir>/<PhotoId>
460
+--	E.g. Photo Path:
461
+--		Server: http://diskstation; Standard Photo Station; Photo Breadcrumb: Albums/Test/2007/2007_08_13_IMG_7415.JPG
462
+--	yields PS Photo-URL:
463
+--		http://diskstation/photo/#!Albums/album_54657374/album_546573742f32303037/photo_546573742f32303037_323030375f30385f31335f494d475f373431352e4a5047
464
+function PSPhotoStationUtils.getPhotoUrl(h, photoPath, isVideo)
465
+	local i
466
+	local subDirPath = ''
467
+	local subDirUrl  = ''
468
+	local photoUrl
469
+
470
+	local albumDir, _ = string.match(photoPath, '(.+)\/([^\/]+)')
471
+
472
+	local albumDirname = split(albumDir, '/')
473
+	if not albumDirname then albumDirname = {} end
474
+
475
+	photoUrl = h.serverUrl .. h.psAlbumRoot
476
+
477
+	for i = 1, #albumDirname do
478
+		if i > 1 then
479
+			subDirPath = subDirPath .. '/'
480
+		end
481
+		subDirPath = subDirPath .. albumDirname[i]
482
+		subDirUrl = PSPhotoStationUtils.getAlbumId(subDirPath)
483
+		photoUrl = photoUrl .. '/' .. subDirUrl
484
+	end
485
+
486
+	photoUrl = photoUrl .. '/' .. PSPhotoStationUtils.getPhotoId(photoPath, isVideo)
487
+
488
+	writeLogfile(3, string.format("getPhotoUrl(%s, %s) returns %s\n", h.serverUrl .. h.psAlbumRoot, photoPath, photoUrl))
489
+
490
+	return photoUrl
491
+end
492
+
493
+---------------------------------------------------------------------------------------------------------
494
+-- getPhotoInfo (h, dstFilename, isVideo, useCache)
495
+-- return photo infos for a given remote filename
496
+-- returns:
497
+-- 		photoInfos				if remote photo was found
498
+-- 		nil,		nil			if remote photo was not found
499
+-- 		nil,		errorCode	on error
500
+function PSPhotoStationUtils.getPhotoInfo(h, dstFilename, isVideo, useCache)
501
+	local dstAlbum = ifnil(string.match(dstFilename , '(.*)\/[^\/]+'), '/')
502
+	local photoInfos, errorCode
503
+	if useCache then
504
+		photoInfos, errorCode=  albumContentCacheList(h, dstAlbum, 'photo,video')
505
+	else
506
+		photoInfos, errorCode=  PSPhotoStationAPI.listAlbum(h, dstAlbum, 'photo,video')
507
+	end
508
+
509
+	if not photoInfos then return nil, errorCode end
510
+
511
+	local photoId = PSPhotoStationUtils.getPhotoId(dstFilename, isVideo)
512
+	for i = 1, #photoInfos do
513
+		if photoInfos[i].id == photoId then
514
+			writeLogfile(3, string.format('getPhotoInfo(%s, useCache %s) found infos.\n', dstFilename, useCache))
515
+			return photoInfos[i]
516
+		end
517
+	end
518
+
519
+	writeLogfile(3, string.format('getPhotoInfo(%s, useCache %s) found no infos.\n', dstFilename, useCache))
520
+	return nil, nil
521
+end
522
+
523
+---------------------------------------------------------------------------------------------------------
524
+-- getSharedPhotoInfo (h, sharedAlbumName, dstFilename, isVideo, useCache)
525
+-- return photo infos for a photo in a shared album
526
+-- returns:
527
+-- 		photoInfos				if remote photo was found
528
+-- 		nil,					if remote photo was not found
529
+-- 		nil,		errorCode	on error
530
+function PSPhotoStationUtils.getSharedPhotoInfo(h, sharedAlbumName, dstFilename, isVideo, useCache)
531
+	local photoInfos, errorCode
532
+	if useCache then
533
+		photoInfos, errorCode=  sharedAlbumContentCacheList(h, sharedAlbumName, 'photo,video')
534
+	else
535
+		photoInfos, errorCode=  PSPhotoStationAPI.listSharedAlbum(h, sharedAlbumName, 'photo,video')
536
+	end
537
+
538
+	if not photoInfos then return nil, errorCode end
539
+
540
+	local photoId = PSPhotoStationUtils.getPhotoId(dstFilename, isVideo)
541
+	for i = 1, #photoInfos do
542
+		if photoInfos[i].id == photoId then
543
+			writeLogfile(3, string.format('getSharedPhotoInfo(%s, %s, useCache %s) found infos.\n', sharedAlbumName, dstFilename, useCache))
544
+			return photoInfos[i]
545
+		end
546
+	end
547
+
548
+	writeLogfile(3, string.format('getSharedPhotoInfo(%s %s, useCache %s) found no infos.\n', sharedAlbumName, dstFilename, useCache))
549
+	return nil
550
+end
551
+
552
+---------------------------------------------------------------------------------------------------------
553
+-- getSharedPhotoPublicUrl (h, sharedAlbumName, dstFilename, isVideo)
554
+-- returns the public share url of a shared photo
555
+function PSPhotoStationUtils.getSharedPhotoPublicUrl(h, sharedAlbumName, dstFilename, isVideo)
556
+	local photoInfos, errorCode = PSPhotoStationUtils.getSharedPhotoInfo(h, sharedAlbumName, dstFilename, isVideo, true)
557
+
558
+	if not photoInfos then return nil, errorCode end
559
+
560
+	return photoInfos.public_share_url
561
+end
562
+
563
+---------------------------------------------------------------------------------------------------------
564
+-- createAndAddPhotoTag (h, dstFilename, isVideo, type, name)
565
+-- create and add a new tag (desc,people,geo) to a photo
566
+function PSPhotoStationUtils.createAndAddPhotoTag(h, dstFilename, isVideo, type, name)
567
+	local tagId = tagMappingFind(h, type, name)
568
+	if not tagId then
569
+		tagId = PSPhotoStationAPI.createTag(h, type, name)
570
+		tagMappingUpdate(h, type)
571
+	end
572
+
573
+
574
+	if not tagId then return false end
575
+
576
+	local photoTagIds, errorCode = PSPhotoStationAPI.addPhotoTag(h, dstFilename, isVideo, type, tagId)
577
+
578
+	if not photoTagIds and errorCode == 467 then
579
+		-- tag was deleted, cache wasn't up to date
580
+		tagId = PSPhotoStationAPI.createTag(h, type, name)
581
+		tagMappingUpdate(h, type)
582
+	 	photoTagIds, errorCode = PSPhotoStationAPI.addPhotoTag(h, dstFilename, isVideo, type, tagId)
583
+	end
584
+
585
+	-- errorCode 468: duplicate tag (tag already there)
586
+	if not photoTagIds and errorCode ~= 468 then return false end
587
+
588
+	writeLogfile(3, string.format('createAndAddPhotoTag(%s, %s, %s) returns OK.\n', dstFilename, type, name))
589
+	return true
590
+end
591
+
592
+---------------------------------------------------------------------------------------------------------
593
+-- createAndAddPhotoTagList (h, dstFilename, isVideo, type, tagList)
594
+-- create and add a list of new tags (general,people,geo) to a photo
595
+function PSPhotoStationUtils.createAndAddPhotoTagList(h, dstFilename, isVideo, type, tagList)
596
+
597
+	for i = 1, #tagList do
598
+		if not PSPhotoStationUtils.createAndAddPhotoTag(h, dstFilename, isVideo, type, tagList[i]) then
599
+			return false
600
+		end
601
+	end
602
+
603
+	writeLogfile(3, string.format('createAndAddPhotoTagList(%s) returns OK.\n', dstFilename))
604
+	return true
605
+end
606
+
607
+---------------------------------------------------------------------------------------------------------
608
+-- createAndAddPhotosToSharedAlbum(h, sharedAlbumName, mkSharedAlbumAdvanced, mkSharedAlbumPublic, sharedAlbumPassword, photos)
609
+-- create a Shared Album and add a list of photos to it
610
+-- returns success and share-link (if public)
611
+function PSPhotoStationUtils.createAndAddPhotosToSharedAlbum(h, sharedAlbumName, mkSharedAlbumAdvanced, mkSharedAlbumPublic, sharedAlbumPassword, photos)
612
+	local sharedAlbumInfo = sharedAlbumsCacheFind(h, sharedAlbumName)
613
+	local isNewSharedAlbum
614
+	local sharedAlbumAttributes = {}
615
+	local shareResult
616
+
617
+	if not sharedAlbumInfo then
618
+		if not PSPhotoStationAPI.createSharedAlbum(h, sharedAlbumName) then return false end
619
+		sharedAlbumsCacheUpdate(h)
620
+		sharedAlbumInfo = sharedAlbumsCacheFind(h, sharedAlbumName)
621
+		isNewSharedAlbum = true
622
+	end
623
+
624
+	local success, errorCode = PSPhotoStationAPI.addPhotosToSharedAlbum(h, sharedAlbumName, photos)
625
+
626
+	if not success and errorCode == 555 then
627
+		-- shared album was deleted, mapping wasn't up to date
628
+		if not PSPhotoStationAPI.createSharedAlbum(h, sharedAlbumName) then return false end
629
+		sharedAlbumsCacheUpdate(h)
630
+		sharedAlbumInfo = sharedAlbumsCacheFind(h, sharedAlbumName)
631
+		isNewSharedAlbum = true
632
+	 	success, errorCode = PSPhotoStationAPI.addPhotosToSharedAlbum(h, sharedAlbumName, photos)
633
+	end
634
+
635
+	if not success then return false end
636
+
637
+	sharedAlbumAttributes.is_shared = mkSharedAlbumPublic
638
+
639
+	--preserve old share start/time end restriction if album was and will be public
640
+	if		mkSharedAlbumPublic
641
+		and not isNewSharedAlbum
642
+		and sharedAlbumInfo
643
+		and sharedAlbumInfo.additional
644
+		and sharedAlbumInfo.additional.public_share
645
+		and sharedAlbumInfo.additional.public_share.share_status == 'valid'
646
+	then
647
+		sharedAlbumAttributes.start_time 	= sharedAlbumInfo.additional.public_share.start_time
648
+		sharedAlbumAttributes.end_time 		= sharedAlbumInfo.additional.public_share.end_time
649
+	end
650
+
651
+	if mkSharedAlbumAdvanced then
652
+		sharedAlbumAttributes.is_advanced = true
653
+
654
+		if sharedAlbumPassword then
655
+			sharedAlbumAttributes.enable_password = true
656
+			sharedAlbumAttributes.password = sharedAlbumPassword
657
+		else
658
+			sharedAlbumAttributes.enable_password = false
659
+		end
660
+
661
+		if isNewSharedAlbum then
662
+    		-- a lot of default parameters ...
663
+    		sharedAlbumAttributes.enable_marquee_tool	= true
664
+    		sharedAlbumAttributes.enable_comment 		= true
665
+    		sharedAlbumAttributes.enable_color_label	= true
666
+    		sharedAlbumAttributes.color_label_1 		= "red"
667
+    		sharedAlbumAttributes.color_label_2 		= "orange"
668
+    		sharedAlbumAttributes.color_label_3 		= "lime green"
669
+    		sharedAlbumAttributes.color_label_4 		= "aqua green"
670
+    		sharedAlbumAttributes.color_label_5 		= "blue"
671
+    		sharedAlbumAttributes.color_label_6 		= "purple"
672
+		else
673
+			--preserve old advcanced settings for already existing Shared Albums
674
+			if sharedAlbumInfo and sharedAlbumInfo.additional and sharedAlbumInfo.additional.public_share and sharedAlbumInfo.additional.public_share.advanced_info then
675
+				local advancedInfo = sharedAlbumInfo.additional.public_share.advanced_info
676
+
677
+        		sharedAlbumAttributes.enable_marquee_tool	= advancedInfo.enable_marquee_tool
678
+        		sharedAlbumAttributes.enable_comment 		= advancedInfo.enable_comment
679
+        		sharedAlbumAttributes.enable_color_label 	= advancedInfo.enable_color_label
680
+        		sharedAlbumAttributes.color_label_1 		= advancedInfo.color_label_1
681
+        		sharedAlbumAttributes.color_label_2 		= advancedInfo.color_label_2
682
+        		sharedAlbumAttributes.color_label_3 		= advancedInfo.color_label_3
683
+        		sharedAlbumAttributes.color_label_4 		= advancedInfo.color_label_4
684
+        		sharedAlbumAttributes.color_label_5 		= advancedInfo.color_label_5
685
+        		sharedAlbumAttributes.color_label_6 		= advancedInfo.color_label_6
686
+			end
687
+		end
688
+	end
689
+
690
+	shareResult = PSPhotoStationAPI.editSharedAlbum(h, sharedAlbumName, sharedAlbumAttributes)
691
+
692
+	if not shareResult then return false end
693
+
694
+	writeLogfile(3, string.format('createAndAddPhotosToSharedAlbum(%s, %s, %s, pw: %s, %d photos) returns OK.\n', sharedAlbumName, iif(mkSharedAlbumAdvanced, 'advanced', 'old'), iif(mkSharedAlbumPublic, 'public', 'private'), iif(sharedAlbumPassword, 'w/ passwd', 'w/o passwd'), #photos))
695
+	return true, shareResult.public_share_url
696
+end
697
+
698
+---------------------------------------------------------------------------------------------------------
699
+-- removePhotosFromSharedAlbumIfExists(h, sharedAlbumName, photos)
700
+-- remove a list of photos from a Shared Album
701
+-- ignore error if Shared Album doesn't exist
702
+function PSPhotoStationUtils.removePhotosFromSharedAlbumIfExists(h, sharedAlbumName, photos)
703
+	local sharedAlbumInfo = sharedAlbumsCacheFind(h, sharedAlbumName)
704
+
705
+	if not sharedAlbumInfo then
706
+		writeLogfile(3, string.format('removePhotosFromSharedAlbumIfExists(%s, %d photos): Shared album not found, returning OK.\n', sharedAlbumName, #photos))
707
+		return true
708
+	end
709
+
710
+	local success, errorCode = PSPhotoStationAPI.removePhotosFromSharedAlbum(h, sharedAlbumName, photos)
711
+
712
+	if not success and errorCode == 555 then
713
+		-- shared album was deleted, mapping wasn't up to date
714
+		sharedAlbumsCacheUpdate(h)
715
+		writeLogfile(3, string.format('removePhotosFromSharedAlbumIfExists(%s, %d photos): Shared album already deleted, returning OK.\n', sharedAlbumName, #photos))
716
+		return true
717
+	end
718
+
719
+	if not success then return false end
720
+
721
+	writeLogfile(3, string.format('removePhotosFromSharedAlbumIfExists(%s, %d photos) returns OK.\n', sharedAlbumName, #photos))
722
+	return true
723
+end
724
+
725
+---------------------------------------------------------------------------------------------------------
726
+-- deleteEmptyAlbumAndParents(h, albumPath)
727
+-- delete an album and all its parents as long as they are empty
728
+-- return count of deleted albums
729
+function PSPhotoStationUtils.deleteEmptyAlbumAndParents(h, albumPath)
730
+	local nDeletedAlbums = 0
731
+	local currentAlbumPath
732
+
733
+	currentAlbumPath = albumPath
734
+	while currentAlbumPath do
735
+		local photoInfos, errorCode =  PSPhotoStationAPI.listAlbum(h, currentAlbumPath, 'photo,video,album')
736
+
737
+    	-- if not existing or not empty or delete fails, we are ready
738
+    	if 		not photoInfos
739
+    		or 	#photoInfos > 0
740
+    		or not PSPhotoStationAPI.deleteAlbum (h, currentAlbumPath)
741
+    	then
742
+    		writeLogfile(3, string.format('deleteEmptyAlbumAndParents(%s) not deleted.\n', currentAlbumPath))
743
+    		return nDeletedAlbums
744
+    	end
745
+
746
+   		writeLogfile(2, string.format('deleteEmptyAlbumAndParents(%s) was empty: deleted.\n', currentAlbumPath))
747
+		nDeletedAlbums = nDeletedAlbums + 1
748
+		currentAlbumPath = string.match(currentAlbumPath , '(.+)\/[^\/]+')
749
+	end
750
+
751
+	return nDeletedAlbums
752
+end
753
+
754
+---------------------------------------------------------------------------------------------------------
755
+-- rating2Stars (h, dstFilename, isVideo, field, value)
756
+function PSPhotoStationUtils.rating2Stars(rating)
757
+	return string.rep ('*', rating)
758
+end

+ 3
- 0
PhotoStation API/webapi/query.conf.php 查看文件

@@ -0,0 +1,3 @@
1
+<?php
2
+/* Copyright (c) 2000-2012 Synology Inc. All rights reserved. */
3
+	define('QUERY_CONF_WEBAPI_SERVICE_INCLUDE_PATH', dirname(__FILE__).'/webapi.inc.php');

+ 8
- 0
PhotoStation API/webapi/query.inc.php 查看文件

@@ -0,0 +1,8 @@
1
+<?php
2
+/* Copyright (c) 2000-2012 Synology Inc. All rights reserved. */
3
+	require_once('query.conf.php');
4
+	require_once(QUERY_CONF_WEBAPI_SERVICE_INCLUDE_PATH);
5
+	require_once(SZ_WEBAPI_CLASS_PATH);
6
+
7
+	define('SZ_QUERY_API_DESCRIPTION', 'Query.api');
8
+	define('SZ_QUERY_API_DESCRIPTION_PATH', SZ_WEBAPI_API_DESCRIPTION_DIR.'/'.SZ_QUERY_API_DESCRIPTION);

+ 88
- 0
PhotoStation API/webapi/query.php 查看文件

@@ -0,0 +1,88 @@
1
+<?php
2
+/* Copyright (c) 2000-2012 Synology Inc. All rights reserved. */
3
+require_once 'query.inc.php';
4
+
5
+class QueryAPI extends WebAPI
6
+{
7
+	public function __construct()
8
+	{
9
+		parent::__construct(SZ_QUERY_API_DESCRIPTION_PATH);
10
+	}
11
+
12
+	protected function Process()
13
+	{
14
+		if (!strcasecmp($this->method, "query")) {
15
+			$this->Query();
16
+		}
17
+	}
18
+
19
+	public static function CheckAPIDescription($apiDesc)
20
+	{
21
+		if (!is_string($apiDesc[SZK_API_PATH]) || !is_int($apiDesc[SZK_API_MIN_VERSION]) || !is_int($apiDesc[SZK_API_MAX_VERSION])) {
22
+			return false;
23
+		}
24
+
25
+		return true;
26
+	}
27
+
28
+	public static function GetAPIDescriptions()
29
+	{
30
+		$allApiDescs = array();
31
+		$apiFiles = glob(CONF_API_DESCRIPTION_DIR.'/*.api', GLOB_MARK);
32
+		foreach ($apiFiles as $file) {
33
+			$jsonString = file_get_contents($file);
34
+			$apiDescs = json_decode($jsonString, true);
35
+			foreach ($apiDescs as $apiName => $apiDesc) {
36
+				if (!self::CheckAPIDescription($apiDesc)) {
37
+					WebAPIUtil::Log("API desc error. api=$apiName, desc=$apiDesc");
38
+					continue;
39
+				}
40
+
41
+				$allApiDescs[$apiName][SZK_API_PATH] = $apiDesc[SZK_API_PATH];
42
+				$allApiDescs[$apiName][SZK_API_MIN_VERSION] = $apiDesc[SZK_API_MIN_VERSION];
43
+				$allApiDescs[$apiName][SZK_API_MAX_VERSION] = $apiDesc[SZK_API_MAX_VERSION];
44
+			}
45
+		}
46
+
47
+		return $allApiDescs;
48
+	}
49
+
50
+	public function Query()
51
+	{
52
+		$allApiDescs = self::GetAPIDescriptions();
53
+		$queryedApiDescs = array();
54
+
55
+		if (!isset($_REQUEST['query']) || !strcasecmp($_REQUEST['query'], 'all')) {
56
+			$this->SetResponse($allApiDescs);
57
+			return;
58
+		}
59
+
60
+		$queries = WebAPIUtil::ParseToArray($_REQUEST['query'], ",");
61
+		foreach ($queries as $query) {
62
+			$isPrefix = (0 === substr_compare($query, '.', -1, 1)); // end with '.'
63
+			if ($isPrefix) {
64
+				foreach ($allApiDescs as $apiName => $apiDesc) {
65
+					if (!strncmp($apiName, $query, strlen($query))) {
66
+						$queryedApiDescs[$apiName] = $apiDesc;
67
+					}
68
+				}
69
+			} else {
70
+				if (isset($allApiDescs[$query])) {
71
+					$queryedApiDescs[$query] = $allApiDescs[$query];
72
+				}
73
+			}
74
+		}
75
+
76
+		if (empty($queryedApiDescs)) {
77
+			// convert empty array to empty object for json output
78
+			$queryedApiDescs = json_decode('{}');
79
+		}
80
+
81
+		$this->SetResponse($queryedApiDescs);
82
+
83
+		return;
84
+	}
85
+}
86
+
87
+$api = new QueryAPI();
88
+$api->Run();

+ 3
- 0
PhotoStation API/webapi/rotate.conf.php 查看文件

@@ -0,0 +1,3 @@
1
+<?php
2
+    define('ROTATE_CONF_WEBAPI_SERVICE_INCLUDE_PATH', dirname(__FILE__).'/webapi.inc.php');
3
+?>

+ 8
- 0
PhotoStation API/webapi/rotate.inc.php 查看文件

@@ -0,0 +1,8 @@
1
+<?php
2
+    require_once('rotate.conf.php');
3
+    require_once("../include/photo/rotate_pic.php");
4
+    require_once("../include/syno_conf.php");
5
+
6
+    require_once(ROTATE_CONF_WEBAPI_SERVICE_INCLUDE_PATH);
7
+    require_once(SZ_WEBAPI_CLASS_PATH);
8
+?>

+ 88
- 0
PhotoStation API/webapi/rotate.php 查看文件

@@ -0,0 +1,88 @@
1
+<?php
2
+
3
+require_once('rotate.inc.php');
4
+
5
+class RotateAPI extends WebAPI {
6
+    function __construct()
7
+    {
8
+        parent::__construct(SZ_WEBAPI_API_DESCRIPTION_PATH);
9
+    }
10
+
11
+    protected function Process()
12
+    {
13
+        csSYNOPhotoDB::GetDBInstance()->SetSessionCache(true);
14
+
15
+        csSYNOPhotoMisc::CheckSessionTimeOut(true);
16
+
17
+        if (!strcasecmp($this->method, "set")) {
18
+            $this->Set();
19
+        }
20
+    }
21
+
22
+    private function GetParams_Set()
23
+    {
24
+        if (!isset($_REQUEST['id']) || !isset($_REQUEST['rotate'])) {
25
+            return false;
26
+        }
27
+        if (!is_numeric($_REQUEST['rotate'])) {
28
+            return false;
29
+        }
30
+        if (!in_array($_REQUEST['rotate'], array('90', '180', '270'))) {
31
+            return false;
32
+        }
33
+        $params['rotate'] = (int)$_REQUEST['rotate'];
34
+
35
+        $arr = explode(',', $_REQUEST['id']);
36
+        $params['ids'] = array();
37
+        foreach ($arr as $item) {
38
+            unset($id);
39
+            $split = explode('_', $item);
40
+            if (3 !== count($split)) {
41
+                return false;
42
+            }
43
+            if (!in_array($split[0], array('photo'))) {
44
+                return false;
45
+            }
46
+            $dirName = @pack('H*', $split[1]);
47
+            $fileName = @pack('H*', $split[2]);
48
+            $filePath = SYNOPHOTO_SERVICE_REAL_DIR . "/" . ("/" === $dirName ? "" : $dirName . "/") . $fileName;
49
+            if (!csSYNOPhotoMisc::CheckPathValid($filePath)) {
50
+                return false;
51
+            }
52
+            $id['fullPath'] = $filePath;
53
+            $id['albumName'] = $dirName;
54
+            array_push($params['ids'], $id);
55
+        }
56
+        return $params;
57
+    }
58
+
59
+    private function Set()
60
+    {
61
+        if (false === ($params = $this->GetParams_Set())) {
62
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
63
+            goto End;
64
+        }
65
+        // check album maganeable
66
+        foreach ($params['ids'] as $item) {
67
+            if (false === csSynoPhotoMisc::CheckAlbumManageable($item['albumName'])) {
68
+                $this->SetError(PHOTOSTATION_ROTATE_ACCESS_DENY);
69
+                goto End;
70
+            }
71
+        }
72
+        // rotate
73
+        foreach ($params['ids'] as $item) {
74
+            $retJson = SYNOPHOTO_ROTATE_PIC_DoRotation($params['rotate'], $item['fullPath']);
75
+            $ret = json_decode($retJson, true);
76
+            if (false === $ret['success']) {
77
+                $this->SetError(PHOTOSTATION_ROTATE_SET_FAIL);
78
+                goto End;
79
+            }
80
+        }
81
+    End:
82
+        return;
83
+    }
84
+}
85
+
86
+$api = new RotateAPI();
87
+$api->Run();
88
+?>

+ 28
- 0
PhotoStation API/webapi/sdk/WebAPI.inc.php 查看文件

@@ -0,0 +1,28 @@
1
+<?php
2
+/* Copyright (c) 2000-2012 Synology Inc. All rights reserved. */
3
+	
4
+	define('SZK_PARAM_API',	'api');
5
+	define('SZK_PARAM_METHOD', 'method');
6
+	define('SZK_PARAM_VERSION',	'version');
7
+
8
+	define('SZK_API_PATH',	'path');
9
+	define('SZK_API_MIN_VERSION', 'minVersion');
10
+	define('SZK_API_MAX_VERSION', 'maxVersion');
11
+	define('SZK_API_METHODS', 'methods');
12
+	
13
+	define('SZK_RESP_SUCCESS', 'success');
14
+	define('SZK_RESP_DATA', 'data');
15
+	define('SZK_RESP_ERROR', 'error');
16
+	define('SZK_RESP_ERROR_CODE', 'code');
17
+	
18
+	define('WEBAPI_ERR_NONE', 0);
19
+	define('WEBAPI_ERR_UNKNOWN', 100);
20
+	define('WEBAPI_ERR_BAD_REQUEST', 101);
21
+	define('WEBAPI_ERR_NO_SUCH_API', 102);
22
+	define('WEBAPI_ERR_NO_SUCH_METHOD', 103);
23
+	define('WEBAPI_ERR_NOT_SUPPORTED_VERSION', 104);
24
+	define('WEBAPI_ERR_NO_PERMISSION', 105);
25
+	define('WEBAPI_ERR_SESSION_TIMEOUT', 106);
26
+	define('WEBAPI_ERR_SESSION_INTERRUPT', 107);
27
+	define('WEBAPI_ERR_CUSTOMIZED_MIN', 400);
28
+?>

+ 188
- 0
PhotoStation API/webapi/sdk/WebAPI.php 查看文件

@@ -0,0 +1,188 @@
1
+<?php
2
+/* Copyright (c) 2000-2012 Synology Inc. All rights reserved. */
3
+require_once('WebAPI.inc.php');
4
+require_once('WebAPIUtil.php');
5
+
6
+abstract class WebAPI {
7
+	private $apiDesc;
8
+	private $resp;
9
+	private $error;
10
+	private $headerSent = false;
11
+
12
+	protected $api;
13
+	protected $method;
14
+	protected $version;
15
+	protected $idPrefix;
16
+	
17
+	function __construct($apiDescFilePath) {
18
+		if (!file_exists($apiDescFilePath)) {
19
+			WebAPIUtil::Log("api desc not found. path=$apiDescFilePath", basename(__FILE__), __LINE__);
20
+			$this->apiDesc = NULL;
21
+			goto End;
22
+		}
23
+
24
+		$jsonString = file_get_contents($apiDescFilePath);
25
+		$this->apiDesc = json_decode($jsonString, true);
26
+	End:
27
+		return;
28
+	}
29
+	
30
+	private function CheckRequestParam($api, $method, $version) {
31
+		$ret = false;
32
+		$apiDesc = $this->apiDesc[$api];
33
+		$minVersion = 0;
34
+		$maxVersion = 0;
35
+
36
+		if (NULL === $apiDesc) {
37
+			$this->SetError(WEBAPI_ERR_NO_SUCH_API);
38
+			goto End;
39
+		}
40
+
41
+		if (!is_int($version) && !ctype_digit($version)) {
42
+			$this->SetError(WEBAPI_ERR_BAD_REQUEST);
43
+			goto End;
44
+		}
45
+
46
+		if (!isset($apiDesc[SZK_API_MIN_VERSION]) || !isset($apiDesc[SZK_API_MAX_VERSION])) {
47
+			WebAPIUtil::Log("Bad API desc. api=$api", basename(__FILE__), __LINE__);
48
+			$this->SetError(WEBAPI_ERR_NO_SUCH_API);
49
+			goto End;
50
+		}
51
+
52
+		$minVersion = $apiDesc[SZK_API_MIN_VERSION];
53
+		$maxVersion = $apiDesc[SZK_API_MAX_VERSION];
54
+
55
+		if ($version < $minVersion || $maxVersion < $version) {
56
+			$this->SetError(WEBAPI_ERR_NOT_SUPPORTED_VERSION);
57
+			goto End;
58
+		}
59
+
60
+		if (!in_array($method, $apiDesc[SZK_API_METHODS][$version])) {
61
+			$this->SetError(WEBAPI_ERR_NO_SUCH_METHOD);
62
+			goto End;
63
+		}
64
+
65
+		$this->api = $api;
66
+		$this->method = $method;
67
+		$this->version = $version;
68
+
69
+		$ret = true;
70
+	End:
71
+		return $ret;
72
+	}
73
+
74
+	protected function OutputSingleSpace() {
75
+		// this function is for connection abort detection
76
+		// PHP will not detect that the user has aborted the connection until an attempt is made to send information to the client.
77
+		// http://www.php.net/manual/en/function.ignore-user-abort.php
78
+		if (!$this->headerSent) {
79
+			$this->headerSent = true;
80
+			header('Content-type: text/plain; charset=utf-8');
81
+		}
82
+		echo ' ';
83
+		ob_flush();
84
+		flush();
85
+	}
86
+	
87
+	private function OutputResponse() {
88
+		$resp = array();
89
+		
90
+		if (WEBAPI_ERR_NONE != $this->error) {
91
+			$resp[SZK_RESP_SUCCESS] = false;
92
+			$resp[SZK_RESP_ERROR][SZK_RESP_ERROR_CODE] = $this->error;
93
+			goto End;
94
+		}
95
+
96
+		$resp[SZK_RESP_SUCCESS] = true;
97
+		if (!is_null($this->resp)) {
98
+			$resp[SZK_RESP_DATA] = $this->resp;
99
+		}
100
+	End:
101
+		if (!$this->headerSent) {
102
+			$this->headerSent = true;
103
+			header('Content-type: text/plain; charset=utf-8');
104
+		}
105
+		echo(json_encode($resp));
106
+		//echo json_encode($resp, JSON_UNESCAPED_UNICODE);
107
+
108
+		// transform from unicode back to utf8                   
109
+		//echo preg_replace("#\\\u([0-9a-f]{4}+)#ie", "iconv('UCS-2', 'UTF-8', pack('H4', '\\1'))", json_encode($resp));
110
+	}
111
+
112
+	private function CheckSessionTimeout() {
113
+		$timeout = false;
114
+		if (isset($_REQUEST['ps_username']) && $_REQUEST['ps_username'] !== "") {
115
+			if (isset($_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_user'])) {
116
+				if ($_REQUEST['ps_username'] !== $_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_user']) {
117
+					$timeout = true;
118
+				}
119
+			} else {
120
+				if (!isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']) || !($_REQUEST['ps_username'] === 'admin' && SYNOPHOTO_ADMIN_USER === 'root' || $_REQUEST['ps_username'] === substr(SYNOPHOTO_ADMIN_USER, 1))) {
121
+					$timeout = true;
122
+				}
123
+			}
124
+		}
125
+		return $timeout;
126
+	}
127
+
128
+	protected function CheckPermission() {
129
+    	return true;
130
+    }
131
+
132
+	abstract protected function Process();
133
+
134
+	protected function EncodeItemId($id, $prefix = '') {
135
+		$idPrefix = $prefix ? $prefix : $this->idPrefix;
136
+		return $idPrefix.bin2hex($id);
137
+	}
138
+
139
+	protected function DecodeItemId($id, $prefix = '')
140
+	{
141
+		$idPrefix = $prefix ? $prefix : $this->idPrefix;
142
+		$arr = explode($idPrefix, $id);
143
+		if (2 !== count($arr)) {
144
+			return false;
145
+		}
146
+		return @pack('H*', $arr[1]);
147
+	}
148
+	
149
+	protected function SetResponse($resp) {
150
+		$this->resp = $resp;
151
+		$this->error = WEBAPI_ERR_NONE;
152
+	}
153
+	
154
+	protected function SetError($error) {
155
+		$this->resp = NULL;
156
+		$this->error = $error;
157
+	}
158
+   
159
+	public function Run() {
160
+		if (!isset($_REQUEST[SZK_PARAM_API]) || !isset($_REQUEST[SZK_PARAM_METHOD]) || !isset($_REQUEST[SZK_PARAM_VERSION])) {
161
+			$this->SetError(WEBAPI_ERR_BAD_REQUEST);
162
+			goto End;
163
+		}
164
+		if ($this->CheckSessionTimeout()) {
165
+			header('x-request-error: error_timeout');
166
+			goto End;
167
+		}
168
+		
169
+		$api = $_REQUEST[SZK_PARAM_API];
170
+		$method = $_REQUEST[SZK_PARAM_METHOD];
171
+		$version = $_REQUEST[SZK_PARAM_VERSION];
172
+
173
+		if (!$this->CheckRequestParam($api, $method, $version)) {
174
+			goto End;
175
+		}
176
+
177
+		if (!$this->CheckPermission()) {
178
+			goto End;
179
+		}
180
+
181
+		$this->Process();
182
+
183
+	End:
184
+		$this->OutputResponse();
185
+		return;
186
+	}
187
+}
188
+?>

+ 62
- 0
PhotoStation API/webapi/sdk/WebAPIUtil.php 查看文件

@@ -0,0 +1,62 @@
1
+<?php
2
+class WebAPIUtil {	
3
+	static function Log($text = "", $file_name = "", $line = "0") {
4
+		if ("" === $text) {
5
+			return;
6
+		}
7
+		$file_name = basename($file_name);
8
+		syslog(LOG_ERR, "$file_name:$line $text");
9
+	}
10
+	
11
+	static function ParseToArray($string, $separator) {
12
+		$result = array();	
13
+		foreach (explode($separator, $string) as $element) {
14
+			$trimmed = trim($element);
15
+			if (!empty($trimmed)) {
16
+				$result[] = $trimmed;
17
+			}
18
+		}
19
+		
20
+		return $result;
21
+	}
22
+	
23
+	static function ParseAuthKey($authkey) {
24
+		$auth_arr = json_decode(WebAPIUtil::Decrypt($authkey), true);
25
+		$ret = false;
26
+
27
+		if (null === $auth_arr) {
28
+			goto End;
29
+		}
30
+		
31
+		if (!isset($auth_arr['myds_id']) || !isset($auth_arr['app_id']) || !isset($auth_arr['time'])) {
32
+			goto End;
33
+		}
34
+		
35
+		$ret = $auth_arr;
36
+		
37
+	End:
38
+		return $ret;
39
+	}
40
+	
41
+	static function Encrypt($str) {
42
+		// Charlie temp - wait to implement
43
+		
44
+		return base64_encode($str);
45
+	}
46
+	
47
+	static function Decrypt($str) {
48
+		// Charlie temp - wait to implement
49
+		
50
+		return base64_decode($str);
51
+	}
52
+
53
+	static function IsEMail($email) {
54
+		$pattern = '/^[a-zA-Z0-9_&%!#+-\.]+@([a-zA-Z0-9_&%!#+-\.]+)$/';
55
+		if (preg_match($pattern, $email)) {
56
+			return true;
57
+		} else {
58
+			return false;
59
+		}
60
+	}
61
+}
62
+?>

+ 3
- 0
PhotoStation API/webapi/shared_album.conf.php 查看文件

@@ -0,0 +1,3 @@
1
+<?php
2
+define('SHARED_ALBUM_CONF_WEBAPI_SERVICE_INCLUDE_PATH', dirname(__FILE__).'/webapi.inc.php');
3
+?>

+ 7
- 0
PhotoStation API/webapi/shared_album.inc.php 查看文件

@@ -0,0 +1,7 @@
1
+<?php
2
+	require_once('shared_album.conf.php');
3
+	require_once('../include/syno_conf.php');
4
+
5
+	require_once(SHARED_ALBUM_CONF_WEBAPI_SERVICE_INCLUDE_PATH);
6
+	require_once(SZ_WEBAPI_CLASS_PATH);
7
+?>

+ 775
- 0
PhotoStation API/webapi/shared_album.php 查看文件

@@ -0,0 +1,775 @@
1
+<?php
2
+require_once('shared_album.inc.php');
3
+require_once('photoutil.php');
4
+
5
+class SharedAlbumAPI extends WebAPI
6
+{
7
+	private $TableName, $PhotoTableName, $VideoTableName, $UserID;
8
+	private $allowSortByArray = array('filename' => 'upper(name)', 'share_status' => 'share_status', 'createdate' => 'create_date');
9
+
10
+	function __construct()
11
+	{
12
+		parent::__construct(SZ_WEBAPI_API_DESCRIPTION_PATH);
13
+	}
14
+
15
+	protected function Process()
16
+	{
17
+		if (!strcasecmp($this->method, "getinfo_public")) {
18
+			$this->GetInfoPublic();
19
+			return;
20
+		}
21
+		csSYNOPhotoDB::GetDBInstance()->SetSessionCache();
22
+		csSYNOPhotoMisc::CheckSessionTimeOut(true);
23
+
24
+		$this->UserID = isset($_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_userid']) ? $_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_userid'] : 0;
25
+		if ($this->UserID === 0) {
26
+			$this->TableName = SHARED_ALBUM_ADMIN_TABLE_NAME;
27
+			$this->PhotoTableName = SHARED_ALBUM_PHOTO_ADMIN_TABLE_NAME;
28
+			$this->VideoTableName = SHARED_ALBUM_VIDEO_ADMIN_TABLE_NAME;
29
+		} else {
30
+			$this->TableName = SHARED_ALBUM_TABLE_NAME;
31
+			$this->PhotoTableName = SHARED_ALBUM_PHOTO_TABLE_NAME;
32
+			$this->VideoTableName = SHARED_ALBUM_VIDEO_TABLE_NAME;
33
+		}
34
+
35
+		if (!strcasecmp($this->method, "list")) {
36
+			$this->SharedAlbumList();
37
+		} else if (!strcasecmp($this->method, "getinfo")) {
38
+			$this->GetInfo();
39
+		} else if (!strcasecmp($this->method, "create")) {
40
+			$this->Create();
41
+		} else if (!strcasecmp($this->method, "edit")) {
42
+			$this->Edit();
43
+		} else if (!strcasecmp($this->method, "delete")) {
44
+			$this->Delete();
45
+		} else if (!strcasecmp($this->method, "add_items")) {
46
+			$this->AddItems();
47
+		} else if (!strcasecmp($this->method, "remove_items")) {
48
+			$this->RemoveItems();
49
+		} else if (!strcasecmp($this->method, "edit_public_share")) {
50
+			$this->EditPublicShare();
51
+		} else if (!strcasecmp($this->method, "get_single_item")) {
52
+			$this->GetSingleItem();
53
+		} else if (!strcasecmp($this->method, "set_single_item")) {
54
+			$this->SetSingleItem();
55
+		}
56
+	}
57
+
58
+	private function SharedAlbumList()
59
+	{
60
+		$ret = false;
61
+
62
+		if (!isset($_REQUEST['offset']) || !isset($_REQUEST['limit']) || !is_numeric($_REQUEST['offset']) || !is_numeric($_REQUEST['limit'])) {
63
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_BAD_PARAMS);
64
+			goto End;
65
+		}
66
+		$offset = $_REQUEST['offset'] + 0;
67
+		$limit = $_REQUEST['limit'] + 0;
68
+		$sortBy = $this->allowSortByArray['filename'];
69
+		$sortDirection = 'ASC';
70
+		$additional = array();
71
+
72
+		if (isset($_REQUEST['sort_by']) && array_key_exists($_REQUEST['sort_by'], $this->allowSortByArray)) {
73
+			$sortBy = $this->allowSortByArray[$_REQUEST['sort_by']];
74
+		}
75
+		if (isset($_REQUEST['sort_direction']) && strtolower($_REQUEST['sort_direction']) === 'desc') {
76
+			$sortDirection = 'DESC';
77
+		}
78
+		if (isset($_REQUEST['additional'])) {
79
+			$additional = explode(",", $_REQUEST['additional']);
80
+		}
81
+
82
+		$query = "SELECT COUNT(*) FROM " . $this->TableName . " WHERE hidden = 'f' AND ";
83
+		$sqlParams = array();
84
+		$this->appendUserIDCond($query, $sqlParams);
85
+		$db_result = PHOTO_DB_Query($query, $sqlParams);
86
+		$row = PHOTO_DB_FetchRow($db_result);
87
+
88
+		$total = intval($row[0]) + 1; // 1 is sharedalbum_single
89
+
90
+		$query = "SELECT id FROM " . $this->TableName . " WHERE hidden = 'f' AND ";
91
+		$sqlParams = array();
92
+		$this->appendUserIDCond($query, $sqlParams);
93
+
94
+		// we will add sharedalbum_single in this method,
95
+		// so regulate the offset/limit to get correct shared_album information
96
+		if ($offset === 0) {
97
+			$shared_limit = $limit -1;
98
+			$shared_offset = 0;
99
+		} else {
100
+			$shared_limit = $limit;
101
+			$shared_offset = $offset - 1;
102
+		}
103
+		$limitOffsetString = PHOTO_DB_GetLimitOffsetString($shared_limit, $shared_offset);
104
+		if ('share_status' === $sortBy) {
105
+			$currentDate = 'root' === SYNOPHOTO_ADMIN_USER ? 'current_date' : "date('now')";
106
+			$query .= "ORDER BY CASE " .
107
+				"WHEN is_shared = 'f' THEN 0 " . // none
108
+				"WHEN start_time IS NULL OR end_time IS NULL THEN 2 ". // valid
109
+				"WHEN start_time > $currentDate OR end_time < $currentDate THEN 1 ". // invalid
110
+				"ELSE 2 ". // valid
111
+				"END $sortDirection, upper(name) ASC";
112
+		} else {
113
+			$query .= "ORDER BY $sortBy $sortDirection";
114
+		}
115
+		$query .= " $limitOffsetString";
116
+		$db_result = PHOTO_DB_Query($query, $sqlParams);
117
+
118
+		$result['items'] = array();
119
+		$i = 0;
120
+		if ((int)$offset === 0) {
121
+			$sharedAlbum = SharedAlbum::GetHiddenAlbumInfo(NULL, $additional);
122
+			if ($sharedAlbum !== NULL) {
123
+				$sharedAlbum = $this->GetInfoById(explode("_", $sharedAlbum['id'])[1], $additional);
124
+			}
125
+			if ($sharedAlbum !== NULL) {
126
+				$sharedAlbum['id'] = 'sharedalbum_single';
127
+				if (isset($sharedAlbum['additional']['public_share']['public_share_url'])) {
128
+					unset($sharedAlbum['additional']['public_share']['public_share_url']);
129
+				}
130
+				$result['items'][$i] = $sharedAlbum;
131
+				$i ++;
132
+			}
133
+		}
134
+
135
+		while(($idRow = PHOTO_DB_FetchRow($db_result))) {
136
+			$sharedAlbum = $this->GetInfoById($idRow[0], $additional);
137
+			if (NULL === $sharedAlbum) {
138
+				continue;
139
+			}
140
+			$result['items'][$i] = $sharedAlbum;
141
+			$i ++;
142
+		}
143
+
144
+		$result['total'] = $total;
145
+		$result['offset'] = (-1 == $limit || $offset + $limit > $total) ? $total : $offset + $limit;
146
+
147
+		$this->SetResponse($result);
148
+		$ret = true;
149
+	End:
150
+		return $ret;
151
+	}
152
+
153
+	private function GetInfo()
154
+	{
155
+		$ret = false;
156
+		if (!isset($_REQUEST['id'])) {
157
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_BAD_PARAMS);
158
+			goto End;
159
+		}
160
+
161
+		$additional = array();
162
+		if (isset($_REQUEST['additional'])) {
163
+			$additional = explode(',', $_REQUEST['additional']);
164
+		}
165
+
166
+		$ids = explode(',', $_REQUEST['id']);
167
+		$result['shared_albums'] = array();
168
+		$success_count = 0;
169
+		foreach ($ids as $id) {
170
+			$id_arr = explode("_", $id);
171
+			if (2 !== count($id_arr) || 'sharedalbum' != $id_arr[0]) {
172
+				continue;
173
+			}
174
+			if ($id_arr[1] === 'single') {
175
+				$sharedAlbum = SharedAlbum::GetHiddenAlbumInfo(NULL, $additional);
176
+				$sharedAlbum['id'] = 'sharedalbum_single';
177
+				if (isset($sharedAlbum['additional']['public_share']['public_share_url'])) {
178
+					unset($sharedAlbum['additional']['public_share']['public_share_url']);
179
+				}
180
+				$result['shared_albums'][] = $sharedAlbum;
181
+				$success_count ++;
182
+				continue;
183
+			}
184
+			$sharedAlbumId = $id_arr[1];
185
+
186
+			if (FALSE === $this->CheckExistenceById($sharedAlbumId)) {
187
+				continue;
188
+			}
189
+
190
+			$sharedAlbum = $this->GetInfoById($sharedAlbumId, $additional);
191
+			if (NULL === $sharedAlbum) {
192
+				continue;
193
+			}
194
+			$result['shared_albums'][] = $sharedAlbum;
195
+			$success_count ++;
196
+		}
197
+
198
+		if ($success_count === 0) {
199
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_GET_INFO_ERROR);
200
+			goto End;
201
+		}
202
+
203
+		$this->SetResponse($result);
204
+		$ret = true;
205
+	End:
206
+		return $ret;
207
+	}
208
+
209
+	private function Create()
210
+	{
211
+		$ret = false;
212
+		if (!isset($_REQUEST['name'])) {
213
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_BAD_PARAMS);
214
+			goto End;
215
+		}
216
+		$name = $_REQUEST['name'];
217
+
218
+		$itemIds = array();
219
+		if (isset($_REQUEST['item_id'])) {
220
+			$itemIds = explode(",", $_REQUEST['item_id']);
221
+		}
222
+
223
+		// check existence
224
+		if (FALSE !== $this->CheckExistenceByName($name)) {
225
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_HAS_EXISTED);
226
+			goto End;
227
+		}
228
+
229
+		$sqlParams = array();
230
+		if ($this->UserID !== 0) {
231
+			$query = "INSERT INTO " . $this->TableName . " (userid, name, is_shared, hidden) VALUES (?, ?, 'f', 'f')";
232
+			$sqlParams = array($this->UserID, $name);
233
+		} else {
234
+			$query = "INSERT INTO " . $this->TableName . " (name, is_shared, hidden) VALUES (?, 'f', 'f')";
235
+			$sqlParams = array($name);
236
+		}
237
+		$db_result = PHOTO_DB_Query($query, $sqlParams);
238
+
239
+		if (FALSE === ($sharedAlbumId = $this->CheckExistenceByName($name))) {
240
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_CREATE_FAIL);
241
+			goto End;
242
+		}
243
+
244
+		foreach($itemIds as $itemId) {
245
+			$id_arr = explode('_', $itemId);
246
+			if (3 !== count($id_arr) || ($id_arr[0] !== 'photo' && $id_arr[0] !== 'video')) {
247
+				continue;
248
+			}
249
+			$type = $id_arr[0];
250
+
251
+			$albumName = @pack('H*', $id_arr[1]);
252
+			$fileName = @pack('H*', $id_arr[2]);
253
+			if (!csSYNOPhotoMisc::CheckAlbumAccessible($albumName)) {
254
+				return false;
255
+			}
256
+			if ('/' === $albumName) {
257
+				$filePath = $fileName;
258
+			} else {
259
+				$filePath = $albumName.'/'.$fileName;
260
+			}
261
+
262
+			$dbId = csSYNOPhotoMisc::GetPhotoVideoDBId($filePath, $type);
263
+			if (FALSE === $dbId) {
264
+				continue;
265
+			}
266
+			$this->AddItemQuery($type, $sharedAlbumId, $dbId);
267
+		}
268
+
269
+		$result['id'] = 'sharedalbum_'.$sharedAlbumId;
270
+		$this->SetResponse($result);
271
+		$ret = true;
272
+	End:
273
+		return $ret;
274
+	}
275
+
276
+	private function Edit()
277
+	{
278
+		$ret = false;
279
+		if (!isset($_REQUEST['id']) || !isset($_REQUEST['name'])) {
280
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_BAD_PARAMS);
281
+			goto End;
282
+		}
283
+		$id_arr = explode("_", $_REQUEST['id']);
284
+		if (2 !== count($id_arr) || 'sharedalbum' != $id_arr[0]) {
285
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_BAD_PARAMS);
286
+			goto End;
287
+		}
288
+		$sharedAlbumId = $id_arr[1];
289
+		$name = $_REQUEST['name'];
290
+
291
+		if (FALSE === $this->CheckExistenceById($sharedAlbumId)) {
292
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_NOT_EXISTS);
293
+			goto End;
294
+		}
295
+
296
+		if (FALSE !== $this->CheckExistenceByName($name, $sharedAlbumId)) {
297
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_HAS_EXISTED);
298
+			goto End;
299
+		}
300
+
301
+		$sqlParams = array($name, $sharedAlbumId);
302
+		$query = "UPDATE " . $this->TableName . " SET name = ? WHERE id = ? AND ";
303
+		$this->appendUserIDCond($query, $sqlParams);
304
+		$db_result = PHOTO_DB_Query($query, $sqlParams);
305
+
306
+		$ret = true;
307
+	End:
308
+		return $ret;
309
+	}
310
+
311
+	private function Delete()
312
+	{
313
+		$ret = false;
314
+		if (!isset($_REQUEST['id'])) {
315
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_BAD_PARAMS);
316
+			goto End;
317
+		}
318
+		$ids = explode(",", $_REQUEST['id']);
319
+
320
+		$success_count = 0;
321
+		foreach ($ids as $id) {
322
+			$id_arr = explode("_", $id);
323
+			if (2 !== count($id_arr) || 'sharedalbum' != $id_arr[0]) {
324
+				continue;
325
+			}
326
+			$sharedAlbumId = $id_arr[1];
327
+
328
+			if (FALSE === $this->CheckExistenceById($sharedAlbumId)) {
329
+				continue;
330
+			}
331
+
332
+			$sqlParams = array($sharedAlbumId);
333
+			$query = "DELETE FROM " . $this->TableName . " WHERE id = ? AND ";
334
+			$this->appendUserIDCond($query, $sqlParams);
335
+			$db_result = PHOTO_DB_Query($query, $sqlParams);
336
+			$success_count ++;
337
+		}
338
+		if ($success_count === 0) {
339
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_DELETE_FAIL);
340
+			goto End;
341
+		}
342
+
343
+		$ret = true;
344
+	End:
345
+		return $ret;
346
+	}
347
+
348
+	private function AddItems()
349
+	{
350
+		$ret = false;
351
+		if (!isset($_REQUEST['id']) || !isset($_REQUEST['item_id'])) {
352
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_BAD_PARAMS);
353
+			goto End;
354
+		}
355
+		$id_arr = explode("_", $_REQUEST['id']);
356
+		if (2 !== count($id_arr) || 'sharedalbum' != $id_arr[0]) {
357
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_BAD_PARAMS);
358
+			goto End;
359
+		}
360
+		$sharedAlbumId = $id_arr[1];
361
+		$itemIds = explode(",", $_REQUEST['item_id']);
362
+
363
+		if (FALSE === $this->CheckExistenceById($sharedAlbumId)) {
364
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_NOT_EXISTS);
365
+			goto End;
366
+		}
367
+
368
+		foreach($itemIds as $itemId) {
369
+			$id_arr = explode('_', $itemId);
370
+			if (3 !== count($id_arr) || ($id_arr[0] !== 'photo' && $id_arr[0] !== 'video')) {
371
+				continue;
372
+			}
373
+			$type = $id_arr[0];
374
+
375
+			$albumName = @pack('H*', $id_arr[1]);
376
+			$fileName = @pack('H*', $id_arr[2]);
377
+			if (!csSYNOPhotoMisc::CheckAlbumAccessible($albumName)) {
378
+				return false;
379
+			}
380
+			if ('/' === $albumName) {
381
+				$filePath = $fileName;
382
+			} else {
383
+				$filePath = $albumName.'/'.$fileName;
384
+			}
385
+
386
+			$dbId = csSYNOPhotoMisc::GetPhotoVideoDBId($filePath, $type);
387
+			if (FALSE === $dbId) {
388
+				continue;
389
+			}
390
+			$this->AddItemQuery($type, $sharedAlbumId, $dbId);
391
+		}
392
+
393
+		$ret = true;
394
+	End:
395
+		return $ret;
396
+	}
397
+
398
+	private function RemoveItems()
399
+	{
400
+		$ret = false;
401
+		if (!isset($_REQUEST['id']) || !isset($_REQUEST['item_id'])) {
402
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_BAD_PARAMS);
403
+			goto End;
404
+		}
405
+		$id_arr = explode("_", $_REQUEST['id']);
406
+		if (2 !== count($id_arr) || 'sharedalbum' != $id_arr[0]) {
407
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_BAD_PARAMS);
408
+			goto End;
409
+		}
410
+		if ($id_arr[1] === 'single') {
411
+			$sharedAlbum = SharedAlbum::GetHiddenAlbumInfo();
412
+			$id_arr[1] = explode("_", $sharedAlbum['id'])[1];
413
+		}
414
+		$sharedAlbumId = $id_arr[1];
415
+		$itemIds = explode(",", $_REQUEST['item_id']);
416
+
417
+		if (FALSE === $this->CheckExistenceById($sharedAlbumId)) {
418
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_NOT_EXISTS);
419
+			goto End;
420
+		}
421
+
422
+		foreach($itemIds as $itemId) {
423
+			$id_arr = explode('_', $itemId);
424
+			if (3 !== count($id_arr) || ($id_arr[0] !== 'photo' && $id_arr[0] !== 'video')) {
425
+				continue;
426
+			}
427
+			$type = $id_arr[0];
428
+
429
+			$albumName = @pack('H*', $id_arr[1]);
430
+			$fileName = @pack('H*', $id_arr[2]);
431
+			if (!csSYNOPhotoMisc::CheckAlbumAccessible($albumName)) {
432
+				return false;
433
+			}
434
+			if ('/' === $albumName) {
435
+				$filePath = $fileName;
436
+			} else {
437
+				$filePath = $albumName.'/'.$fileName;
438
+			}
439
+
440
+			$dbId = csSYNOPhotoMisc::GetPhotoVideoDBId($filePath, $type);
441
+			if (FALSE === $dbId) {
442
+				continue;
443
+			}
444
+			$this->DeleteItemQuery($type, $sharedAlbumId, $dbId);
445
+		}
446
+
447
+		$ret = true;
448
+	End:
449
+		return $ret;
450
+	}
451
+
452
+	private function GetInfoPublic()
453
+	{
454
+		$ret = false;
455
+		if (!isset($_REQUEST['public_share_id'])) {
456
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_BAD_PARAMS);
457
+			goto End;
458
+		}
459
+
460
+		$sharedAlbum = SharedAlbum::GetInfoByPublicShare($_REQUEST['public_share_id']);
461
+		if (NULL === $sharedAlbum) {
462
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_GET_INFO_ERROR);
463
+			goto End;
464
+		}
465
+		if ('valid' !== $sharedAlbum['share_status']) {
466
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_ACCESS_DENY);
467
+			goto End;
468
+		}
469
+		$result['shared_album'] = $sharedAlbum;
470
+		$this->SetResponse($result);
471
+
472
+		$ret = true;
473
+	End:
474
+		return $ret;
475
+	}
476
+
477
+	private function validateDate($date) {
478
+		$d = DateTime::createFromFormat('Y-m-d', $date);
479
+		return $d && $d->format('Y-m-d') == $date;
480
+	}
481
+
482
+	private function EditPublicShare()
483
+	{
484
+		$ret = false;
485
+		if (!SharedAlbum::CheckPublicSharePermission()) {
486
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_ACCESS_DENY);
487
+			goto End;
488
+		}
489
+
490
+		if (!isset($_REQUEST['id']) || !isset($_REQUEST['is_shared'])) {
491
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_BAD_PARAMS);
492
+			goto End;
493
+		}
494
+		$id_arr = explode("_", $_REQUEST['id']);
495
+		if (2 !== count($id_arr) || 'sharedalbum' != $id_arr[0]) {
496
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_BAD_PARAMS);
497
+			goto End;
498
+		}
499
+		$sharedAlbumId = $id_arr[1];
500
+		$isShared = $_REQUEST['is_shared'] === 'true';
501
+
502
+		if (FALSE === $this->CheckExistenceById($sharedAlbumId)) {
503
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_NOT_EXISTS);
504
+			goto End;
505
+		}
506
+
507
+		$start_time = NULL;
508
+		$end_time = NULL;
509
+		if (isset($_REQUEST['start_time']) && isset($_REQUEST['end_time']) && $this->validateDate($_REQUEST['start_time']) && $this->validateDate($_REQUEST['end_time'])) {
510
+			$start_time = $_REQUEST['start_time'];
511
+			$end_time = $_REQUEST['end_time'];
512
+			$startDate = new DateTime($start_time);
513
+			$endDate = new DateTime($end_time);
514
+			if ($startDate > $endDate) {
515
+				$this->SetError(PHOTOSTATION_SHARED_ALBUM_BAD_PARAMS);
516
+				goto End;
517
+			}
518
+		}
519
+
520
+		if (NULL === ($shareLink = $this->GetShareLinkById($sharedAlbumId)) && $isShared) {
521
+			do {
522
+				$shareLink = SharedAlbum::GetRandomString();
523
+			} while (SharedAlbum::CheckExistenceBySharelink($shareLink));
524
+		}
525
+
526
+		$sqlParams = array(
527
+			$isShared ? 't' : 'f',
528
+			$shareLink,
529
+			$start_time,
530
+			$end_time,
531
+			$sharedAlbumId
532
+		);
533
+		$query = "UPDATE " . $this->TableName . " SET is_shared = ?, sharelink = ?, start_time = ?, end_time = ? WHERE id = ? AND ";
534
+		$this->appendUserIDCond($query, $sqlParams);
535
+		$db_result = PHOTO_DB_Query($query, $sqlParams);
536
+
537
+		$info = $this->GetInfoById($sharedAlbumId, array('public_share'));
538
+		if ($info === NULL) {
539
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_GET_INFO_ERROR);
540
+			goto End;
541
+		}
542
+		$this->SetResponse($info['additional']['public_share']);
543
+		$ret = true;
544
+	End:
545
+		return $ret;
546
+	}
547
+
548
+	private function GetSingleItem()
549
+	{
550
+		$ret = false;
551
+		if (!SharedAlbum::CheckPublicSharePermission()) {
552
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_ACCESS_DENY);
553
+			goto End;
554
+		}
555
+
556
+		if (!isset($_REQUEST['item_id'])) {
557
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_BAD_PARAMS);
558
+			goto End;
559
+		}
560
+		$id_arr = explode("_", $_REQUEST['item_id']);
561
+		if (3 !== count($id_arr) || ('photo' !== $id_arr[0] && 'video' !== $id_arr[0])) {
562
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_BAD_PARAMS);
563
+			goto End;
564
+		}
565
+		$type = $id_arr[0];
566
+
567
+		$albumName = @pack('H*', $id_arr[1]);
568
+		$fileName = @pack('H*', $id_arr[2]);
569
+		if (!csSYNOPhotoMisc::CheckAlbumAccessible($albumName)) {
570
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_ACCESS_DENY);
571
+			goto End;
572
+		}
573
+
574
+		$hiddenAlbum = SharedAlbum::GetHiddenAlbumInfo();
575
+		if ($hiddenAlbum === NULL) {
576
+			$result['is_shared'] = false;
577
+		} else {
578
+			$collectionid = explode("_", $hiddenAlbum['id'])[1];
579
+			$table = ('photo' === $type) ? $this->PhotoTableName : $this->VideoTableName;
580
+
581
+			if ('/' === $albumName) {
582
+				$filePath = $fileName;
583
+			} else {
584
+				$filePath = $albumName.'/'.$fileName;
585
+			}
586
+			$dbId = csSYNOPhotoMisc::GetPhotoVideoDBId($filePath, $type);
587
+			if (FALSE === $dbId) {
588
+				$this->SetError(PHOTOSTATION_SHARED_ALBUM_BAD_PARAMS);
589
+				goto End;
590
+			}
591
+			$query = "SELECT COUNT(*) FROM " . $table . " WHERE collectionid = ? AND {$type}id = ?";
592
+			$sqlParams = array($collectionid, $dbId);
593
+			$db_result = PHOTO_DB_Query($query, $sqlParams);
594
+			$row = PHOTO_DB_FetchRow($db_result);
595
+			if (1 !== (int)$row[0]) {
596
+				$result['is_shared'] = false;
597
+			} else {
598
+				$result['is_shared'] = true;
599
+				$result['public_share_url'] = csSYNOPhotoMisc::GetServerHost(true) . SYNOPHOTO_URL_PREFIX . '/photo/share/' . $hiddenAlbum['additional']['public_share']['shareid'] . '/' . $_REQUEST['item_id'];
600
+			}
601
+		}
602
+
603
+		$this->SetResponse($result);
604
+		$ret = true;
605
+	End:
606
+		return $ret;
607
+	}
608
+
609
+	private function SetSingleItem()
610
+	{
611
+		$ret = false;
612
+		if (!SharedAlbum::CheckPublicSharePermission()) {
613
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_ACCESS_DENY);
614
+			goto End;
615
+		}
616
+
617
+		if (!isset($_REQUEST['item_id']) || !isset($_REQUEST['enable'])) {
618
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_BAD_PARAMS);
619
+			goto End;
620
+		}
621
+		$id_arr = explode("_", $_REQUEST['item_id']);
622
+		if (3 !== count($id_arr) || ('photo' !== $id_arr[0] && 'video' !== $id_arr[0])) {
623
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_BAD_PARAMS);
624
+			goto End;
625
+		}
626
+		$type = $id_arr[0];
627
+
628
+		$albumName = @pack('H*', $id_arr[1]);
629
+		$fileName = @pack('H*', $id_arr[2]);
630
+		if (!csSYNOPhotoMisc::CheckAlbumAccessible($albumName)) {
631
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_ACCESS_DENY);
632
+			goto End;
633
+		}
634
+
635
+		$hiddenAlbum = SharedAlbum::GetHiddenAlbumInfo();
636
+		if ($hiddenAlbum === NULL) {
637
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_GET_INFO_ERROR);
638
+			goto End;
639
+		}
640
+
641
+		if ('/' === $albumName) {
642
+			$filePath = $fileName;
643
+		} else {
644
+			$filePath = $albumName.'/'.$fileName;
645
+		}
646
+		$dbId = csSYNOPhotoMisc::GetPhotoVideoDBId($filePath, $type);
647
+		if (FALSE === $dbId) {
648
+			$this->SetError(PHOTOSTATION_SHARED_ALBUM_BAD_PARAMS);
649
+			goto End;
650
+		}
651
+
652
+		$collectionid = explode("_", $hiddenAlbum['id'])[1];
653
+		$sqlParams = array($collectionid, $dbId);
654
+		if ($_REQUEST['enable'] === "true") {
655
+			$this->AddItemQuery($type, $collectionid, $dbId);
656
+			$result['is_shared'] = true;
657
+			$result['public_share_url'] = csSYNOPhotoMisc::GetServerHost(true) . SYNOPHOTO_URL_PREFIX . '/photo/share/' . $hiddenAlbum['additional']['public_share']['shareid'] . '/' . $_REQUEST['item_id'];
658
+		} else {
659
+			$this->DeleteItemQuery($type, $collectionid, $dbId);
660
+			$result['is_shared'] = false;
661
+		}
662
+
663
+		$this->SetResponse($result);
664
+		$ret = true;
665
+	End:
666
+		return $ret;
667
+	}
668
+
669
+	/**
670
+	 * @return id if exist, FALSE otherwise
671
+	 */
672
+	private function CheckExistenceByName($name, $filter_id = -1)
673
+	{
674
+		$sqlParams = array($name, $filter_id);
675
+		$query = "SELECT COUNT(*) FROM " . $this->TableName . " WHERE name = ? AND id <> ? AND hidden = 'f' AND ";
676
+		$this->appendUserIDCond($query, $sqlParams);
677
+		$db_result = PHOTO_DB_Query($query, $sqlParams);
678
+		$row = PHOTO_DB_FetchRow($db_result);
679
+		if (1 !== (int)$row[0]) {
680
+			return FALSE;
681
+		}
682
+
683
+		$sqlParams = array($name);
684
+		$query = "SELECT id FROM " . $this->TableName . " WHERE name = ? AND hidden = 'f' AND ";
685
+		$this->appendUserIDCond($query, $sqlParams);
686
+		$db_result = PHOTO_DB_Query($query, $sqlParams);
687
+		$row = PHOTO_DB_FetchRow($db_result);
688
+		return $row[0];
689
+	}
690
+
691
+	private function CheckExistenceById($id)
692
+	{
693
+		$sqlParams = array($id);
694
+		$query = "SELECT COUNT(*) FROM " . $this->TableName . " WHERE id = ? AND ";
695
+		$this->appendUserIDCond($query, $sqlParams);
696
+		$db_result = PHOTO_DB_Query($query, $sqlParams);
697
+		$row = PHOTO_DB_FetchRow($db_result);
698
+		if (1 === (int)$row[0]) {
699
+			return TRUE;
700
+		}
701
+
702
+		return FALSE;
703
+	}
704
+
705
+	private function GetInfoById($sharedAlbumId, $additional = array()) {
706
+		$sharedAlbum = SharedAlbum::GetInfoById($sharedAlbumId, $additional);
707
+
708
+		if ($sharedAlbum === NULL) {
709
+			return NULL;
710
+		}
711
+
712
+		$thumbSize['preview']['resolutionx'] = 0;
713
+		$thumbSize['preview']['resolutiony'] = 0;
714
+		$thumbSize['small']['resolutionx'] = 0;
715
+		$thumbSize['small']['resolutiony'] = 0;
716
+		$thumbSize['large']['resolutionx'] = 0;
717
+		$thumbSize['large']['resolutiony'] = 0;
718
+		$thubmSize['sig'] = "";
719
+		$cover = SharedAlbum::GetSharedAlbumCover($sharedAlbumId);
720
+		$getRealPath = ('root' === SYNOPHOTO_ADMIN_USER) ? false : true;
721
+		if ($cover === NULL) {
722
+			$sharedAlbum['thumbnail_status'] = 'default';
723
+		} else if (false !== ($item = PhotoAPIUtil::getItemByPath($cover['path'], array('thumb_size'), $cover['type'], $getRealPath))) {
724
+			$sharedAlbum['thumbnail_status'] = $item['thumbnail_status'];
725
+			$thumbSize = $item['additional']['thumb_size'];
726
+		}
727
+		if (in_array("thumb_size", $additional)) {
728
+			$sharedAlbum['additional']['thumb_size'] = $thumbSize;
729
+		}
730
+		return $sharedAlbum;
731
+	}
732
+
733
+	private function AddItemQuery($type, $sharedAlbumId, $itemDBId)
734
+	{
735
+		$query = "INSERT INTO " . ($type === 'photo' ? $this->PhotoTableName : $this->VideoTableName) . " VALUES (?, ?)";
736
+		$db_result = PHOTO_DB_Query($query, array($sharedAlbumId, $itemDBId));
737
+	}
738
+
739
+	private function DeleteItemQuery($type, $sharedAlbumId, $itemDBId)
740
+	{
741
+		$query = "DELETE FROM " . ($type === 'photo' ? $this->PhotoTableName : $this->VideoTableName) . " WHERE collectionid = ? AND {$type}id = ?";
742
+		$db_result = PHOTO_DB_Query($query, array($sharedAlbumId, $itemDBId));
743
+	}
744
+
745
+	private function GetShareLinkById($sharedAlbumId)
746
+	{
747
+		$sqlParams = array($sharedAlbumId);
748
+		$query = "SELECT sharelink FROM " . $this->TableName . " WHERE id = ? AND";
749
+		$this->appendUserIDCond($query, $sqlParams);
750
+		$db_result = PHOTO_DB_Query($query, $sqlParams);
751
+		$row = PHOTO_DB_FetchRow($db_result);
752
+
753
+		if (!$row) {
754
+			return NULL;
755
+		}
756
+
757
+		return $row[0];
758
+	}
759
+
760
+	private function appendUserIDCond(&$sql, &$sqlParams)
761
+	{
762
+		$useridCond = " 1=1 ";
763
+		if ($this->UserID !== 0) {
764
+			$useridCond = " userid = ? ";
765
+			$sqlParams[] = $this->UserID;
766
+		}
767
+
768
+		$sql = $sql . $useridCond;
769
+	}
770
+}
771
+
772
+$api = new SharedAlbumAPI();
773
+$api->Run();
774
+
775
+?>

+ 6
- 0
PhotoStation API/webapi/slideshow_music.inc.php 查看文件

@@ -0,0 +1,6 @@
1
+<?php
2
+	require_once("../include/syno_conf.php");
3
+	require_once(dirname(__FILE__).'/webapi.inc.php');
4
+	require_once('../include/slideshow_music.php');
5
+	require_once(SZ_WEBAPI_CLASS_PATH);
6
+?>

+ 345
- 0
PhotoStation API/webapi/slideshow_music.php 查看文件

@@ -0,0 +1,345 @@
1
+<?php
2
+ini_set("session.use_only_cookies", 0);
3
+set_time_limit(0);
4
+session_cache_limiter("must-revalidate");
5
+
6
+require_once('slideshow_music.inc.php');
7
+
8
+class SlideshowMusicAPI extends WebAPI {
9
+	private $operationPemission = "admin";
10
+	protected $idPrefix = 'slideshowmusic_';
11
+	private $mimeType = array(
12
+		'mp3' => 'audio/mpeg'
13
+	);
14
+
15
+	function __construct() {
16
+		parent::__construct(SZ_WEBAPI_API_DESCRIPTION_PATH);
17
+	}
18
+
19
+	protected function Process()
20
+	{
21
+		if (!strcasecmp($this->method, "list")) {
22
+			$this->SlideshowMusicList();
23
+		} elseif (!strcasecmp($this->method, "get")) {
24
+			session_write_close();
25
+			$this->GetMusic();
26
+		} else if (!strcasecmp($this->method, "add")) {
27
+			$this->AddItem();
28
+		} else if (!strcasecmp($this->method, "edit")) {
29
+			$this->Edit();
30
+		} else if (!strcasecmp($this->method, "delete")) {
31
+			$this->Delete();
32
+		}
33
+	}
34
+
35
+	private function SlideshowMusicList()
36
+	{
37
+		$resp = array();
38
+
39
+		$params = $this->GetParams_List();
40
+
41
+		$data = SlideshowMusic::ListItem($params['offset'], $params['limit']);
42
+		$items = array();
43
+		foreach ($data['data'] as $k => $v) {
44
+			$v['id'] = $this->EncodeItemId($k);
45
+			$items[] = $v;
46
+		}
47
+		$resp['items'] = $items;
48
+		$resp['total'] = $data['total'];
49
+        $offset = (0 > (int)$params['limit']) ? $resp['total'] : $params['offset'] + $params['limit'];
50
+        $resp['offset'] = ($offset > $resp['total']) ?  $resp['total'] : $offset;
51
+		$this->SetResponse($resp);
52
+	}
53
+
54
+	private function GetMusic()
55
+	{
56
+		$id = $this->DecodeItemId($_REQUEST['id']);
57
+		if (false === $id) {
58
+			$this->SetError(WEBAPI_ERR_BAD_REQUEST);
59
+			goto End;
60
+		}
61
+
62
+		$path = SlideshowMusic::GetMusicPath($id);
63
+		if (!$path) {
64
+			$this->SetError(PHOTOSTATION_SLIDESHOWMUSIC_NO_FILE);
65
+			goto End;
66
+		}
67
+
68
+		@header("Content-Type: audio/mpeg");
69
+		@header("Content-Transfer-Encoding: binary");
70
+
71
+		$this->GetRangeDownload($path);
72
+		exit;
73
+
74
+	End:
75
+		return;
76
+	}
77
+
78
+	private function GetRangeDownload($file)
79
+	{
80
+		$fp = @fopen($file, 'rb');
81
+		if (false == $fp) {
82
+			return false;
83
+		}
84
+
85
+		$size   = filesize($file); // File size
86
+		$length = $size;		   // Content length
87
+		$start  = 0;			   // Start byte
88
+		$end	= $size - 1;	   // End byte
89
+		// Now that we've gotten so far without errors we send the accept range header
90
+		// At the moment we only support single ranges.
91
+		// Multiple ranges requires some more work to ensure it works correctly
92
+		// and comply with the spesifications: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
93
+		//
94
+		// Multirange support annouces itself with:
95
+		// header('Accept-Ranges: bytes');
96
+		//
97
+		// Multirange content must be sent with multipart/byteranges mediatype,
98
+		// (mediatype = mimetype)
99
+		// as well as a boundry header to indicate the various chunks of data.
100
+		//
101
+		//header("Accept-Ranges: 0-$length");
102
+		header('Accept-Ranges: bytes');
103
+		// multipart/byteranges
104
+		// http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
105
+		if (isset($_SERVER['HTTP_RANGE'])) {
106
+			$c_start = $start;
107
+			$c_end   = $end;
108
+			// Extract the range string
109
+			list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);
110
+			// Make sure the client hasn't sent us a multibyte range
111
+			if (strpos($range, ',') !== false) {
112
+
113
+				// (?) Shoud this be issued here, or should the first
114
+				// range be used? Or should the header be ignored and
115
+				// we output the whole content?
116
+				header('HTTP/1.1 416 Requested Range Not Satisfiable');
117
+				header("Content-Range: bytes $start-$end/$size");
118
+				// (?) Echo some info to the client?
119
+				exit;
120
+			}
121
+			// If the range starts with an '-' we start from the beginning
122
+			// If not, we forward the file pointer
123
+			// And make sure to get the end byte if spesified
124
+			if ($range{0} == '-') {
125
+
126
+				// The n-number of the last bytes is requested
127
+				$c_start = $size - substr($range, 1);
128
+			} else {
129
+
130
+				$range  = explode('-', $range);
131
+				$c_start = $range[0];
132
+				$c_end   = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size;
133
+			}
134
+			// Check the range and make sure it's treated according to the specs.
135
+			// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
136
+			//
137
+			// End bytes can not be larger than $end.
138
+			$c_end = ($c_end > $end) ? $end : $c_end;
139
+			// Validate the requested range and return an error if it's not correct.
140
+			if ($c_start > $c_end || $c_start > $size - 1 || $c_end >= $size) {
141
+
142
+				header('HTTP/1.1 416 Requested Range Not Satisfiable');
143
+				header("Content-Range: bytes $start-$end/$size");
144
+				// (?) Echo some info to the client?
145
+				exit;
146
+			}
147
+			$start  = $c_start;
148
+			$end	= $c_end;
149
+			$length = $end - $start + 1; // Calculate new content length
150
+			header('HTTP/1.1 206 Partial Content');
151
+		}
152
+		// Notify the client the byte range we'll be outputting
153
+		header("Content-Range: bytes $start-$end/$size");
154
+		header("Content-Length: $length");
155
+
156
+
157
+		// Start buffered download
158
+		$this->OutputFileWithRange($fp, $start, $end);
159
+		fclose($fp);
160
+	}
161
+
162
+	// this function output contain $end
163
+	private function OutputFileWithRange($fp, $start, $end)
164
+	{
165
+		fseek($fp, $start);
166
+		$buffer = 1024 * 8;
167
+		while (!feof($fp) && ($end == -1 || ($p = ftell($fp)) <= $end)) {
168
+
169
+			if ($p + $buffer > $end && $end != -1) {
170
+
171
+				// In case we're only outputtin a chunk, make sure we don't
172
+				// read past the length
173
+				$buffer = $end - $p + 1;
174
+			}
175
+
176
+			echo fread($fp, $buffer);
177
+			flush(); // Free up memory. Otherwise large files will trigger PHP's memory limit.
178
+		}
179
+	}
180
+
181
+	private function AddItem()
182
+	{
183
+		$filename = $_FILES['file']['name'];
184
+		$title = $_REQUEST['title'];
185
+
186
+		if (!csSYNOPhotoMisc::IsMusicFile($filename)) {
187
+			$this->SetError(PHOTOSTATION_SLIDESHOWMUSIC_FILE_EXT_ERR);
188
+			goto End;
189
+		}
190
+		if (!isset($_FILES['file'])) {
191
+			$this->SetError(PHOTOSTATION_SLIDESHOWMUSIC_NO_FILE);
192
+			goto End;
193
+		}
194
+
195
+		if ($_FILES['file']['error'] > 0) { // files is not uploaded successfully by form upload
196
+			$this->SetError(PHOTOSTATION_SLIDESHOWMUSIC_UPLOAD_ERROR);
197
+			goto End;
198
+		}
199
+
200
+		if (SlideshowMusic::LIMIT <= SlideshowMusic::GetCount()) {
201
+			$this->SetError(array(PHOTOSTATION_SLIDESHOWMUSIC_EXCEED_LIMIT, SlideshowMusic::LIMIT));
202
+			goto End;
203
+		}
204
+
205
+		if (!SlideshowMusic::Add($filename, $_FILES['file']['tmp_name'], $title)) {
206
+			$this->SetError(PHOTOSTATION_SLIDESHOWMUSIC_SET_FAIL);
207
+			goto End;
208
+		}
209
+		$resp = SlideshowMusic::GetLastInsertMusic();
210
+		$resp['id'] = $this->EncodeItemId($resp['id']);
211
+		$this->SetResponse($resp);
212
+	End:
213
+		return;
214
+	}
215
+
216
+	private function Edit()
217
+	{
218
+		$params = $this->GetParams_Edit();
219
+
220
+		if (!$params) {
221
+			$this->SetError(WEBAPI_ERR_BAD_REQUEST);
222
+			goto End;
223
+		}
224
+
225
+		$res = SlideshowMusic::EditById($params['id'], $params['title'], $params['default']);
226
+
227
+		if (!$res) {
228
+			$this->SetError(PHOTOSTATION_SLIDESHOWMUSIC_SET_FAIL);
229
+		}
230
+	End:
231
+		return;
232
+	}
233
+
234
+	private function Delete()
235
+	{
236
+		$params = $this->GetParams_Delete();
237
+
238
+		if (!$params) {
239
+			$this->SetError(WEBAPI_ERR_BAD_REQUEST);
240
+			goto End;
241
+		}
242
+		$res = SlideshowMusic::DeleteById($params['id']);
243
+
244
+		if (!$res) {
245
+			$this->SetError(PHOTOSTATION_SLIDESHOWMUSIC_SET_FAIL);
246
+		}
247
+	End:
248
+		return;
249
+	}
250
+
251
+	private function GetParams_List()
252
+	{
253
+		// $variable + 0 => convert to integer
254
+		$params = array(
255
+			'offset' => !isset($_REQUEST['offset']) || $_REQUEST['offset'] < 0 ? 0 : $_REQUEST['offset'] + 0,
256
+			'limit' => !isset($_REQUEST['limit']) || $_REQUEST['limit'] < 0 ? NULL : $_REQUEST['limit'] + 0,
257
+		);
258
+		return $params;
259
+	}
260
+
261
+	private function GetParams_Edit()
262
+	{
263
+		if (!isset($_REQUEST['id']) || !((isset($_REQUEST['title']) && '' !== $_REQUEST['title']) || isset($_REQUEST['default']))) {
264
+			return false;
265
+		}
266
+
267
+		$id = $this->DecodeItemId($_REQUEST['id']);
268
+
269
+		if (false === $id) {
270
+			return false;
271
+		}
272
+
273
+		$params = array(
274
+			'id' => $id,
275
+			'title'	=> $_REQUEST['title'],
276
+			'default' => isset($_REQUEST['default']) ? ("true" === $_REQUEST['default'] ? true : false) : NULL
277
+		);
278
+		return $params;
279
+	}
280
+
281
+	private function GetParams_Delete()
282
+	{
283
+		if (!isset($_REQUEST['id'])){
284
+			return false;
285
+		}
286
+
287
+		$ids = explode(',', $_REQUEST['id']);
288
+		$validIds= array();
289
+		foreach($ids as $id) {
290
+			$decodeId = $this->DecodeItemId($id);
291
+			if(false === $decodeId) {
292
+				continue;
293
+			}
294
+			$validIds[] = $decodeId;
295
+		}
296
+
297
+		if (!count($validIds)) {
298
+			return false;
299
+		}
300
+
301
+		$params = array(
302
+			'id' => $validIds
303
+		);
304
+		return $params;
305
+	}
306
+
307
+	protected function CheckPermission()
308
+	{
309
+		$res = true;
310
+		$check = array(
311
+			"add" => $this->operationPemission,
312
+			"edit" => $this->operationPemission,
313
+			"delete" => $this->operationPemission
314
+		);
315
+		if (!array_key_exists($this->method, $check)) {
316
+			goto End;
317
+		}
318
+
319
+		$funcName = "check_".$check[$this->method];
320
+
321
+		if (!method_exists($this, $funcName)) {
322
+			$res = false;
323
+			goto End;
324
+		}
325
+
326
+		$res = $this->$funcName();
327
+
328
+	End:
329
+		if (!$res) {
330
+			$this->SetError(PHOTOSTATION_SLIDESHOWMUSIC_ACCESS_DENY);
331
+		}
332
+		return $res;
333
+	}
334
+
335
+	private function check_admin()
336
+	{
337
+		csSYNOPhotoDB::GetDBInstance()->SetSessionCache();
338
+		csSYNOPhotoMisc::CheckSessionTimeOut(true);
339
+		return isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']);
340
+	}
341
+}
342
+$api = new SlideshowMusicAPI();
343
+$api->Run();
344
+
345
+?>

+ 3
- 0
PhotoStation API/webapi/smart_album.conf.php 查看文件

@@ -0,0 +1,3 @@
1
+<?php
2
+    define('SMART_ALBUM_CONF_WEBAPI_SERVICE_INCLUDE_PATH', dirname(__FILE__).'/webapi.inc.php');
3
+?>

+ 7
- 0
PhotoStation API/webapi/smart_album.inc.php 查看文件

@@ -0,0 +1,7 @@
1
+<?php
2
+    require_once('smart_album.conf.php');
3
+    require_once("../include/syno_conf.php");
4
+
5
+    require_once(SMART_ALBUM_CONF_WEBAPI_SERVICE_INCLUDE_PATH);
6
+    require_once(SZ_WEBAPI_CLASS_PATH);
7
+?>

+ 889
- 0
PhotoStation API/webapi/smart_album.php 查看文件

@@ -0,0 +1,889 @@
1
+<?php
2
+
3
+require_once('smart_album.inc.php');
4
+require_once('albumutil.php');
5
+
6
+class SmartAlbumAPI extends WebAPI {
7
+
8
+    function __construct() {
9
+        parent::__construct(SZ_WEBAPI_API_DESCRIPTION_PATH);
10
+    }
11
+
12
+    protected function Process() {
13
+        if (!strcasecmp($this->method, "list")) {
14
+			session_write_close();
15
+            $this->SmartAlbumList();
16
+        } elseif (!strcasecmp($this->method, "getinfo")) {
17
+			session_write_close();
18
+            $this->GetInfo();
19
+        } else {
20
+            csSYNOPhotoMisc::CheckSessionTimeOut();
21
+
22
+            if (!strcasecmp($this->method, "create")) {
23
+                $this->Create();
24
+            } elseif (!strcasecmp($this->method, "edit")) {
25
+                $this->Edit();
26
+            } elseif (!strcasecmp($this->method, "delete")) {
27
+                $this->Delete();
28
+            }
29
+        }
30
+	}
31
+
32
+	private function ExtractAlbumToArray($data)
33
+	{
34
+		$params = array();
35
+		$idArr = array();
36
+		$items = explode(',', $data);
37
+		foreach ($items as $item) {
38
+			$arr = explode('_', $item);
39
+			if (2 !== count($arr)) {
40
+				return $idArr;
41
+			}
42
+			if ('album' !== $arr[0]) {
43
+				return $idArr;
44
+			}
45
+			$sharename = trim($arr[1]);
46
+			if ('' != $sharename) {
47
+				$sharename = @pack('H*', $arr[1]);
48
+				array_push($params, $sharename);
49
+			}
50
+		}
51
+		$albums = Album::GetBySharename($params);
52
+		foreach ($albums as $album) {
53
+			$id = $album['shareid'];
54
+			array_push($idArr, $id);
55
+		}
56
+		return $idArr;
57
+	}
58
+
59
+    private function ExtractTagStringToArray($tagString)
60
+    {
61
+        $tagArr = array();
62
+        $arr = explode(',', $tagString);
63
+        foreach ($arr as $tag) {
64
+            $split = explode('tag_', $tag);
65
+            if (2 !== count($split)) {
66
+                return $tagArr;
67
+            }
68
+            array_push($tagArr, $split[1]);
69
+        }
70
+        return $tagArr;
71
+	}
72
+
73
+	private function ExtractExifStringToArray($exifString, $prefix = 'tag_')
74
+	{
75
+		$exifArr = array();
76
+		$arr = explode(',', $exifString);
77
+		foreach ($arr as $exif) {
78
+			$split = explode($prefix, $exif);
79
+			if (2 !== count($split)) {
80
+				return false;
81
+			}
82
+			array_push($exifArr, $split[1]);
83
+		}
84
+		return $exifArr;
85
+	}
86
+
87
+    private function GetParams_List() {
88
+        $params = array();
89
+        if (!isset($_REQUEST['offset']) || !isset($_REQUEST['limit'])) {
90
+            return false;
91
+        }
92
+        $params['offset'] = $_REQUEST['offset'];
93
+        $params['limit'] = $_REQUEST['limit'];
94
+
95
+        $params['sort_by'] = (isset($_REQUEST['sort_by'])) ? $_REQUEST['sort_by'] : 'title';
96
+        if (!in_array($params['sort_by'], array('title'))) {
97
+            return false;
98
+        }
99
+        $params['sort_direction'] = (isset($_REQUEST['sort_direction'])) ? $_REQUEST['sort_direction'] : 'asc';
100
+        if (!in_array($params['sort_direction'], array('asc', 'desc'))) {
101
+            return false;
102
+        }
103
+
104
+        // additional operator
105
+        $params['additional'] = array();
106
+        $params['additional'] = explode(',', $_REQUEST['additional']);
107
+
108
+        return $params;
109
+    }
110
+
111
+    /**
112
+     * 
113
+     *  @return
114
+     *   params object - success
115
+     *   -1 - bad parameter
116
+     *   -2 - smart album not exist
117
+     */
118
+    private function GetParams_Info() {
119
+        $params = array();
120
+        if (!isset($_REQUEST['id'])) {
121
+            return -1;
122
+        }
123
+        $arr = explode(',', $_REQUEST['id']);
124
+        $params['id'] = array();
125
+        foreach ($arr as $smartID) {
126
+            if (!$this->CheckSmartAlbumExist($smartID)) {
127
+                return -2;
128
+            }
129
+            $split = explode('smart_', $smartID);
130
+            array_push($params['id'], $split[1]);
131
+        }
132
+        // additional operator
133
+        $params['additional'] = array();
134
+        $params['additional'] = explode(',', $_REQUEST['additional']);
135
+
136
+        return $params;
137
+    }
138
+
139
+    /**
140
+     * 
141
+     * @return 
142
+     *   -1 - bad parameter
143
+     *   -2 - smart album not exist
144
+     */
145
+    private function GetPrams_Edit() {
146
+        $params = array();
147
+
148
+        if (!isset($_REQUEST['id'])) {
149
+            return -1;
150
+        }
151
+        // check smart album exist
152
+        if (!$this->CheckSmartAlbumExist($_REQUEST['id'])) {
153
+            return -2;
154
+        }
155
+        $arr = explode('smart_', $_REQUEST['id']);
156
+        if (2 !== count($arr)) {
157
+            return -1;
158
+        }
159
+        $params['id'] = $arr[1];
160
+        $smartName =  @pack('H*', $arr[1]);
161
+        $params['name'] = (isset($_REQUEST['name']) && '' !== $_REQUEST['name']) ? $_REQUEST['name'] : $smartName;
162
+        $params['orig_name'] = $smartName;
163
+
164
+        // get original smart album setting
165
+        $smartAlbumCond = SmartAlbum::GetSmartAlbumInstance()->GetSmartCondition($smartName);
166
+        // type
167
+        if (isset($_REQUEST['type'])) {
168
+            $params['type'] = $_REQUEST['type'];
169
+            $arr = explode(',', $params['type']);
170
+            $count = 0;
171
+            foreach ($arr as $type) {
172
+                if (!in_array($type, array('photo', 'video'))) {
173
+                    return -1;
174
+                }
175
+                $count++;
176
+            }
177
+            if (1 > $count) {
178
+                return -1;
179
+            }
180
+        } else {
181
+            $params['type'] = $smartAlbumCond['type'];
182
+		}
183
+		// albums
184
+		if (isset($_REQUEST['albums'])) {
185
+			if (empty($_REQUEST['albums'])) {
186
+				$params['albums'] = null;
187
+			} else {
188
+				$params['albums'] = $this->ExtractAlbumToArray($_REQUEST['albums']);
189
+			}
190
+		} else {
191
+			$arr = $this->ExtractAlbumToArray($smartAlbumCond['albums']);
192
+			$params['albums'] = empty($arr) ? null : $arr;
193
+		}
194
+		// keyword
195
+		if (isset($_REQUEST['keyword'])) {
196
+            if ('' === $_REQUEST['keyword']) {
197
+                $params['keyword'] = null;
198
+                $params['keyword_op'] = null;
199
+            } else {
200
+                $params['keyword'] = $_REQUEST['keyword'];
201
+                $default = isset($smartAlbumCond['keyword_op']) ? $smartAlbumCond['keyword_op'] : 'any';
202
+                $params['keyword_op'] = (isset($_REQUEST['keyword_op'])) ? $_REQUEST['keyword_op'] : $default;
203
+                if (isset($params['keyword_op']) && !in_array($params['keyword_op'], array('any', 'all', 'exact'))) {
204
+                    return -1;
205
+                }
206
+            }
207
+        } else {
208
+            $params['keyword'] = $smartAlbumCond['keyword'];
209
+            $params['keyword_op'] = $smartAlbumCond['keyword_op'];
210
+        }
211
+        // date
212
+        if (isset($_REQUEST['date'])) {
213
+            if (empty($_REQUEST['date'])) {
214
+                $params['date'] = null;
215
+                $params['date_op'] = null;
216
+            } else {
217
+                $default = isset($smartAlbumCond['date_op']) ? $smartAlbumCond['date_op'] : 'taken';
218
+                $params['date_op'] = (isset($_REQUEST['date_op'])) ? $_REQUEST['date_op'] : $default;
219
+				if (in_array($params['date_op'], array('recently_add', 'recently_comment'))) {
220
+					$params['date'] = $_REQUEST['date'];
221
+
222
+				} else if (in_array($params['date_op'], array('taken', 'upload'))) {
223
+					$dates = explode(',', $_REQUEST['date']);
224
+					try {
225
+						if ($dates[0]) {
226
+							$date1 = new DateTime($dates[0]);
227
+						}
228
+						if ($dates[1]) {
229
+							$date2 = new DateTime($dates[1]);
230
+						}
231
+					} catch (Exception $e) {
232
+						return -1;
233
+					}
234
+					if (($date1 && $date2 && $date2 < $date1) || (!$date1 && !$date2)) {
235
+						return -1;
236
+					}
237
+					$params['date'] = ($date1 ? $date1->format('Y-m-d') : "").",".($date2 ? $date2->format("Y-m-d") : "");
238
+				} else {
239
+					return -1;
240
+				}
241
+            }
242
+        } else {
243
+            $params['date'] = $smartAlbumCond['date'];
244
+            $params['date_op'] = $smartAlbumCond['date_op'];
245
+        }
246
+        // peopel_tag
247
+        if (isset($_REQUEST['people_tag'])) {
248
+            if (empty($_REQUEST['people_tag'])) {
249
+                $params['people_tag'] = null;
250
+                $params['people_tag_op'] = null;
251
+            } else {
252
+                // get people_tag
253
+                $params['people_tag'] = $this->ExtractTagStringToArray($_REQUEST['people_tag']);
254
+                // get people_tag_op
255
+                $default = isset($smartAlbumCond['people_tag_op']) ? $smartAlbumCond['people_tag_op'] : 'all';
256
+                $params['people_tag_op'] = (isset($_REQUEST['people_tag_op'])) ? $_REQUEST['people_tag_op'] : $default;
257
+                if (isset($params['people_tag_op']) && !in_array($params['people_tag_op'], array('all', 'any'))) {
258
+                    return -1;
259
+                }
260
+            }
261
+        } else {
262
+            $arr = $this->ExtractTagStringToArray($smartAlbumCond['people_tag']);
263
+            $params['people_tag'] = empty($arr) ? null : $arr;
264
+            $params['people_tag_op'] = $smartAlbumCond['people_tag_op'];
265
+        }
266
+        // geo_tag
267
+        if (isset($_REQUEST['geo_tag'])) {
268
+            if (empty($_REQUEST['geo_tag'])) {
269
+                $params['geo_tag'] = null;
270
+                $params['geo_tag_op'] = null;
271
+            } else {
272
+                // get geo_tag
273
+                $params['geo_tag'] = $this->ExtractTagStringToArray($_REQUEST['geo_tag']);
274
+                // get geo_tag_op
275
+                $default = isset($smartAlbumCond['geo_tag_op']) ? $smartAlbumCond['geo_tag_op'] : 'all';
276
+                $params['geo_tag_op'] = (isset($_REQUEST['geo_tag_op'])) ? $_REQUEST['geo_tag_op'] : $default;
277
+                if (isset($params['geo_tag_op']) && !in_array($params['geo_tag_op'], array('all', 'any'))) {
278
+                    return -1;
279
+                }
280
+            }
281
+        } else {
282
+            $arr = $this->ExtractTagStringToArray($smartAlbumCond['geo_tag']);
283
+            $params['geo_tag'] = empty($arr) ? null : $arr;
284
+            $params['geo_tag_op'] = $smartAlbumCond['geo_tag_op'];
285
+        }
286
+        // desc_tag
287
+        if (isset($_REQUEST['desc_tag'])) {
288
+            if (empty($_REQUEST['desc_tag'])) {
289
+                $params['desc_tag'] = null;
290
+                $params['desc_tag_op'] = null;
291
+            } else {
292
+                // get desc_tag
293
+                $params['desc_tag'] = $this->ExtractTagStringToArray($_REQUEST['desc_tag']);
294
+                // get desc_tag_op
295
+                $default = isset($smartAlbumCond['desc_tag_op']) ? $smartAlbumCond['desc_tag_op'] : 'all';
296
+                $params['desc_tag_op'] = (isset($_REQUEST['desc_tag_op'])) ? $_REQUEST['desc_tag_op'] : $default;
297
+                if (isset($params['desc_tag_op']) && !in_array($params['desc_tag_op'], array('all', 'any'))) {
298
+                    return -1;
299
+                }
300
+            }
301
+        } else {
302
+            $arr = $this->ExtractTagStringToArray($smartAlbumCond['desc_tag']);
303
+            $params['desc_tag'] = empty($arr) ? null : $arr;
304
+            $params['desc_tag_op'] = $smartAlbumCond['desc_tag_op'];
305
+		}
306
+
307
+		foreach(SmartAlbum::$exif2IdPrefix as $label => $prefix) {
308
+			$exifParam = $this->GetExifParams_Edit($label, $prefix.'_');
309
+			if (-1 === $exifParam['success']) {
310
+				return -1;
311
+			} else {
312
+				$params[$label] = $exifParam['label'];
313
+				$params[$label.'_op'] = $exifParam['op'];
314
+			}
315
+		}
316
+
317
+		if ((null === $params['keyword'] || '' === $params['keyword']) && empty($params['date']) && empty($params['people_tag']) && empty($params['geo_tag'])
318
+			&& empty($params['desc_tag']) && empty($params['albums']) && empty($params['camera']) && empty($params['model']) && empty($params['exposure']) && empty($params['aperture'])
319
+			&& empty($params['iso']) && empty($params['focal']) && empty($params['lens']) && empty($params['flash'])) {
320
+            return -1;
321
+        }
322
+        return $params;
323
+	}
324
+
325
+	private function GetExifParams_Edit($label, $prefix) {
326
+		$ret['success'] = true;
327
+		$op = $label."_op";
328
+		if (isset($_REQUEST[$label])) {
329
+			if (empty($_REQUEST[$label])) {
330
+				$ret['label'] = null;
331
+				$ret['op'] = null;
332
+			} else {
333
+				$ret['label'] = array();
334
+				$ret['label'] = $this->ExtractExifStringToArray($_REQUEST[$label], $prefix);
335
+				$defalt = isset($smartAlbumCond[$op]) ? $smartAlbumCond[$op] : 'any';
336
+				$ret['op'] = (isset($_REQUEST[$op])) ? $_REQUEST[$op] : $default;
337
+				if (isset($ret['op']) && 'any' !== $ret['op']) {
338
+					$ret['success'] = false;
339
+				}
340
+			}
341
+		} else {
342
+			$arr = $this->ExtractExifStringToArray($smartAlbumCond[$lable]);
343
+			$ret['label'] = empty($arr) ? null : $arr;
344
+			$ret['op'] = $smartAlbumCond[$op];
345
+		}
346
+		return $ret;
347
+	}
348
+
349
+    private function GetParams_Create() {
350
+        $params = array();
351
+
352
+        if (!isset($_REQUEST['name']) || !isset($_REQUEST['type'])) {
353
+            return false;
354
+        }
355
+        $params['name'] = $_REQUEST['name'];
356
+        // type
357
+        $params['type'] = $_REQUEST['type'];
358
+        $arr = explode(',', $params['type']);
359
+        $count = 0;
360
+        foreach ($arr as $type) {
361
+            if (!in_array($type, array('photo', 'video'))) {
362
+                return false;
363
+            }
364
+            $count++;
365
+        }
366
+        if (1 > $count) {
367
+            return false;
368
+		}
369
+
370
+		$albums = (isset($_REQUEST['albums'])) ? $_REQUEST['albums'] : null;
371
+		if (!empty($_REQUEST['albums'])) {
372
+			$params['albums'] = $this->ExtractAlbumToArray($albums);
373
+		}
374
+
375
+        $params['keyword'] = (isset($_REQUEST['keyword'])) ? $_REQUEST['keyword'] : null;
376
+        if (!empty($_REQUEST['keyword']) || '0' === $_REQUEST['keyword']) {
377
+            $default = 'all';
378
+            $params['keyword_op'] = (isset($_REQUEST['keyword_op'])) ? $_REQUEST['keyword_op'] : $default;
379
+            if (isset($params['keyword_op']) && !in_array($params['keyword_op'], array('any', 'all', 'exact'))) {
380
+                return false;
381
+            }
382
+        }
383
+        $params['date'] = (isset($_REQUEST['date'])) ? $_REQUEST['date'] : null;
384
+        if (!empty($_REQUEST['date'])) {
385
+            $default = 'taken';
386
+            $params['date_op'] = (isset($_REQUEST['date_op'])) ? $_REQUEST['date_op'] : $default;
387
+            if (isset($params['date_op']) && !in_array($params['date_op'], array('taken', 'upload', 'recently_add', 'recently_comment'))) {
388
+                return false;
389
+            }
390
+        }
391
+        $people_tags = (isset($_REQUEST['people_tag'])) ? $_REQUEST['people_tag'] : null;
392
+        if (!empty($_REQUEST['people_tag'])) {
393
+            // get people_tag
394
+            $params['people_tag'] = array();
395
+            $params['people_tag'] = $this->ExtractTagStringToArray($people_tags);
396
+            // get people_tag_op
397
+            $default = 'all';
398
+            $params['people_tag_op'] = (isset($_REQUEST['people_tag_op'])) ? $_REQUEST['people_tag_op'] : $default;
399
+            if (isset($params['people_tag_op']) && !in_array($params['people_tag_op'], array('all', 'any'))) {
400
+                return false;
401
+            }
402
+        }
403
+        $geo_tags = (isset($_REQUEST['geo_tag'])) ? $_REQUEST['geo_tag'] : null;
404
+        if (!empty($_REQUEST['geo_tag'])) {
405
+            // get geo_tag
406
+            $params['geo_tag'] = array();
407
+            $params['geo_tag'] = $this->ExtractTagStringToArray($geo_tags);
408
+            // get geo_tag_op
409
+            $default = 'all';
410
+            $params['geo_tag_op'] = (isset($_REQUEST['geo_tag_op'])) ? $_REQUEST['geo_tag_op'] : $default;
411
+            if (isset($params['geo_tag_op']) && !in_array($params['geo_tag_op'], array('all', 'any'))) {
412
+                return false;
413
+            }
414
+        }
415
+        $desc_tags = (isset($_REQUEST['desc_tag'])) ? $_REQUEST['desc_tag'] : null;
416
+        if (!empty($_REQUEST['desc_tag'])) {
417
+            // get desc_tag
418
+            $params['desc_tag'] = array();
419
+            $params['desc_tag'] = $this->ExtractTagStringToArray($desc_tags);
420
+            // get desc_tag_op
421
+            $default = 'all';
422
+            $params['desc_tag_op'] = (isset($_REQUEST['desc_tag_op'])) ? $_REQUEST['desc_tag_op'] : $default;
423
+            if (isset($params['desc_tag_op']) && !in_array($params['desc_tag_op'], array('all', 'any'))) {
424
+                return false;
425
+            }
426
+		}
427
+		foreach(SmartAlbum::$exif2IdPrefix as $label => $prefix) {
428
+			$value = (isset($_REQUEST[$label])) ? $_REQUEST[$label] : null;
429
+			$op = $label.'_op';
430
+			if (!empty($_REQUEST[$label])) {
431
+				$params[$label] = $this->ExtractExifStringToArray($value, $prefix.'_');
432
+				$default = 'any';
433
+				$params[$op] = (isset($_REQUEST[$op])) ? $_REQUEST[$op] : $default;
434
+				if (isset($params[$op]) && 'any' !== $params[$op]) {
435
+					return false;
436
+				}
437
+			}
438
+		}
439
+
440
+		if ((null === $params['keyword'] || '' === $params['keyword']) && empty($params['albums']) && empty($params['date']) && empty($params['people_tag'])
441
+			&& empty($params['geo_tag']) && empty($params['desc_tag']) && empty($params['camera']) && empty($params['model'])
442
+			&& empty($params['exposure']) && empty($params['aperture']) && empty($params['iso']) && empty($params['focal'])
443
+			&& empty($params['lens']) && empty($params['flash'])) {
444
+            return false;
445
+        }
446
+
447
+		return $params;
448
+	}
449
+
450
+    /**
451
+     * 
452
+     * @param 
453
+     *  $tagIDs - array
454
+     *  $type - 0 means people tag
455
+     *          1 means geo tag
456
+     *          2 means desc tag
457
+     */
458
+    private function CheckTagExist($tagIDs, $type) {
459
+        if (!is_array($tagIDs)) {
460
+            return false;
461
+        }
462
+        foreach ($tagIDs as $id) {
463
+            $query = "SELECT count(id) FROM photo_label WHERE id={$id} AND category={$type}";
464
+            $db_result = PHOTO_DB_Query($query);
465
+            $row = PHOTO_DB_FetchRow($db_result);
466
+            if (1 !== (int)$row[0]) {
467
+                return false;
468
+            }
469
+        }
470
+
471
+        return true;
472
+	}
473
+	private function CheckExifExist($exifNames, $columnName) {
474
+		if (!is_array($exifNames)) {
475
+			return false;
476
+		}
477
+		foreach($exifNames as $exifName) {
478
+			if ('flash_v2' == $columnName) {
479
+				$exifName = str_replace('#', ',', $exifName);
480
+			}
481
+			$query = "SELECT count(". $columnName .") FROM photo_image WHERE ". $columnName ."=?";
482
+			$db_result = PHOTO_DB_Query($query, array($exifName));
483
+			$row = PHOTO_DB_FetchRow($db_result);
484
+			if (1 > (int)$row[0]) {
485
+				return false;
486
+			}
487
+		}
488
+
489
+		return true;
490
+	}
491
+
492
+    /**
493
+     * 
494
+     * @param 
495
+     *  $smartID - ex: smart_id 
496
+     */
497
+    private function CheckSmartAlbumExist($smartID) {
498
+        $arr = explode('smart_', $smartID);
499
+        if (2 !== count($arr)) {
500
+            return false;
501
+        }
502
+        $smartName = @pack('H*', $arr[1]);
503
+        $result = SmartAlbum::GetSmartAlbumInstance()->ReadSmartAlbumFile();
504
+        if (!isset($result['smart_albums'][$smartName])) {
505
+            return false;
506
+        }
507
+        return true;
508
+    }
509
+
510
+	private function ConvertToSmartExifCondition($data, $label, $op){
511
+		$rule_set['field'] = $label;
512
+		$rule_set['operator'] = $data[$op];
513
+		$strings = implode(',', $data[$label]);
514
+		$rule_set['value'] = $strings;
515
+		return $rule_set;
516
+	}
517
+
518
+    private function ConvertToSmartCondition($data) {
519
+        $rule_sets = array();
520
+        $rule_sets[0] = array();
521
+
522
+        $ret['orig_name'] = $data['orig_name'];
523
+        $ret['name'] = $data['name'];
524
+        $ret['desc'] = '';
525
+        $ret['show_photo'] = strstr($data['type'], 'photo') ? true : false;
526
+        $ret['show_video'] = strstr($data['type'], 'video') ? true : false;
527
+
528
+		if (isset($data['albums'])) {
529
+			$rule_set['field'] = 'albums';
530
+			$rule_set['operator'] = 'any';
531
+			$albumString = implode(',', $data['albums']);
532
+			$rule_set['value'] = $albumString;
533
+			array_push($rule_sets[0], $rule_set);
534
+		}
535
+        if (isset($data['keyword']) && isset($data['keyword_op'])) {
536
+            $rule_set['field'] = 'keyword';
537
+            $rule_set['operator'] = $data['keyword_op'];
538
+            $rule_set['value'] = $data['keyword'];
539
+            array_push($rule_sets[0], $rule_set);
540
+        }
541
+        if (isset($data['date']) && isset($data['date_op'])) {
542
+            $rule_set['field'] = 'date';
543
+            $rule_set['operator'] = $data['date_op'];
544
+            $rule_set['value'] = $data['date'];
545
+            array_push($rule_sets[0], $rule_set);
546
+        }
547
+        if (isset($data['people_tag']) && isset($data['people_tag_op'])) {
548
+            $rule_set['field'] = 'people';
549
+            $rule_set['operator'] = $data['people_tag_op'];
550
+            $peopleString = implode(',', $data['people_tag']);
551
+            $rule_set['value'] = $peopleString;
552
+            array_push($rule_sets[0], $rule_set);
553
+        }
554
+        if (isset($data['geo_tag']) && isset($data['geo_tag_op'])) {
555
+            $rule_set['field'] = 'geo';
556
+            $rule_set['operator'] = $data['geo_tag_op'];
557
+            $geoString = implode(',', $data['geo_tag']);
558
+            $rule_set['value'] = $geoString;
559
+            array_push($rule_sets[0], $rule_set);
560
+        }
561
+        if (isset($data['desc_tag']) && isset($data['desc_tag_op'])) {
562
+            $rule_set['field'] = 'desc';
563
+            $rule_set['operator'] = $data['desc_tag_op'];
564
+            $descString = implode(',', $data['desc_tag']);
565
+            $rule_set['value'] = $descString;
566
+            array_push($rule_sets[0], $rule_set);
567
+		}
568
+		foreach(SmartAlbum::$exif2IdPrefix as $label => $prefix) {
569
+			$op = $label.'_op';
570
+			if (isset($data[$label]) && isset($data[$op])) {
571
+				$rule_set = $this->ConvertToSmartExifCondition($data, $label, $op);
572
+				array_push($rule_sets[0], $rule_set);
573
+			}
574
+		}
575
+
576
+		$ret['rule_sets'] = $rule_sets;
577
+        return json_encode($ret);
578
+	}
579
+
580
+    private function FormSmartAlbums($smartNames = false, $params = array()) {
581
+        $smartAlbumList = SmartAlbum::GetSmartAlbumInstance()->ReadSmartAlbumFile();
582
+        $smartAlbumArr = array();
583
+
584
+        if (empty($smartAlbumList) || !is_array($smartAlbumList['smart_albums'])) {
585
+            return $smartAlbumArr;
586
+        }
587
+		$smartAlbums = $smartAlbumList['smart_albums'];
588
+		if (false != $smartNames) {
589
+			foreach($smartNames as $name) {
590
+				if ($smartAlbums[$name]) {
591
+					$list[$name] = $smartAlbums[$name];
592
+				}
593
+			}
594
+		} else {
595
+			$list = $smartAlbums;
596
+		}
597
+
598
+		$needThumbSize = in_array('thumb_size', $params['additional']);
599
+        foreach ($list as $key => $item) {
600
+            $item['name'] = (string)$key;
601
+			$smartAlbum = Decorator::SmartFormula($item, array(
602
+				'param'=> $params,
603
+				'needThumbSize' => $needThumbSize
604
+			));
605
+			if (-1 === $smartAlbum) {
606
+				continue;
607
+			}
608
+            $smartAlbumArr[] = $smartAlbum;
609
+        }
610
+        return $smartAlbumArr;
611
+    }
612
+
613
+    private function SortTitle_ASC($a, $b) {
614
+        if (strtoupper($a['name']) == strtoupper($b['name'])) {
615
+            return 0;
616
+        }
617
+        return (strtoupper($a['name']) < strtoupper($b['name'])) ? -1 : 1;
618
+    }
619
+
620
+    private function SortTitle_DESC($a, $b) {
621
+        if (strtoupper($a['name']) == strtoupper($b['name'])) {
622
+            return 0;
623
+        }
624
+        return (strtoupper($a['name']) > strtoupper($b['name'])) ? -1 : 1;
625
+    }
626
+
627
+    private function SortItem($smartAlbums, $sortBy, $sortDirection, $offset, $limit) {
628
+        if ('title' ===$sortBy) {
629
+            if ('asc' === $sortDirection) {
630
+                usort($smartAlbums, 'self::SortTitle_ASC');
631
+            } else {
632
+                usort($smartAlbums, 'self::SortTitle_DESC');
633
+            }
634
+        }
635
+        $cutItemSort = array();
636
+        if (0 > (int)$limit) {
637
+            // array_slice() will reorder and reset the numeric array indices by default.
638
+            // Preserve_keys are set to TRUE to solve bug#2195.
639
+            $cutItemsSort = array_slice($smartAlbums, $offset, NULL, true);
640
+        } else {
641
+            $cutItemsSort = array_slice($smartAlbums, $offset, $limit, true);
642
+        }
643
+        return $cutItemsSort;
644
+    }
645
+
646
+    private function SmartAlbumList() {
647
+        // Get and check params
648
+        $params = array();
649
+        $params = $this->GetParams_List();
650
+        if (!$params) {
651
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
652
+            goto End;
653
+        }
654
+		$resp = array(
655
+			'total' => 0,
656
+			'offset' => 0,
657
+			'smart_albums'=> array()
658
+		);
659
+		$smartAlbumList = SmartAlbum::GetSmartAlbumInstance()->ReadSmartAlbumFile();
660
+		if (empty($smartAlbumList) || !is_array($smartAlbumList['smart_albums'])) {
661
+			goto End;
662
+		}
663
+		$smartAlbums = $smartAlbumList['smart_albums'];
664
+
665
+		$selectedAlbums = array();
666
+		if ('title' == $params['sort_by']) {
667
+			if ('asc' === $params['sort_direction']) {
668
+				ksort($smartAlbums);
669
+			} else {
670
+				krsort($smartAlbums);
671
+			}
672
+
673
+			if (0 > (int)$limit) {
674
+				// array_slice() will reorder and reset the numeric array indices by default.
675
+				// Preserve_keys are set to TRUE to solve bug#2195.
676
+				$selectedAlbums = array_slice($smartAlbums, $params['offset'], NULL, true);
677
+			} else {
678
+				$selectedAlbums = array_slice($smartAlbums, $params['offset'], $params['limit'], true);
679
+			}
680
+		}
681
+
682
+		if (count($selectedAlbums)) {
683
+			$albumNames = array_keys($selectedAlbums);
684
+		} else {
685
+			$albumNames = false;
686
+		}
687
+
688
+        // Get and form to webapi format
689
+        $smart_albums = array();
690
+        $smart_albums = $this->FormSmartAlbums($albumNames, $params);
691
+        $total = count($smart_albums);
692
+
693
+        // sort and set offset, limit
694
+		$smartSort = $smart_albums;
695
+		$smartSort = $this->SortItem($smart_albums, $params['sort_by'], $params['sort_direction'], $params['offset'], $params['limit']);
696
+        $resp['total'] = $total;
697
+        $offset = (0 > $params['limit']) ? $resp['total'] : $params['offset'] + $params['limit'];
698
+        $resp['offset'] = ($offset > $total) ?  $total : $offset;
699
+        $resp['smart_albums'] = $smartSort;
700
+    End:
701
+        $this->SetResponse($resp);
702
+        return;
703
+    }
704
+
705
+    private function GetInfo() {
706
+        // Get and check params
707
+        $params = array();
708
+        $params = $this->GetParams_Info();
709
+        if (-1 === $params) {
710
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
711
+            goto End;
712
+        } elseif (-2 === $params) {
713
+            $this->SetError(PHOTOSTATION_SMARTALBUM_NOT_EXIST);
714
+            goto End;
715
+        }
716
+
717
+        // Get and form to webapi format
718
+        $smartNames = array();
719
+        foreach ($params['id'] as $id) {
720
+            $smartName = @pack('H*', $id);
721
+            array_push($smartNames, $smartName);
722
+        }
723
+        $smart_albums = $this->FormSmartAlbums($smartNames, $params);
724
+
725
+        $resp['smart_albums'] = $smart_albums;
726
+        $this->SetResponse($resp);
727
+    End:
728
+        return;
729
+    }
730
+
731
+    private function Create() {
732
+        $isAdmin = isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']);
733
+        if (false === $isAdmin) {
734
+            $this->SetError(PHOTOSTATION_SMARTALBUM_ACCESS_DENY);
735
+            goto End;
736
+        }
737
+
738
+        // check parameter
739
+		if (false === ($params = $this->GetParams_Create())) {
740
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
741
+            goto End;
742
+        }
743
+
744
+        // check tag_id exist
745
+        if (is_array($params['people_tag']) && !$this->CheckTagExist($params['people_tag'], 0)) {
746
+            $this->SetError(PHOTOSTATION_SMARTALBUM_TAG_NOT_EXIST);
747
+            goto End;
748
+        }
749
+        if (is_array($params['geo_tag']) && !$this->CheckTagExist($params['geo_tag'], 1)) {
750
+            $this->SetError(PHOTOSTATION_SMARTALBUM_TAG_NOT_EXIST);
751
+            goto End;
752
+        }
753
+        if (is_array($params['desc_tag']) && !$this->CheckTagExist($params['desc_tag'], 2)) {
754
+            $this->SetError(PHOTOSTATION_SMARTALBUM_TAG_NOT_EXIST);
755
+            goto End;
756
+		}
757
+		foreach(SmartAlbum::$exif2Column as $label => $column) {
758
+			if (is_array($params[$label]) && !$this->CheckExifExist($params[$label], $column)) {
759
+				$this->SetError(PHOTOSTATION_SMARTALBUM_TAG_NOT_EXIST);
760
+				goto End;
761
+			}
762
+		}
763
+
764
+        // add smart album
765
+        $jsonRuleData = $this->ConvertToSmartCondition($params);
766
+        $jsonRet = SmartAlbum::GetSmartAlbumInstance()->AddSmartAlbum($jsonRuleData);
767
+        $ret = json_decode($jsonRet, true);
768
+        if (false === $ret['success']) {
769
+	    if ($ret["error"]["code"] == 103) {
770
+		$this->SetError(PHOTOSTATION_SMARTALBUM_CREATE_FAIL_EXIST);
771
+	    } else {
772
+                $this->SetError(PHOTOSTATION_SMARTALBUM_CREATE_FAIL);
773
+	    }
774
+            goto End;
775
+        }
776
+
777
+        $resp['id'] = 'smart_'.bin2hex($params['name']);
778
+        $this->SetResponse($resp);
779
+    End:
780
+        return;
781
+    }
782
+
783
+    private function Edit() {
784
+        $isAdmin = isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']);
785
+        if (false === $isAdmin) {
786
+            $this->SetError(PHOTOSTATION_SMARTALBUM_ACCESS_DENY);
787
+            goto End;
788
+        }
789
+        // get parameter
790
+        $params = $this->GetPrams_Edit();
791
+        if (-1 === $params) {
792
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
793
+            goto End;
794
+        } elseif (-2 === $params) {
795
+            $this->SetError(PHOTOSTATION_SMARTALBUM_NOT_EXIST);
796
+            goto End;
797
+        }
798
+
799
+        // check tag_id exist
800
+        if (is_array($params['people_tag']) && !$this->CheckTagExist($params['people_tag'], 0)) {
801
+            $this->SetError(PHOTOSTATION_SMARTALBUM_TAG_NOT_EXIST);
802
+            goto End;
803
+        }
804
+        if (is_array($params['geo_tag']) && !$this->CheckTagExist($params['geo_tag'], 1)) {
805
+            $this->SetError(PHOTOSTATION_SMARTALBUM_TAG_NOT_EXIST);
806
+            goto End;
807
+        }
808
+        if (is_array($params['desc_tag']) && !$this->CheckTagExist($params['desc_tag'], 2)) {
809
+            $this->SetError(PHOTOSTATION_SMARTALBUM_TAG_NOT_EXIST);
810
+            goto End;
811
+        }
812
+		foreach(SmartAlbum::$exif2Column as $label => $column) {
813
+			if (is_array($params[$label]) && !$this->CheckExifExist($params[$label], $column)) {
814
+				$this->SetError(PHOTOSTATION_SMARTALBUM_TAG_NOT_EXIST);
815
+				goto End;
816
+			}
817
+		}
818
+
819
+        // edit smart album
820
+        $jsonRuleData = $this->ConvertToSmartCondition($params);
821
+        $jsonRet = SmartAlbum::GetSmartAlbumInstance()->EditSmartAlbum($jsonRuleData);
822
+        $ret = json_decode($jsonRet, true);
823
+        if (empty($ret) || false === $ret['success']) {
824
+            $this->SetError(PHOTOSTATION_SMARTALBUM_EDIT_FAIL);
825
+            goto End;
826
+        }
827
+
828
+        // category hook
829
+        if ($params['name'] !== $params['orig_name']) {
830
+            $newID = bin2hex($params['name']);
831
+            $query = "UPDATE category_items SET smart_id='{$newID}' WHERE smart_id='{$params['id']}'";
832
+            if (false === PHOTO_DB_Query($query)) {
833
+                $this->SetError(PHOTOSTATION_SMARTALBUM_EDIT_FAIL);
834
+                goto End;
835
+            }
836
+        }
837
+
838
+        $resp['id'] = 'smart_'.bin2hex($params['name']);
839
+        $this->SetResponse($resp);
840
+    End:
841
+        return;
842
+    }
843
+    
844
+    private function Delete() {
845
+        $isAdmin = isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']);
846
+        if (false === $isAdmin) {
847
+            $this->SetError(PHOTOSTATION_SMARTALBUM_ACCESS_DENY);
848
+            goto End;
849
+        }
850
+        // Get and check params
851
+        $params = $this->GetParams_Info();
852
+        if (-1 === $params) {
853
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
854
+            goto End;
855
+        } elseif (-2 === $params) {
856
+            $this->SetError(PHOTOSTATION_SMARTALBUM_NOT_EXIST);
857
+            goto End;
858
+        }
859
+
860
+        // get smart album names
861
+        $smartNames = array();
862
+        foreach ($params['id'] as $id) {
863
+            $smartName = @pack('H*', $id);
864
+            array_push($smartNames, $smartName);
865
+        }
866
+
867
+        // delete
868
+        foreach ($smartNames as $smartName) {
869
+            $data['name'] = $smartName;
870
+            $jsonData = json_encode($data);
871
+            SmartAlbum::GetSmartAlbumInstance()->DeleteSmartAlbum($jsonData);
872
+
873
+            // category hook
874
+            $id = bin2hex($smartName);
875
+            $query = "DELETE FROM category_items WHERE smart_id='{$id}'";
876
+            PHOTO_DB_Query($query);
877
+        }
878
+
879
+
880
+
881
+    End:
882
+        return;
883
+    }
884
+}
885
+
886
+$api = new SmartAlbumAPI();
887
+$api->Run();
888
+
889
+?>

+ 3
- 0
PhotoStation API/webapi/tag.conf.php 查看文件

@@ -0,0 +1,3 @@
1
+<?php
2
+    define('TAG_CONF_WEBAPI_SERVICE_INCLUDE_PATH', dirname(__FILE__).'/webapi.inc.php');
3
+?>

+ 9
- 0
PhotoStation API/webapi/tag.inc.php 查看文件

@@ -0,0 +1,9 @@
1
+<?php
2
+    require_once('tag.conf.php');
3
+    require_once("../include/syno_conf.php");
4
+    require_once("../include/label.php");
5
+    require_once("../include/item_label.php");
6
+
7
+    require_once(TAG_CONF_WEBAPI_SERVICE_INCLUDE_PATH);
8
+    require_once(SZ_WEBAPI_CLASS_PATH);
9
+?>

+ 586
- 0
PhotoStation API/webapi/tag.php 查看文件

@@ -0,0 +1,586 @@
1
+<?php
2
+
3
+require_once('tag.inc.php');
4
+require_once('albumutil.php');
5
+
6
+define('PHP_CMD', '/usr/bin/php -n -d extension_dir=/lib/php/modules -d extension=syno_compiler.so -d extension=curl.so -d extension=bz2.so');
7
+
8
+class TagAPI extends WebAPI {
9
+    function __construct() {
10
+        parent::__construct(SZ_WEBAPI_API_DESCRIPTION_PATH);
11
+    }
12
+
13
+    protected function Process()
14
+    {
15
+        if (!strcasecmp($this->method, "list")) {
16
+            $this->TagList();
17
+        } elseif (!strcasecmp($this->method, "getinfo")) {
18
+            $this->GetInfo();
19
+        } else {
20
+            csSYNOPhotoMisc::CheckSessionTimeOut(true);
21
+
22
+            if (!strcasecmp($this->method, "create")) {
23
+                 $this->Create();
24
+            } else if (!strcasecmp($this->method, "searchplace")) {
25
+                $this->SearchPlace();
26
+            } else {
27
+                csSYNOPhotoMisc::CheckSessionTimeOut();
28
+
29
+                if (!strcasecmp($this->method, "edit")) {
30
+                    $this->Edit();
31
+                } elseif (!strcasecmp($this->method, "delete")) {
32
+                    $this->Delete();
33
+				} elseif (!strcasecmp($this->method, "delete_unconfirmed_tag")) {
34
+					$this->DeleteUnconfirmedTag();
35
+				}
36
+            }
37
+        }
38
+    }
39
+
40
+	private function GetParams_SearchPlace()
41
+	{
42
+		if (!isset($_REQUEST['query']) || !isset($_REQUEST['location'])) {
43
+			return false;
44
+		}
45
+		$params = array();
46
+		$params['query'] = $_REQUEST['query'];
47
+		$params['location'] = $_REQUEST['location'];
48
+		$params['radius'] = (isset($_REQUEST['radius'])) ? $_REQUEST['radius'] : '500';
49
+		$params['format'] = $_REQUEST['format'] === 'true';
50
+		return $params;
51
+	}
52
+
53
+    private function GetParams_Edit()
54
+    {
55
+        if (!isset($_REQUEST['id']) || !isset($_REQUEST['name'])) {
56
+            return false;
57
+        }
58
+        $arr = explode('tag_', $_REQUEST['id']);
59
+        if (2 !== count($arr)) {
60
+            return false;
61
+        }
62
+        $params['id'] = (int) $arr[1];
63
+        $params['name'] = $_REQUEST['name'];
64
+
65
+        // for geo tag
66
+        $params['place_id'] = (isset($_REQUEST['place_id'])) ? $_REQUEST['place_id'] : null;
67
+        $params['reference'] = (isset($_REQUEST['reference'])) ? $_REQUEST['reference'] : null;
68
+        $params['lat'] = (isset($_REQUEST['lat'])) ? $_REQUEST['lat'] : null;
69
+        $params['lng'] = (isset($_REQUEST['lng'])) ? $_REQUEST['lng'] : null;
70
+        $params['address'] = (isset($_REQUEST['address'])) ? $_REQUEST['address'] : null;
71
+        return $params;
72
+    }
73
+
74
+    private function GetParams_Create()
75
+    {
76
+        if (!isset($_REQUEST['name']) || !isset($_REQUEST['type'])) {
77
+            return false;
78
+        }
79
+        $params['name'] = $_REQUEST['name'];
80
+        $params['type'] = $_REQUEST['type'];
81
+        if (!in_array($params['type'], array('people', 'geo', 'desc'))) {
82
+            return false;
83
+        }
84
+
85
+        // for geo tag
86
+        if ('geo' === $params['type']) {
87
+            $params['place_id'] = (isset($_REQUEST['place_id'])) ? $_REQUEST['place_id'] : null;
88
+            $params['reference'] = (isset($_REQUEST['reference'])) ? $_REQUEST['reference'] : null;
89
+            $params['lat'] = (isset($_REQUEST['lat'])) ? $_REQUEST['lat'] : null;
90
+            $params['lng'] = (isset($_REQUEST['lng'])) ? $_REQUEST['lng'] : null;
91
+            $params['address'] = (isset($_REQUEST['address'])) ? $_REQUEST['address'] : null;
92
+        }
93
+        return $params;
94
+    }
95
+
96
+    private function GetParams_Info()
97
+    {
98
+        if (!isset($_REQUEST['id'])) {
99
+            return false;
100
+        }
101
+        $params['id'] = array();
102
+        $arr = explode(',', $_REQUEST['id']);
103
+        foreach ($arr as $item) {
104
+            $split = explode('tag_', $item);
105
+            if (2 !== count($split)) {
106
+                return false;
107
+            }
108
+            $params['id'][] = (int) $split[1];
109
+        }
110
+
111
+        $params['thumbnail_status'] = isset($_REQUEST['thumbnail_status']) ? $_REQUEST['thumbnail_status'] : 'false';
112
+        if (!in_array($params['thumbnail_status'], array('true', 'false'))) {
113
+            return false;
114
+        }
115
+
116
+        $params['additional'] = explode(',', $_REQUEST['additional']);
117
+        return $params;
118
+    }
119
+
120
+    private function GetParams_List()
121
+    {
122
+        if (!isset($_REQUEST['offset']) || !isset($_REQUEST['limit']) || !isset($_REQUEST['type'])) {
123
+            return false;
124
+        }
125
+        if (!is_numeric($_REQUEST['offset']) || !is_numeric($_REQUEST['limit'])) {
126
+            return false;
127
+        }
128
+        $params['offset'] = (int)$_REQUEST['offset'];
129
+        $params['limit'] = (int)$_REQUEST['limit'];
130
+        $params['type'] = explode(',', $_REQUEST['type']);
131
+        foreach ($params['type'] as $type) {
132
+            if (!in_array($type, array('people', 'geo', 'desc'))) {
133
+                return false;
134
+            }
135
+        }
136
+
137
+        $params['thumbnail_status'] = isset($_REQUEST['thumbnail_status']) ? $_REQUEST['thumbnail_status'] : 'false';
138
+        if (!in_array($params['thumbnail_status'], array('true', 'false'))) {
139
+            return false;
140
+        }
141
+
142
+        $params['sort_by'] = (isset($_REQUEST['sort_by'])) ? $_REQUEST['sort_by'] : 'title';
143
+        if ((null !== $params['sort_by']) && !in_array($params['sort_by'], array('title'))) {
144
+            return false;
145
+        }
146
+        $params['sort_direction'] = (isset($_REQUEST['sort_direction'])) ? $_REQUEST['sort_direction'] : 'asc';
147
+        if ((null !== $params['sort_direction']) && !in_array($params['sort_direction'], array('asc', 'desc'))) {
148
+            return false;
149
+        }
150
+
151
+        $params['unconfirm_people_tag'] = isset($_REQUEST['unconfirm_people_tag']) ? $_REQUEST['unconfirm_people_tag'] : 'false';
152
+        if (!in_array($params['unconfirm_people_tag'], array('true', 'false'))) {
153
+            return false;
154
+        }
155
+
156
+        $params['additional'] = explode(',', $_REQUEST['additional']);
157
+
158
+        return $params;
159
+    }
160
+
161
+    private function CategoryNameToNum($name)
162
+    {
163
+        if (!isset($name)) {
164
+            return false;
165
+        }
166
+        switch ($name) {
167
+        case 'people':
168
+            $codeNum = Label::PEOPLE;
169
+            break;
170
+        case 'geo':
171
+            $codeNum = Label::GEO;
172
+            break;
173
+        case 'desc':
174
+            $codeNum = Label::DESC;
175
+            break;
176
+        default:
177
+            return false;
178
+            break;
179
+        }
180
+        return $codeNum;
181
+    }
182
+
183
+    private function FormListTags($data, $params)
184
+    {
185
+		$extraConfig = array(
186
+			'needThumbSize'	=> in_array('thumb_size', $params['additional']),
187
+			'param'			=> $params
188
+		);
189
+
190
+        $tags = array();
191
+        foreach ($data as $key => $value) {
192
+            // 0 - people tag; 1 - geo tag; 2 - desc tag
193
+            if (Label::PEOPLE === $key && !in_array('people', $params['type'])) {
194
+                continue;
195
+            }
196
+            if (Label::GEO === $key && !in_array('geo', $params['type'])) {
197
+                continue;
198
+            }
199
+            if (Label::DESC === $key && !in_array('desc', $params['type'])) {
200
+                continue;
201
+            }
202
+            foreach ($data[$key] as $item) {
203
+				$tag = Decorator::TagFormula($item, $extraConfig);
204
+                $tags[] = $tag;
205
+            }
206
+        }
207
+        return $tags;
208
+    }
209
+
210
+    private function FormInfoTags($data, $params)
211
+    {
212
+        $tags = array();
213
+		$extraConfig = array(
214
+			'needThumbSize'	=> in_array('thumb_size', $params['additional']),
215
+			'param'			=> $params
216
+		);
217
+        foreach ($data as $item) {
218
+			$tag = Decorator::TagFormula($item, $extraConfig);
219
+			$tags[] = $tag;
220
+        }
221
+        return $tags;
222
+    }
223
+
224
+    private function AddUnConfirmTag($list, $params, &$tags)
225
+    {
226
+        $needThumbSize = in_array('thumb_size', $params['additional']);
227
+
228
+        $arr = array();
229
+        $unConfirmTag['id'] = SYNOPHOTO_UNCONFIRM_TAG_ID;
230
+        $unConfirmTag['type'] = 'tag';
231
+        $unConfirmTag['tag_type'] = 'people';
232
+        $unConfirmTag['name'] = '';
233
+
234
+		$coverPath = '';
235
+        if (is_array($list)) {
236
+            $pathList = array_keys($list);
237
+			$coverPath = $pathList[0];
238
+        }
239
+        $blConversion = csSYNOPhotoMisc::IsAlbumAllowConversion(dirname($coverPath));
240
+        list($orig_resolutionx, $orig_resolutiony, $blThumbRotated) = AlbumAPIUtil::GetCoverResolutionRotation($coverPath);
241
+        AlbumAPIUtil::FillThumbStatus($unConfirmTag, $coverPath, $needThumbSize, $orig_resolutionx, $orig_resolutiony, $blThumbRotated, $blConversion);
242
+
243
+        array_push($arr, $unConfirmTag);
244
+        array_splice($tags, 0, 0, $arr);
245
+    }
246
+
247
+    private function SortTags($sortBy, $sortDirection, &$sortTags)
248
+    {
249
+        if ('title' === $sortBy) {
250
+            if ('asc' === $sortDirection) {
251
+                return;
252
+            } else {
253
+                $sortTags = array_reverse($sortTags);
254
+            }
255
+        }
256
+    }
257
+
258
+    private function TagList()
259
+    {
260
+        if (false === ($params = $this->GetParams_List())) {
261
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
262
+            goto End;
263
+        }
264
+        // get category type number  - people(0), geo(1), desc(2)
265
+        $types = array();
266
+        foreach ($params['type'] as $type) {
267
+            if (false === ($num = $this->CategoryNameToNum($type))) {
268
+                $this->SetError(WEBAPI_ERR_BAD_REQUEST);
269
+                goto End;
270
+            }
271
+            array_push($types, $num);
272
+        }
273
+        // database query
274
+        $result = Label::GetByCategoryPermission($types, isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']));
275
+		$totalTags = $this->FormListTags($result, $params);
276
+        
277
+        // sorting
278
+        $this->SortTags($params['sort_by'], $params['sort_direction'], $totalTags);
279
+
280
+        // add people unconfirm tag
281
+        if (in_array('people', $params['type']) && 'true' === $params['unconfirm_people_tag']) {
282
+            $list = AlbumAPIUtil::GetUnConfirmItemList();
283
+            if (0 < (int)$list['total']) {
284
+                $this->AddUnConfirmTag($list, $params, $totalTags);
285
+            }
286
+        }
287
+        $total = count($totalTags);
288
+
289
+        // set offset and limit
290
+        if (0 > $params['limit']) {
291
+            $tags = array_slice($totalTags, $params['offset']);
292
+        } else {
293
+            $tags = array_slice($totalTags, $params['offset'], $params['limit']);
294
+        }
295
+
296
+        $resp['total'] = (int)$total;
297
+        $offset = (0 > $params['limit']) ? $resp['total'] : $params['offset'] + $params['limit'];
298
+        $resp['offset'] = ($offset > $total) ?  (int)$total : (int)$offset;
299
+        $resp['tags'] = $tags;
300
+        $this->SetResponse($resp);
301
+    End:
302
+        return;
303
+    }
304
+
305
+    private function GetInfo()
306
+    {
307
+        if (false === ($params = $this->GetParams_Info())) {
308
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
309
+            goto End;
310
+        }
311
+
312
+        // database query
313
+        if (false === ($result = Label::GetByIds($params['id']))) {
314
+            $this->SetError(PHOTOSTATION_TAG_GETINFO_FAIL);
315
+            goto End;
316
+        }
317
+        // form to webapi format
318
+        $tags = $this->FormInfoTags($result, $params);
319
+
320
+        $resp['tags'] = $tags;
321
+        $this->SetResponse($resp);
322
+    End:
323
+        return;
324
+    }
325
+
326
+    private function Create()
327
+    {
328
+        if (false === ($params = $this->GetParams_Create())) {
329
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
330
+            goto End;
331
+        }
332
+        // add label parameter
333
+        $label['name'] = $params['name'];
334
+        $label['category'] = $this->CategoryNameToNum($params['type']);
335
+        $label['info'] = '';
336
+        if ('geo' === $params['type']) {
337
+			$info['placeId'] = '';
338
+            if (null !== $params['place_id']) {
339
+                $info['placeId'] = $params['place_id'];
340
+            }
341
+            if (null !== $params['reference']) {
342
+                $info['reference'] = $params['reference'];
343
+            }
344
+            if (null !== $params['lat']) {
345
+                $info['lat'] = $params['lat'];
346
+            }
347
+            if (null !== $params['lng']) {
348
+                $info['lng'] = $params['lng'];
349
+            }
350
+            if (null !== $params['address']) {
351
+                $info['address'] = $params['address'];
352
+            }
353
+            if (!empty($info)) {
354
+                $json = json_encode($info);
355
+                $label['info'] = $json;
356
+            }
357
+        }
358
+
359
+        $blCreateNewTag = false;
360
+        if ('geo' === $params['type']) {
361
+            // (name, place_id) as primary key in geo tag
362
+            if (false === ($row = Label::GetGeoLabelByNamePlaceId($label['name'], $info['placeId']))) {
363
+                $blCreateNewTag = true;
364
+            }
365
+        } elseif ('people' === $params['type'] || 'desc' === $params['type']) {
366
+            // (name, category) as primary key in people, desc tag
367
+            if (false === ($row = Label::GetByNameCategory($label['name'], $label['category']))) {
368
+                $blCreateNewTag = true;
369
+            }
370
+        }
371
+        if ($blCreateNewTag) {
372
+            // database query
373
+            if (false === ($lastinsertId = Label::Add($label['name'], $label['category'], $label['info']))) {
374
+                $this->SetError(PHOTOSTATION_TAG_CREATE_FAIL);
375
+                goto End;
376
+            }
377
+            $row = array('id' => $lastinsertId);
378
+        }
379
+
380
+        $resp['id'] = 'tag_'.$row['id'];
381
+        $this->SetResponse($resp);
382
+    End:
383
+        return;
384
+    }
385
+
386
+    private function Edit()
387
+    {
388
+        if (false === ($params = $this->GetParams_Edit())) {
389
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
390
+            goto End;
391
+        }
392
+
393
+        // get original label
394
+		$id = $params['id'];
395
+        if (false === ($label_objs = Label::GetByIds(array($id)))) {
396
+            $this->SetError(PHOTOSTATION_TAG_EDIT_FAIL);
397
+            goto End;
398
+        }
399
+		$result = $label_objs[$id];
400
+        $origLabel = json_decode($result['info'], true);
401
+        $type = $result['category'];
402
+        // add label parameter
403
+        $label['info'] = '';
404
+        if (Label::GEO === $type) {
405
+            $info['placeId'] = (null !== $params['place_id']) ? $params['place_id'] : $origLabel['placeId'];
406
+            $info['reference'] = (null !== $params['reference']) ? $params['reference'] : $origLabel['reference'];
407
+            $info['lat'] = (null !== $params['lat']) ? $params['lat'] : $origLabel['lat'];
408
+            $info['lng'] = (null !== $params['lng']) ? $params['lng'] : $origLabel['lng'];
409
+            $info['address'] = (null !== $params['address']) ? $params['address'] : $origLabel['address'];
410
+            if ('' === $info['placeId'] || null === $info['placeId']) {
411
+                unset($info['placeId']);
412
+            }
413
+            if ('' === $info['reference'] || null === $info['reference']) {
414
+                unset($info['reference']);
415
+            }
416
+            if ('' === $info['lat'] || null === $info['lat']) {
417
+                unset($info['lat']);
418
+            }
419
+            if ('' === $info['lng'] || null === $info['lng']) {
420
+                unset($info['lng']);
421
+            }
422
+            if ('' === $info['address'] || null === $info['address']) {
423
+                unset($info['address']);
424
+            }
425
+            $label['info'] = json_encode($info);
426
+        }
427
+
428
+        // check tag exist
429
+        if (Label::isDuplicated($params['id'], $params['name'], $info['placeId'], $type)) {
430
+            $this->SetError(PHOTOSTATION_TAG_HAS_EXIST);
431
+            goto End;
432
+        }
433
+
434
+        // database query
435
+        if (0 === Label::EditById($params['id'], $params['name'], $label['info'])) {
436
+            $this->SetError(PHOTOSTATION_TAG_EDIT_FAIL);
437
+            goto End;
438
+        }
439
+
440
+        // update metadata
441
+        if (Label::GEO !== $type) {
442
+            $image_label_objs = ItemLabel::GetPhotoLabelByLabelIds(array($params['id']));
443
+            foreach ($image_label_objs as $objs) {
444
+                self::OutputSingleSpace();
445
+                ItemLabel::UpdatePhotoMetadata($objs['image_id'], $result['category']);
446
+            }
447
+        }
448
+
449
+    End:
450
+        return;
451
+    }
452
+
453
+    private function Delete()
454
+    {
455
+        if (false === ($params = $this->GetParams_Info())) {
456
+            $this->SetError(WEBAPI_ERR_BAD_REQUEST);
457
+            goto End;
458
+        }
459
+
460
+        foreach ($params['id'] as $id) {
461
+            // To avoid cgi timeout, have to delete label one by one
462
+            // Label::DeleteAllByIds support delete multilabels
463
+            self::OutputSingleSpace();
464
+            Label::DeleteAllByIds(array($id));
465
+        }
466
+    End:
467
+        return;
468
+    }
469
+
470
+	private function DeleteUnconfirmedTag()
471
+	{
472
+		$unconfirmedLabelId = ItemLabel::GetUnconfirmedLabelId();
473
+		Label::DeleteAllByIds($unconfirmedLabelId, false);
474
+	}
475
+
476
+	private function SearchPlace()
477
+	{
478
+		if (false === ($params = $this->GetParams_SearchPlace())) {
479
+			$this->SetError(WEBAPI_ERR_BAD_REQUEST);
480
+			goto End;
481
+		}
482
+
483
+		$query = escapeshellarg($params['query']);
484
+		$location = escapeshellarg($params['location']);
485
+		$radius = escapeshellarg($params['radius']);
486
+		$language = escapeshellarg($_SESSION[SYNOPHOTO_ADMIN_USER]['lang']);
487
+
488
+		$phppath = '/var/packages/PhotoStation/target/photo_scripts/encrypted/tag_place_search.php';
489
+
490
+		$cmd = PHP_CMD." $phppath -q $query -l $location -r $radius -n $language";
491
+
492
+
493
+		@exec($cmd, $output, $ret);
494
+		if (0 !== $ret) {
495
+			$this->SetError(PHOTOSTATION_TAG_SEARCH_FAIL);
496
+			goto End;
497
+		}
498
+
499
+		$json = json_decode(implode('', $output), true);
500
+
501
+		$searchPlaces = array();
502
+		$existPlace = array();
503
+
504
+		if (is_array($json['results'])) {
505
+			foreach ($json['results'] as $place) {
506
+				$item = array();
507
+				$item['address'] = $place['formatted_address'];
508
+				$item['gps'] = $place['geometry']['location'];
509
+				$item['placeId'] = $place['id'];
510
+				$item['name'] = $place['name'];
511
+				$item['tagId'] = -1;
512
+				$item['type'] = 'search';
513
+				$searchPlaces[] = $item;
514
+			}
515
+		}
516
+		$existPlace = Label::SearchGeoLabel($params['query']);
517
+
518
+		if ($params['format']) {
519
+			for($i=0; $i < count($existPlace); ++$i) {
520
+				$existPlace[$i]['type'] = 'db';
521
+			}
522
+			$resp = array_merge($existPlace, $searchPlaces);
523
+		} else {
524
+			$resp['existPlaces'] = $existPlace;
525
+			$resp['searchPlaces'] = $searchPlaces;
526
+		}
527
+
528
+		$this->SetResponse($resp);
529
+	End:
530
+		return;
531
+	}
532
+
533
+	protected function CheckPermission()
534
+	{
535
+		csSYNOPhotoDB::GetDBInstance()->SetSessionCache();
536
+
537
+		$res = true;
538
+		$check = array(
539
+			"edit" => "admin",
540
+			"delete" => "admin",
541
+			"delete_unconfirmed_tag" => "admin",
542
+			"create" => "manage",
543
+			"searchplace" => "manage"
544
+		);
545
+		if (!array_key_exists($this->method, $check)) {
546
+			goto End;
547
+		}
548
+
549
+		$funcName = "check_".$check[$this->method];
550
+
551
+		if (!method_exists($this, $funcName)) {
552
+			$res = false;
553
+			goto End;
554
+		}
555
+
556
+		$res = $this->$funcName();
557
+
558
+	End:
559
+		if (!$res) {
560
+			$this->SetError(PHOTOSTATION_TAG_ACCESS_DENY);
561
+		}
562
+		return $res;
563
+	}
564
+
565
+	private function check_admin()
566
+	{
567
+		return isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']);
568
+	}
569
+
570
+	private function check_manage()
571
+	{
572
+		if (isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user'])) {
573
+			return true;
574
+		}
575
+		if (0 < count($_SESSION[SYNOPHOTO_ADMIN_USER]['manageable_album'])) {
576
+			return true;
577
+		}
578
+		return false;
579
+	}
580
+}
581
+
582
+$api = new TagAPI();
583
+$api->Run();
584
+
585
+
586
+?>

+ 4
- 0
PhotoStation API/webapi/thumb.conf.php 查看文件

@@ -0,0 +1,4 @@
1
+<?php
2
+	define('THUMB_CONF_WEBAPI_SERVICE_INCLUDE_PATH', dirname(__FILE__) . '/webapi.inc.php');
3
+	$SYNOPHOTO_ALLOW_ID_TYPE = array('photo', 'video', 'album', 'smart', 'virtual', 'tag', 'defaultsmart', 'sharedalbum', 'publicshare');
4
+	$SYNOPHOTO_ID_TYPE_LEN = array('photo' => 3, 'video' => 3, 'album' => 2, 'smart' => 2, 'virtual'=> 2, 'tag' => 2, 'defaultsmart' => 2, 'sharedalbum' => 2, 'publicshare' => 2);

+ 6
- 0
PhotoStation API/webapi/thumb.inc.php 查看文件

@@ -0,0 +1,6 @@
1
+<?php
2
+	require_once('thumb.conf.php');
3
+	require_once("../include/syno_defines.php");
4
+
5
+	require_once(THUMB_CONF_WEBAPI_SERVICE_INCLUDE_PATH);
6
+	require_once(SZ_WEBAPI_CLASS_PATH);

+ 518
- 0
PhotoStation API/webapi/thumb.php 查看文件

@@ -0,0 +1,518 @@
1
+<?PHP
2
+
3
+set_time_limit(0);
4
+session_cache_limiter("must-revalidate");
5
+
6
+//workaround for locale-awared function "basename()" in DSM 5.0 env (#bug 2037)
7
+setlocale(LC_CTYPE, "en_US.UTF-8");
8
+
9
+require_once('thumb.inc.php');
10
+require_once '../include/SYNOPhotoEA.php';
11
+require_once('../include/photo/synophoto_csPhotoMisc.php');
12
+require_once '../include/shared_album.php';
13
+require_once('../include/photo/synophoto_csPhotoDB.php');
14
+require_once '../include/album.php';
15
+
16
+class ThumbAPI extends WebAPI
17
+{
18
+	function __construct()
19
+	{
20
+		parent::__construct(SZ_WEBAPI_API_DESCRIPTION_PATH);
21
+	}
22
+
23
+	protected function Process()
24
+	{
25
+		csSYNOPhotoMisc::PhotoSessionStart();
26
+		if ('' == SYNOPHOTO_URL_PREFIX && csSYNOPhotoMisc::checkDsmAccountEnabled()) {
27
+			define('PHOTO_ACCESS_RIGHT_TABLE', 'photo_access_right_for_dsm_account');
28
+			define('PHOTO_UPLOAD_RIGHT_TABLE', 'photo_upload_right_for_dsm_account');
29
+			define('PHOTO_MANAGE_RIGHT_TABLE', 'photo_manage_right_for_dsm_account');
30
+			define('PHOTO_GROUP_PERMISSION_TABLE', 'photo_group_permission_for_dsm_account');
31
+			define('SHARED_ALBUM_TABLE_NAME', 'photo_personal_collection_for_dsm_account');
32
+			define('SHARED_ALBUM_PHOTO_TABLE_NAME', 'photo_personal_collection_photo_for_dsm_account');
33
+			define('SHARED_ALBUM_VIDEO_TABLE_NAME', 'photo_personal_collection_video_for_dsm_account');
34
+		} else {
35
+			define('PHOTO_ACCESS_RIGHT_TABLE', 'photo_access_right');
36
+			define('PHOTO_UPLOAD_RIGHT_TABLE', 'photo_upload_right');
37
+			define('PHOTO_MANAGE_RIGHT_TABLE', 'photo_manage_right');
38
+			define('PHOTO_GROUP_PERMISSION_TABLE', 'photo_group_permission');
39
+			define('PHOTO_USER_TABLE', 'photo_user');
40
+			define('PHOTO_USER_GROUP_TABLE', 'photo_user_group');
41
+			define('PHOTO_GROUP_TABLE', 'photo_group');
42
+			define('SHARED_ALBUM_TABLE_NAME', 'photo_personal_collection');
43
+			define('SHARED_ALBUM_PHOTO_TABLE_NAME', 'photo_personal_collection_photo');
44
+			define('SHARED_ALBUM_VIDEO_TABLE_NAME', 'photo_personal_collection_video');
45
+		}
46
+		define('SHARED_ALBUM_ADMIN_TABLE_NAME', 'photo_personal_collection_for_admin');
47
+		define('SHARED_ALBUM_PHOTO_ADMIN_TABLE_NAME', 'photo_personal_collection_photo_for_admin');
48
+		define('SHARED_ALBUM_VIDEO_ADMIN_TABLE_NAME', 'photo_personal_collection_video_for_admin');
49
+
50
+		csSYNOPhotoDB::GetDBInstance()->SetSessionCache();
51
+		if (isset($_REQUEST['public_share_id'])) {
52
+			csSYNOPhotoDB::GetDBInstance()->SetPublicShareSessionCache($_REQUEST['public_share_id']);
53
+		} else if (isset($_REQUEST['id']) && 0 === strpos($_REQUEST['id'], 'publicshare_')) {
54
+			$id = $_REQUEST['id'];
55
+			$id_arr = explode('_', $id);
56
+			csSYNOPhotoDB::GetDBInstance()->SetPublicShareSessionCache($id_arr[1]);
57
+		}
58
+		session_write_close();
59
+		if (!strcasecmp($this->method, "get")) {
60
+			$this->Get();
61
+		} else if (!strcasecmp($this->method, "get_dsm_thumb")) {
62
+			$this->GetDSMThumb();
63
+		}
64
+	}
65
+
66
+	private function Get()
67
+	{
68
+		global $SYNOPHOTO_ALLOW_ID_TYPE;
69
+		global $SYNOPHOTO_ID_TYPE_LEN;
70
+		$ret = false;
71
+
72
+		// return when lack of params
73
+		if (!isset($_REQUEST['id']) || !isset($_REQUEST['size'])) {
74
+			$this->SetError(PHOTOSTATION_THUMB_BAD_PARAMS);
75
+			goto End;
76
+		}
77
+
78
+		// check params
79
+		$id = $_REQUEST['id'];
80
+		$id_arr = explode('_', $id);
81
+		if ($id_arr[0] !== 'photo' && $id_arr[0] !== 'video') {
82
+			// require albumutil.php before variable $type is assigned
83
+			// due to "include/photo/album_util.php" will read variable $type and lead to check timeout
84
+			require_once('albumutil.php');
85
+
86
+			session_write_close();
87
+		}
88
+
89
+		$type = $id_arr[0];
90
+		if (!in_array($type, $SYNOPHOTO_ALLOW_ID_TYPE)) {
91
+			$this->SetError(PHOTOSTATION_THUMB_BAD_PARAMS);
92
+			goto End;
93
+		}
94
+		if (count($id_arr) !== $SYNOPHOTO_ID_TYPE_LEN[$type]) {
95
+			$this->SetError(PHOTOSTATION_THUMB_BAD_PARAMS);
96
+			goto End;
97
+		}
98
+		$size = $_REQUEST['size'];
99
+		if ('small' !== $size && 'large' !== $size && 'preview' !== $size) {
100
+			$this->SetError(PHOTOSTATION_THUMB_BAD_PARAMS);
101
+			goto End;
102
+		}
103
+
104
+		$publicShareId = NULL;
105
+		if (isset($_REQUEST['public_share_id']) || $id_arr[0] === 'publicshare') {
106
+			$publicShareId = isset($_REQUEST['public_share_id']) ? $_REQUEST['public_share_id'] : $id_arr[1];
107
+			if (!SharedAlbum::CheckPublicSharePermission($publicShareId)) {
108
+				$this->SetError(PHOTOSTATION_THUMB_ACCESS_DENY);
109
+				goto End;
110
+			}
111
+			$publicShareInfo = SharedAlbum::GetInfoByPublicShare($publicShareId);
112
+			if (NULL === $publicShareInfo || 'valid' !== $publicShareInfo['share_status']) {
113
+				$this->SetError(PHOTOSTATION_THUMB_ACCESS_DENY);
114
+				goto End;
115
+			}
116
+		}
117
+		if ($publicShareId !== NULL && ($type === 'photo' || $type === 'video')) {
118
+			$albumName = @pack('H*', $id_arr[1]);
119
+			$fileName = @pack('H*', $id_arr[2]);
120
+			if (!csSYNOPhotoMisc::CheckAlbumAccessible($albumName, false, $publicShareId)) {
121
+				$this->SetError(PHOTOSTATION_THUMB_ACCESS_DENY);
122
+				goto End;
123
+			}
124
+			$filePath = ('/' === $albumName ? "" : $albumName . "/") . $fileName;
125
+			if (!SharedAlbum::CheckSharedAlbumItemValid($filePath, $type, $publicShareId)) {
126
+				$this->SetError(PHOTOSTATION_THUMB_ACCESS_DENY);
127
+				goto End;
128
+			}
129
+		}
130
+
131
+		// start to construct thumbnail path from the given id
132
+		$dir = '';
133
+		$sharename = '';
134
+		$fileName = '';
135
+		$albumName = '';
136
+		$url = '';
137
+		$path = '';
138
+		$isPublic = false;
139
+		if (SYNOPHOTO_UNCONFIRM_TAG_ID === $id) {
140
+			$list = AlbumAPIUtil::GetUnConfirmItemList(1, 0);
141
+			$keys = array_keys($list);
142
+			$fullPath = $keys[0];
143
+			$dir = dirname($fullPath);
144
+			$albumName = substr($dir, strlen(SYNOPHOTO_SERVICE_REAL_DIR.'/'));
145
+			$fileName = basename($fullPath);
146
+			$sharename = $albumName;
147
+		} else if ('photo' === $type || 'video' === $type) {
148
+			$sharename = $albumName = @pack('H*', $id_arr[1]);
149
+			$dir = SYNOPHOTO_SERVICE_REAL_DIR . ('/' === $albumName ? "" : "/" . $albumName);
150
+			$fileName = @pack('H*', $id_arr[2]);
151
+		} elseif ('smart' === $type) {
152
+			$albumName = @pack('H*', $id_arr[1]);
153
+			$album = SmartAlbum::GetSmartAlbumInstance()->GetCoverOfSmartAlbumByName($albumName);
154
+			if (!$album['success']) {
155
+				$this->SetError(PHOTOSTATION_THUMB_ACCESS_DENY);
156
+				goto End;
157
+			}
158
+			$cover_path = $album['cover']['path'];
159
+			if ('' !== $cover_path) {
160
+				$dir = substr($cover_path, 0, strrpos($cover_path, '/'));
161
+				$fileName = basename($cover_path);
162
+			} else {
163
+				$path = SYNO_PKG_DIR . "/target" . SYNOPHOTO_IMG_EMPTY_HIGH_QUALITY;
164
+			}
165
+			$isPublic = true;
166
+		} else if ('sharedalbum' === $type || 'publicshare' === $type) {
167
+			if ('sharedalbum' === $type) {
168
+				if ($id_arr[1] === 'single') {
169
+					$sharedAlbum = SharedAlbum::GetHiddenAlbumInfo();
170
+					if ($sharedAlbum !== NULL) {
171
+						$id_arr[1] = explode("_", $sharedAlbum['id'])[1];
172
+					}
173
+				}
174
+				$cover = SharedAlbum::GetSharedAlbumCover($id_arr[1]);
175
+			} else {
176
+				$cover = SharedAlbum::GetSharedAlbumCover(NULL, $id_arr[1]);
177
+			}
178
+			if (NULL === $cover) {
179
+				$this->SetError(PHOTOSTATION_THUMB_ACCESS_DENY);
180
+				goto End;
181
+			}
182
+			$path_parts = pathinfo($cover['path']);
183
+			$fileName = $path_parts['basename'];
184
+			$sharename = substr($path_parts['dirname'], strlen(SYNOPHOTO_SERVICE_REAL_DIR_PATH));
185
+			$dir = $path_parts['dirname'];
186
+			if ('root' === SYNOPHOTO_ADMIN_USER) {
187
+				if ($dir === SYNOPHOTO_SERVICE_REAL_DIR) {
188
+					$albumName = '/';
189
+				} else {
190
+					$albumName = substr($dir, strlen(SYNOPHOTO_SERVICE_REAL_DIR) + 1);
191
+				}
192
+			} else {
193
+				$albumName = "." === $dir ? "/" : $dir;
194
+				$dir = SYNOPHOTO_SERVICE_REAL_DIR . "/" . $dir;
195
+			}
196
+			if (empty($albumName) || empty($fileName)) {
197
+				$this->SetError(PHOTOSTATION_THUMB_NO_COVER);
198
+				goto End;
199
+			}
200
+		} else {
201
+			if ('album' === $type) {
202
+				$albumName = @pack('H*', $id_arr[1]);
203
+				$album = csSYNOPhotoAlbum::GetAlbumInstance()->GetAlbumCover($albumName);
204
+			} elseif ('virtual' === $type) {
205
+				$isPublic = true;
206
+				$album = csSYNOPhotoBrowse::GetBrowseInstance()->GetVirtualAlbumThumb($id_arr[1]);
207
+			} elseif ('tag' === $type) {
208
+				$isPublic = true;
209
+				$album = csSYNOPhotoAlbum::GetAlbumInstance()->GetLabelAlbumCover($id_arr[1]);
210
+            } elseif ('defaultsmart' === $type) {
211
+                $index = 0;
212
+                if ('people' === $id_arr[1]) {
213
+                    $index = 0;
214
+                } elseif ('geo' === $id_arr[1]) {
215
+                    $index = 1;
216
+                } elseif ('desc' === $id_arr[1]) {
217
+                    $index = 2;
218
+                }
219
+                $isPublic = true;
220
+                $categoryArr = array();
221
+                array_push($categoryArr, $index);
222
+                $result = SYNOPHOTO_LABEL_UTIL_GetAllLabel(false, $categoryArr);
223
+                $album = csSYNOPhotoAlbum::GetAlbumInstance()->GetLabelAlbumCover($result[$index][0]['id']);
224
+            }
225
+			$url = $album['bigCover']['src'];
226
+
227
+			if (!empty($album['coverPath'])) {
228
+				// extract cover info from coverPath
229
+				$relativePath = substr($album['coverPath'], strlen(SYNOPHOTO_SERVICE_REAL_DIR_PATH));
230
+				$path_parts = pathinfo($relativePath);
231
+				$fileName = $path_parts['basename'];
232
+				$albumName = $path_parts['dirname'];
233
+				if (in_array($albumName, array('.', ''), true)) {
234
+					$albumName = '/';
235
+				}
236
+				$dir = SYNOPHOTO_SERVICE_REAL_DIR . "/" . $albumName;
237
+			} else {
238
+				$albumName = @pack('H*', substr($url, strpos($url, 'dir=') + 4, strpos($url, '&name') - strpos($url, 'dir=') - 4));
239
+				$fileName = @pack('H*', substr($url, strpos($url, 'name=') + 5, strpos($url, '&type') - strpos($url, 'name=') - 5));
240
+				$dir = SYNOPHOTO_SERVICE_REAL_DIR . "/" . $albumName;
241
+			}
242
+
243
+			if ("" === $albumName || empty($fileName)) {
244
+				$this->SetError(PHOTOSTATION_THUMB_NO_COVER);
245
+				goto End;
246
+			}
247
+		}
248
+
249
+		// check access right
250
+		if (!$isPublic && !csSYNOPhotoMisc::CheckAlbumAccessible($albumName, false, $publicShareId)) {
251
+			$this->SetError(PHOTOSTATION_THUMB_ACCESS_DENY);
252
+			goto End;
253
+		}
254
+
255
+		// construct corresonding thumbnail path
256
+		$hasSmall = $hasLarge = true;
257
+		$blConversion = csSYNOPhotoMisc::IsAlbumAllowConversion($sharename);
258
+		if ('' === $path) {
259
+			if ('small' === $size) {
260
+				$hasSmall = SYNOPhotoEA::checkFilePathByDirFile($dir, $fileName, SYNOPhotoEA::FILE_THUMB_M, $path);
261
+			}
262
+			if ('large' === $size) {
263
+				$hasLarge = SYNOPhotoEA::checkFilePathByDirFile($dir, $fileName, SYNOPhotoEA::FILE_THUMB_XL, $path);
264
+				if (!$hasLarge) {
265
+					$hasLarge = SYNOPhotoEA::checkFilePathByDirFile($dir, $fileName, SYNOPhotoEA::FILE_THUMB_LARGE_PREVIEW, $path);
266
+				}
267
+			}
268
+			if ('preview' === $size || (!$blConversion && !$hasSmall)) {
269
+				SYNOPhotoEA::checkFilePathByDirFile($dir, $fileName, SYNOPhotoEA::FILE_THUMB_PREVIEW, $path);
270
+			}
271
+			if (!$blConversion && !$hasLarge && in_array(strtolower(pathinfo($fileName, PATHINFO_EXTENSION)), array('jpeg', 'bmp', 'png', 'jpg', 'jpe'))) {
272
+				$path = "$dir/$fileName";
273
+			}
274
+		}
275
+
276
+		// set mime type, default is jpeg
277
+		$mime = 'image/jpeg';
278
+		preg_match('/\.([^\.]*?)$/', $fileName, $matches);
279
+		$extension = strtolower($matches[1]);
280
+		// special case for gif, return original file
281
+		if ('gif' === $extension) {
282
+			$path = $dir . "/" . $fileName;
283
+			$mime = 'image/gif';
284
+		}
285
+
286
+		if (!file_exists($path)) {
287
+			$this->SetError(PHOTOSTATION_THUMB_FILE_NOT_EXISTS);
288
+			goto End;
289
+		}
290
+
291
+		@header("Cache-Control: max-age=".(3600*24*7));
292
+
293
+		// check last-modified value
294
+		$lastModified = $this->GetLastModified($path);
295
+		if ($lastModified) {
296
+			if ($this->CheckLastModified($lastModified)) {
297
+				@header("HTTP/1.1 304 Not Modified");
298
+				return;
299
+			} else {
300
+				@header("Last-Modified: " . $lastModified);
301
+			}
302
+		}
303
+
304
+		// output binary
305
+		@header("Content-Type: $mime");
306
+		$filename = $fileName;
307
+
308
+		// Windows Phone WebHttpReponse cannot accept non-ascii character in response header
309
+		// Replace non-ascii character with '_'
310
+		if (preg_match("/Windows Phone OS/i", $_SERVER['HTTP_USER_AGENT'])) {
311
+			$filename = preg_replace('/[^(\x20-\x7F)]*/', '_', $filename);
312
+		}
313
+		@header('Content-Disposition: inline; filename="'.addslashes($filename).'"');
314
+
315
+		$this->SYNOPHOTO_CONVERT_Rrange_Download($path);
316
+		exit;
317
+
318
+		End:
319
+			return $ret;
320
+	}
321
+
322
+	private function GetPhotoMimeType($path)
323
+	{
324
+		$SYNOPHOTO_THUMB_MIME = array(
325
+			'jpg' => 'image/jpeg',
326
+			'jpeg' => 'image/jpeg',
327
+			'jpe' => 'image/jpeg',
328
+			'bmp' => 'image/bmp',
329
+			'gif' => 'image/gif',
330
+			'png' => 'image/png'
331
+		);
332
+
333
+		$path = strtolower($path);
334
+		$path_parts = pathinfo($path);
335
+		$extension = $path_parts['extension'];
336
+		if (array_key_exists($extension, $SYNOPHOTO_THUMB_MIME)) {
337
+			return $SYNOPHOTO_PICASA_MIME[$extension];
338
+		}
339
+		return null;
340
+	}
341
+
342
+	private function GetLoginUsername() {
343
+		if (!isset($_SESSION[SYNOPHOTO_ADMIN_USER]) || (!isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']) && !isset($_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_user']))) {
344
+			return "Guest";
345
+		}
346
+		$username = 'admin';
347
+		if (SYNOPHOTO_ADMIN_USER !== "root") {
348
+			$username = substr(SYNOPHOTO_ADMIN_USER, 1);
349
+		}
350
+		if (isset($_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_user'])) {
351
+			$username = $_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_user'];
352
+			if (!isset($_SESSION[SYNOPHOTO_ADMIN_USER]['use_dsm_account']) || $_SESSION[SYNOPHOTO_ADMIN_USER]['use_dsm_account'] != '1') {
353
+				$username = "Guest";
354
+			}
355
+		}
356
+		return $username;
357
+	}
358
+
359
+	private function GetDSMThumb()
360
+	{
361
+		$ret = false;
362
+
363
+		// return when lack of params
364
+		$id = $this->DecodeItemId($_REQUEST['id'], 'dsmshare_');
365
+		if (false === $id) {
366
+			$this->SetError(PHOTOSTATION_THUMB_BAD_PARAMS);
367
+			goto End;
368
+		}
369
+
370
+		$command = "/usr/syno/bin/synophoto_extract_preview ".escapeshellarg(urldecode($id))." --stdout ".escapeshellarg($this->GetLoginUsername());
371
+		$output = @shell_exec($command);
372
+
373
+		if (($pos = strpos($output, "\n")) === false) {
374
+			//output default image
375
+			$imagefp = fopen("../photo_new/images/Window/image_default_photo_dark.png", "rb");
376
+			@header("Content-Type: image/png");
377
+			@header('Content-Disposition: inline; filename="image_default_photo_dark.png"');
378
+			fpassthru($imagefp);
379
+			exit;
380
+		}
381
+		$thumbPath = substr($output, 0, $pos);
382
+		$mime = $this->GetPhotoMimeType($thumbPath);
383
+		@header("Cache-Control: public, max-age=86400");
384
+		@header("Content-Type: $mime");
385
+		@header('Content-Disposition: inline; filename="'.addslashes($fileName).'"');
386
+		echo substr($output, $pos+1);
387
+		//echo $thumbName;
388
+		exit;
389
+	End:
390
+		return $ret;
391
+	}
392
+
393
+	private function GetLastModified($thumbPath)
394
+	{
395
+		$filest = @stat($thumbPath);
396
+
397
+		if ($filest !== false) {
398
+			$lastModified = gmdate('D, d M Y H:i:s', $filest[9]) . ' GMT';
399
+			return $lastModified;
400
+		} else {
401
+			return false;
402
+		}
403
+	}
404
+
405
+	private function CheckLastModified($lastModified)
406
+	{
407
+		if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
408
+			$ims = preg_replace('/;.*$/', '', $_SERVER['HTTP_IF_MODIFIED_SINCE'] );
409
+			if ($ims === $lastModified) {
410
+				return true;
411
+			}
412
+		}
413
+
414
+		return false;
415
+	}
416
+
417
+	private function SYNOPHOTO_CONVERT_Rrange_Download($file)
418
+	{
419
+		$fp = @fopen($file, 'rb');
420
+		if (FALSE == $fp) {
421
+			return FALSE;
422
+		}
423
+
424
+		$size   = filesize($file); // File size
425
+		$length = $size;		   // Content length
426
+		$start  = 0;			   // Start byte
427
+		$end	= $size - 1;	   // End byte
428
+		// Now that we've gotten so far without errors we send the accept range header
429
+		// At the moment we only support single ranges.
430
+		// Multiple ranges requires some more work to ensure it works correctly
431
+		// and comply with the spesifications: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
432
+		//
433
+		// Multirange support annouces itself with:
434
+		// header('Accept-Ranges: bytes');
435
+		//
436
+		// Multirange content must be sent with multipart/byteranges mediatype,
437
+		// (mediatype = mimetype)
438
+		// as well as a boundry header to indicate the various chunks of data.
439
+		//
440
+		//header("Accept-Ranges: 0-$length");
441
+		header('Accept-Ranges: bytes');
442
+		// multipart/byteranges
443
+		// http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
444
+		if (isset($_SERVER['HTTP_RANGE'])) {
445
+			$c_start = $start;
446
+			$c_end   = $end;
447
+			// Extract the range string
448
+			list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);
449
+			// Make sure the client hasn't sent us a multibyte range
450
+			if (strpos($range, ',') !== false) {
451
+
452
+				// (?) Shoud this be issued here, or should the first
453
+				// range be used? Or should the header be ignored and
454
+				// we output the whole content?
455
+				header('HTTP/1.1 416 Requested Range Not Satisfiable');
456
+				header("Content-Range: bytes $start-$end/$size");
457
+				// (?) Echo some info to the client?
458
+				exit;
459
+			}
460
+			// If the range starts with an '-' we start from the beginning
461
+			// If not, we forward the file pointer
462
+			// And make sure to get the end byte if spesified
463
+			if ($range{0} == '-') {
464
+
465
+				// The n-number of the last bytes is requested
466
+				$c_start = $size - substr($range, 1);
467
+			}
468
+			else {
469
+
470
+				$range  = explode('-', $range);
471
+				$c_start = $range[0];
472
+				$c_end   = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size;
473
+			}
474
+			// Check the range and make sure it's treated according to the specs.
475
+			// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
476
+			//
477
+			// End bytes can not be larger than $end.
478
+			$c_end = ($c_end > $end) ? $end : $c_end;
479
+			// Validate the requested range and return an error if it's not correct.
480
+			if ($c_start > $c_end || $c_start > $size - 1 || $c_end >= $size) {
481
+
482
+				header('HTTP/1.1 416 Requested Range Not Satisfiable');
483
+				header("Content-Range: bytes $start-$end/$size");
484
+				// (?) Echo some info to the client?
485
+				exit;
486
+			}
487
+			$start  = $c_start;
488
+			$end	= $c_end;
489
+			$length = $end - $start + 1; // Calculate new content length
490
+			fseek($fp, $start);
491
+			header('HTTP/1.1 206 Partial Content');
492
+		}
493
+		// Notify the client the byte range we'll be outputting
494
+		if (preg_match("/Windows Mobile/i", $_SERVER['HTTP_USER_AGENT'])) {
495
+			header("Content-Range: bytes $start-$end/$size");
496
+		}
497
+		header("Content-Length: $length");
498
+
499
+		// Start buffered download
500
+		$buffer = 1024 * 8;
501
+		while(!feof($fp) && ($p = ftell($fp)) <= $end) {
502
+
503
+			if ($p + $buffer > $end) {
504
+
505
+				// In case we're only outputtin a chunk, make sure we don't
506
+				// read past the length
507
+				$buffer = $end - $p + 1;
508
+			}
509
+
510
+			echo fread($fp, $buffer);
511
+			flush(); // Free up memory. Otherwise large files will trigger PHP's memory limit.
512
+		}
513
+		fclose($fp);
514
+	}
515
+}
516
+
517
+$api = new ThumbAPI();
518
+$api->Run();

+ 221
- 0
PhotoStation API/webapi/timeline.php 查看文件

@@ -0,0 +1,221 @@
1
+<?PHP
2
+
3
+require_once('photo.inc.php');
4
+require_once('photoutil.php');
5
+require_once('albumutil.php');
6
+
7
+class TimelineAPI extends WebAPI
8
+{
9
+	function __construct()
10
+	{
11
+		parent::__construct(SZ_WEBAPI_API_DESCRIPTION_PATH);
12
+	}
13
+
14
+	protected function Process()
15
+	{
16
+		csSYNOPhotoDB::GetDBInstance()->SetSessionCache();
17
+		if (!strcasecmp($this->method, "getindex")) {
18
+			$this->GetIndex();
19
+		}
20
+	}
21
+
22
+	private function GetIndex()
23
+	{
24
+		$ret = false;
25
+		$resp = array();
26
+		$dates = array();
27
+		$datesCount = array();
28
+		$year_index = array();
29
+		$month_index = array();
30
+
31
+		/* required params setting */
32
+		$type = $_REQUEST['type'];
33
+		if (!strstr($type, 'photo') && !strstr($type, 'video')) {
34
+			$this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
35
+			goto End;
36
+		}
37
+		$filter_smart = isset($_REQUEST['filter_smart']) ? $_REQUEST['filter_smart'] : '';
38
+		$filter_tag = isset($_REQUEST['filter_tag']) ? $_REQUEST['filter_tag'] : '';
39
+		$filter_album = isset($_REQUEST['filter_album']) ? $_REQUEST['filter_album'] : '';
40
+		$sort_direction = 'asc' === $_REQUEST['sort_direction'] ? 'asc' : 'desc';
41
+
42
+		if ('' !== $filter_album) {
43
+			$arr = explode('_', $filter_album);
44
+			if ('album' !== $arr[0]) {
45
+				$this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
46
+				goto End;
47
+			}
48
+			if (!csSynoPhotoMisc::CheckPathValid($albumPath = SYNOPHOTO_SERVICE_REAL_DIR . "/" . @pack('H*', $arr[1]))) {
49
+				$this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
50
+				goto End;
51
+			}
52
+			$personalAlbumPath = @pack('H*', $arr[1]);
53
+		} else {
54
+			$albumPath = SYNOPHOTO_SERVICE_REAL_DIR;
55
+			$personalAlbumPath = '';
56
+		}
57
+
58
+		if ('root' !== SYNOPHOTO_ADMIN_USER) { //personal photo station
59
+			if ('' !== $filter_album) {
60
+				$escPath = $personalAlbumPath . '/%';
61
+			} else {
62
+				$escPath = $personalAlbumPath . '%';
63
+			}
64
+		} else {
65
+			$escPath = $albumPath . '/%';
66
+		}
67
+
68
+		if ('' === $filter_smart) {
69
+			/* filter by albumname and tag */
70
+			$sqlParam = array();
71
+			$photoQuery = '';
72
+
73
+			$albumCondition = csSYNOPhotoMisc::GetAccessibleAlbumQueryConditionWithExcludeFormat();
74
+			if (!$albumCondition['albumCond']) {
75
+				$this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
76
+				goto End;
77
+			}
78
+
79
+			if (strstr($type, 'photo')) {
80
+				$photoQuery .= "SELECT p.timetaken as date ";
81
+				$photoQuery .= "FROM photo_image as p ";
82
+				$photoQuery .= "WHERE p.path LIKE ? ";
83
+				$sqlParam[] = $escPath;
84
+
85
+				if ('' !== $filter_tag) {
86
+					$photoQuery .= "AND p.id IN (SELECT photo_image_label.image_id FROM photo_image_label LEFT JOIN photo_image ON photo_image_label.image_id = photo_image.id ";
87
+					$photoQuery .= "WHERE (" . PhotoAPIUtil::getLabelIDConstraint($filter_tag) . ")) ";
88
+				}
89
+
90
+				$photoQuery .= "AND {$albumCondition['albumCond']} ";
91
+				$sqlParam = array_merge($sqlParam, $albumCondition['sqlParam']);
92
+			}
93
+			$videoQuery = '';
94
+			if (strstr($type, 'video')) {
95
+				$videoQuery = "SELECT v.mdate as date ";
96
+				$videoQuery .= "FROM video v ";
97
+				$videoQuery .= "WHERE v.path LIKE ? ";
98
+				$sqlParam[] = $escPath;
99
+
100
+				if ('' !== $filter_tag) {
101
+					$videoQuery .= "AND v.path IN (SELECT video_path FROM photo_video_label LEFT JOIN video ON photo_video_label.video_path = video.path ";
102
+					$videoQuery .= "WHERE (" . PhotoAPIUtil::getLabelIDConstraint($filter_tag) . "))";
103
+				}
104
+
105
+				$videoQuery .= "AND {$albumCondition['albumCond']} ";
106
+				$sqlParam = array_merge($sqlParam, $albumCondition['sqlParam']);
107
+			}
108
+
109
+			if ('' !== $photoQuery && '' !== $videoQuery) {
110
+				$query = "SELECT date(g.date) as date, COUNT(*) AS count from ($photoQuery UNION ALL $videoQuery) as g";
111
+			} elseif ('' === $photoQuery) {
112
+				$query = "SELECT date(g.date) as date, COUNT(*) AS count from ($videoQuery) as g";
113
+			} else {
114
+				$query = "SELECT date(g.date) as date, COUNT(*) AS count from ($photoQuery) as g";
115
+			}
116
+
117
+			$query .= " GROUP BY date(g.date) ORDER BY date(g.date) desc";
118
+			$db_result = PHOTO_DB_Query($query, $sqlParam);
119
+			while ($row = PHOTO_DB_FetchRow($db_result)) {
120
+				if (strlen($row["date"]) < 10) {
121
+					$date = '1970-01-01';
122
+				} else {
123
+					$date = $row["date"];
124
+				}
125
+				if (FALSE == in_array($date, $dates)) {
126
+					$dates[] = $date;
127
+				}
128
+				$datesCount[$date] = intval($row["count"]);
129
+			}
130
+		} else {
131
+			/* for smart album */
132
+			$arr = explode('_', $filter_smart);
133
+			if ('smart' !== $arr[0]) {
134
+				$this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
135
+				goto End;
136
+			}
137
+			$albumName = @pack('H*', $arr[1]);
138
+			$data = array(
139
+				"name" => $albumName,
140
+				"offset" => 0,
141
+				"limit" => -1
142
+			);
143
+			$smart = json_decode(SmartAlbum::GetSmartAlbumInstance()->ListItem(json_encode($data), 'takendate', 'desc'), true);
144
+			if (!$smart['success']) {
145
+				$this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
146
+				goto End;
147
+			}
148
+			$itemLists = $smart['data']['itemList'];
149
+
150
+			foreach ($itemLists as $row) {
151
+				if (!strstr($type, $row['type'])) {
152
+					continue;
153
+				}
154
+				if (false !== ($item = PhotoAPIUtil::getItemByPath($row['path'], array(), $row['type'], false))) {
155
+					$takentime = $item['info']['takendate'];
156
+					$date = substr($takentime, 0, 10);
157
+					if (strlen($date) < 10) {
158
+						$date = '1970-01-01';
159
+					}
160
+					if (FALSE == in_array($date, $dates)) {
161
+						$dates[] = $date;
162
+					}
163
+					if (!isset($datesCount[$date])) {
164
+						$datesCount[$date] = 0;
165
+					}
166
+					$datesCount[$date]++;
167
+				}
168
+			}
169
+		}
170
+
171
+		$sorted_dates = array();
172
+		if ('asc' === $sort_direction) {
173
+			$unknownDate = false;
174
+			asort($dates);
175
+			foreach ($dates as $date) {
176
+				if ($date !== '1970-01-01') {
177
+					$sorted_dates[] = $date;
178
+				} else {
179
+					$unknownDate = true;
180
+				}
181
+			}
182
+			if ($unknownDate) {
183
+				$sorted_dates[] = '1970-01-01';
184
+			}
185
+		} else {
186
+			arsort($dates);
187
+			foreach ($dates as $date) {
188
+				$sorted_dates[] = $date;
189
+			}
190
+		}
191
+		$dates = $sorted_dates;
192
+
193
+		$objs = array();
194
+		for ($i=0; $i<count($dates); $i++) {
195
+			$item = array();
196
+			$item['date'] = $dates[$i];
197
+			$item['year'] = substr($dates[$i], 0, 4);
198
+			$item['month'] = substr($dates[$i], 5, 2);
199
+			$item['day'] = substr($dates[$i], 8, 2);
200
+			$item['index'] = $i;
201
+			$item['count'] = $datesCount[$dates[$i]];
202
+			$objs[] = $item;
203
+		}
204
+
205
+		// set return data
206
+		$resp['total'] = count($dates);
207
+		$resp['date'] = $dates;
208
+		$resp['date_objs'] = $objs;
209
+
210
+		$this->SetResponse($resp);
211
+
212
+
213
+		$ret = true;
214
+		End:
215
+			return $ret;
216
+	}
217
+
218
+}
219
+
220
+$api = new TimelineAPI();
221
+$api->Run();

+ 0
- 0
PhotoStation API/webapi/webabi.lua 查看文件


+ 6
- 0
PhotoStation API/webapi/webapi.conf.php 查看文件

@@ -0,0 +1,6 @@
1
+<?php
2
+
3
+    define('CONF_WEBAPI_SERVICE_PATH', '/var/packages/PhotoStation/target/photo/webapi');
4
+    define('CONF_API_DESCRIPTION_DIR', CONF_WEBAPI_SERVICE_PATH);
5
+
6
+?>

+ 142
- 0
PhotoStation API/webapi/webapi.inc.php 查看文件

@@ -0,0 +1,142 @@
1
+<?php
2
+    require_once('webapi.conf.php');
3
+	require_once('formulautil.php');
4
+
5
+    define('SZ_WEBAPI_SDK_DIR',     CONF_WEBAPI_SERVICE_PATH.'/sdk');
6
+    define('SZ_WEBAPI_CLASS_PATH',  SZ_WEBAPI_SDK_DIR.'/WebAPI.php');
7
+    define('SZ_WEBAPI_API_DESCRIPTION_DIR', CONF_WEBAPI_SERVICE_PATH);
8
+    define('SZ_API_DESCRIPTION', 'PhotoStation.api');
9
+    define('SZ_WEBAPI_API_DESCRIPTION_PATH', SZ_WEBAPI_API_DESCRIPTION_DIR.'/'.SZ_API_DESCRIPTION);
10
+
11
+    // SYNO.PhotoStation.Info (401-405)
12
+
13
+    // SYNO.PhotoStation.Auth (406-415)
14
+    define('PHOTOSTATION_AUTH_LOGIN_NOPRIVILEGE', 406);
15
+    define('PHOTOSTATION_AUTH_LOGIN_ERROR', 407);
16
+    define('PHOTOSTATION_AUTH_LOGIN_DISABLE_ACCOUNT', 408);
17
+    define('PHOTOSTATION_AUTH_LOGIN_GUEST_ERROR', 409);
18
+
19
+    // SYNO.PhotoStaion.Album (416-425)
20
+    define('PHOTOSTATION_ALBUM_PASSWORD_ERROR', 416);
21
+    define('PHOTOSTATION_ALBUM_NO_ACCESS_RIGHT', 417);
22
+    define('PHOTOSTATION_ALBUM_NO_UPLOAD_RIGHT', 418);
23
+    define('PHOTOSTATION_ALBUM_NO_MANAGE_RIGHT', 419);
24
+    define('PHOTOSTATION_ALBUM_NOT_ADMIN', 420);
25
+    define('PHOTOSTATION_ALBUM_HAS_EXIST', 421);
26
+    define('PHOTOSTATION_ALBUM_CREATE_FAIL', 422);
27
+    define('PHOTOSTATION_ALBUM_EDIT_FAIL', 423);
28
+    define('PHOTOSTATION_ALBUM_DELETE_FAIL', 424);
29
+    define('PHOTOSTATION_ALBUM_SELECT_CONFLICT', 425);
30
+
31
+    // SYNO.PhotoStation.Permission (426-435)
32
+    define('PHOTOSTATION_PERMISSION_BAD_PARAMS', 426);
33
+    define('PHOTOSTATION_PERMISSION_ACCESS_DENY', 427);
34
+
35
+    // SYNO.PhotoStation.Tag (436-445)
36
+    define('PHOTOSTATION_TAG_LIST_FAIL', 436);
37
+    define('PHOTOSTATION_TAG_GETINFO_FAIL', 437);
38
+    define('PHOTOSTATION_TAG_CREATE_FAIL', 438);
39
+    define('PHOTOSTATION_TAG_EDIT_FAIL', 439);
40
+    define('PHOTOSTATION_TAG_ACCESS_DENY', 440);
41
+    define('PHOTOSTATION_TAG_HAS_EXIST', 441);
42
+    define('PHOTOSTATION_TAG_SEARCH_FAIL', 442);
43
+
44
+    // SYNO.PhotoStation.SmartAlbum (446-455)
45
+    define('PHOTOSTATION_SMARTALBUM_CREATE_FAIL', 446);
46
+    define('PHOTOSTATION_SMARTALBUM_EDIT_FAIL', 447);
47
+    define('PHOTOSTATION_SMARTALBUM_ACCESS_DENY', 448);
48
+    define('PHOTOSTATION_SMARTALBUM_NOT_EXIST', 449);
49
+    define('PHOTOSTATION_SMARTALBUM_TAG_NOT_EXIST', 450);
50
+    define('PHOTOSTATION_SMARTALBUM_CREATE_FAIL_EXIST', 451);
51
+
52
+    // SYNO.PhotoStation.Photo (456-465)
53
+    define('PHOTOSTATION_PHOTO_BAD_PARAMS', 456);
54
+    define('PHOTOSTATION_PHOTO_ACCESS_DENY', 457);
55
+    define('PHOTOSTATION_PHOTO_SELECT_CONFLICT', 458);
56
+
57
+    // SYNO.PhotoStation.PhotoTag (466-475)
58
+    define('PHOTOSTATION_PHOTO_TAG_ACCESS_DENY', 466);
59
+    define('PHOTOSTATION_PHOTO_TAG_NOT_EXIST', 467);
60
+    define('PHOTOSTATION_PHOTO_TAG_DUPLICATE', 468);
61
+    define('PHOTOSTATION_PHOTO_TAG_VIDEO_NOT_EXIST', 469);
62
+    define('PHOTOSTATION_PHOTO_TAG_ADD_GEO_DESC_FAIL', 470);
63
+    define('PHOTOSTATION_PHOTO_TAG_ADD_PEOPLE_FAIL', 471);
64
+    define('PHOTOSTATION_PHOTO_TAG_DELETE_FAIL', 472);
65
+    define('PHOTOSTATION_PHOTO_TAG_PEOPLE_TAG_CONFIRM_FAIL', 473);
66
+
67
+    // SYNO.PhotoStation.Category (476-490)
68
+    define('PHOTOSTATION_CATEGORY_ACCESS_DENY', 476);
69
+    define('PHOTOSTATION_CATEGORY_WRONG_ID_FORMAT', 477);
70
+    define('PHOTOSTATION_CATEGORY_GETINFO_FAIL', 478);
71
+    define('PHOTOSTATION_CATEGORY_CREATE_FAIL', 479);
72
+    define('PHOTOSTATION_CATEGORY_DELETE_FAIL', 480);
73
+    define('PHOTOSTATION_CATEGORY_EDIT_FAIL', 481);
74
+    define('PHOTOSTATION_CATEGORY_ARRANGE_FAIL', 482);
75
+    define('PHOTOSTATION_CATEGORY_ADD_ITEM_FAIL', 483);
76
+    define('PHOTOSTATION_CATEGORY_LIST_ITEM_FAIL', 484);
77
+    define('PHOTOSTATION_CATEGORY_REMOVE_ITEM_FAIL', 485);
78
+    define('PHOTOSTATION_CATEGORY_ARRANGE_ITEM_FAIL', 486);
79
+    define('PHOTOSTATION_CATEGORY_DUPLICATE', 487);
80
+
81
+    // SYNO.PhotoStation.Comment (491-495)
82
+    define('PHOTOSTATION_COMMENT_VALIDATE_FAIL', 491);
83
+    define('PHOTOSTATION_COMMENT_ACCESS_DENY', 492);
84
+    define('PHOTOSTATION_COMMENT_CREATE_FAIL', 493);
85
+
86
+    // SYNO.PhotoStation.Thumb (496-505)
87
+    define('PHOTOSTATION_THUMB_BAD_PARAMS', 501);
88
+    define('PHOTOSTATION_THUMB_ACCESS_DENY', 502);
89
+    define('PHOTOSTATION_THUMB_NO_COVER', 503);
90
+    define('PHOTOSTATION_THUMB_FILE_NOT_EXISTS', 504);
91
+
92
+    // SYNO.PhotoStation.Download (506-515)
93
+    define('PHOTOSTATION_DOWNLOAD_BAD_PARAMS', 506);
94
+    define('PHOTOSTATION_DOWNLOAD_ACCESS_DENY', 507);
95
+    define('PHOTOSTATION_DOWNLOAD_CHDIR_ERROR', 508);
96
+
97
+    // SYNO.PhotoStation.File (516-525)
98
+    define('PHOTOSTATION_FILE_BAD_PARAMS', 516);
99
+    define('PHOTOSTATION_FILE_ACCESS_DENY', 517);
100
+    define('PHOTOSTATION_FILE_FILE_EXT_ERR', 518);
101
+    define('PHOTOSTATION_FILE_DIR_NOT_EXISTS', 519);
102
+    define('PHOTOSTATION_FILE_UPLOAD_ERROR', 520);
103
+    define('PHOTOSTATION_FILE_NO_FILE', 521);
104
+
105
+    // SYNO.PhotoStation.Cover (526-530)
106
+    define('PHOTOSTATION_COVER_ACCESS_DENY', 526);
107
+    define('PHOTOSTATION_COVER_ALBUM_NOT_EXIST', 527);
108
+    define('PHOTOSTATION_COVER_PHOTO_VIDEO_NOT_EXIST', 528);
109
+    define('PHOTOSTATION_COVER_PHOTO_VIDEO_NOT_IN_ALBUM', 529);
110
+    define('PHOTOSTATION_COVER_SET_FAIL', 530);
111
+
112
+    // SYNO.PhotoStation.Rotate (531-535)
113
+    define('PHOTOSTATION_ROTATE_ACCESS_DENY', 531);
114
+    define('PHOTOSTATION_ROTATE_SET_FAIL', 532);
115
+
116
+    // SYNO.PhotoStation.SlideshowMusic (536-545)
117
+    define('PHOTOSTATION_SLIDESHOWMUSIC_ACCESS_DENY', 536);
118
+    define('PHOTOSTATION_SLIDESHOWMUSIC_SET_FAIL', 537);
119
+    define('PHOTOSTATION_SLIDESHOWMUSIC_FILE_EXT_ERR', 538);
120
+    define('PHOTOSTATION_SLIDESHOWMUSIC_UPLOAD_ERROR', 539);
121
+    define('PHOTOSTATION_SLIDESHOWMUSIC_NO_FILE', 540);
122
+    define('PHOTOSTATION_SLIDESHOWMUSIC_EXCEED_LIMIT', 541);
123
+
124
+    // SYNO.PhotoStation.DsmShare (546-550)
125
+    define('PHOTOSTATION_DSMSHARE_UPLOAD_ERROR', 546);
126
+    define('PHOTOSTATION_DSMSHARE_ACCESS_DENY', 547);
127
+
128
+    // SYNO.PhotoStation.SharedAlbum (551-560)
129
+    define('PHOTOSTATION_SHARED_ALBUM_ACCESS_DENY', 551);
130
+    define('PHOTOSTATION_SHARED_ALBUM_BAD_PARAMS', 552);
131
+    define('PHOTOSTATION_SHARED_ALBUM_HAS_EXISTED', 553);
132
+    define('PHOTOSTATION_SHARED_ALBUM_CREATE_FAIL', 554);
133
+    define('PHOTOSTATION_SHARED_ALBUM_NOT_EXISTS', 555);
134
+    define('PHOTOSTATION_SHARED_ALBUM_GET_INFO_ERROR', 556);
135
+    define('PHOTOSTATION_SHARED_ALBUM_LIST_ERROR', 557);
136
+
137
+    // SYNO.PhotoStation.Log (561-565)
138
+    define('PHOTOSTATION_LOG_ACCESS_DENY', 561);
139
+
140
+    // SYNO.PhotoStation.PATH (566-570)
141
+    define('PHOTOSTATION_PATH_ACCESS_DENY', 566);
142
+?>

+ 29
- 1
addon.py 查看文件

@@ -82,6 +82,8 @@ def main():
82 82
         items = []
83 83
         for f in content["items"]:
84 84
             label = f["info"]["title"]
85
+            if not label:
86
+                continue
85 87
             label2 = f["additional"]["file_location"] if "file_location" in f["additional"] else ""
86 88
             type = f["type"]
87 89
             oid2 = f["id"]
@@ -226,6 +228,7 @@ def main():
226 228
 
227 229
 def get_view_mode(vm):
228 230
     modes = {
231
+        #"skin.estuary.isl"
229 232
         "skin.estuary": {
230 233
             "None": None,
231 234
             "List": 50,
@@ -237,7 +240,32 @@ def get_view_mode(vm):
237 240
             "Wall": 500,
238 241
             "Banner": 501,
239 242
             "FanArt": 502
240
-        }
243
+        },
244
+        "skin.estuary.isl": {
245
+            "None": None,
246
+            "List": 50,
247
+            "Poster": 51,
248
+            "IconWall":52 ,
249
+            "Shift": 53,
250
+            "InfoWall": 54,
251
+            "WideList": 55,
252
+            "Wall": 500,
253
+            "Banner": 501,
254
+            "FanArt": 502
255
+        },
256
+        "skin.estuary.is": {
257
+            "None": None,
258
+            "List": 50,
259
+            "Poster": 51,
260
+            "IconWall":52 ,
261
+            "Shift": 53,
262
+            "InfoWall": 54,
263
+            "WideList": 55,
264
+            "Wall": 500,
265
+            "Banner": 501,
266
+            "FanArt": 502
267
+        },
268
+
241 269
     }
242 270
     skin = xbmc.getSkinDir()
243 271
     if skin in modes and vm in modes[skin]:

+ 1
- 1
addon.xml 查看文件

@@ -1,5 +1,5 @@
1 1
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2
-<addon version="0.1.16" id="plugin.image.photostation" name="PhotoStation" provider-name="ivars777"  >
2
+<addon version="0.1.17" id="plugin.image.photostation" name="PhotoStation" provider-name="ivars777"  >
3 3
   <requires>
4 4
     <import addon="xbmc.python" version="2.1.0"/>
5 5
     <import addon="script.module.requests" />

二進制
icon.png 查看文件


+ 3
- 59
kmake.bat 查看文件

@@ -18,15 +18,6 @@ set CP=\MinGW\msys\1.0\bin\cp.exe
18 18
 set ZIP=
19 19
 rem set ZIP=\Program Files (x86)\Gow\bin\zip.exe
20 20
 
21
-
22
-:=== Pull content submodule ===
23
-pushd resources\lib\content
24
-git commit -a -m labojumi
25
-git checkout .
26
-git pull
27
-popd
28
-
29
-
30 21
 :=== Create package zip file ===
31 22
 if exist "%pack_name%" rm -r -f "%pack_name%"
32 23
 mkdir "%pack_name%""
@@ -38,10 +29,6 @@ readme.md
38 29
 changelog.md
39 30
 addon.xml
40 31
 addon.py
41
-context_menu.py
42
-context_download.py
43
-downloadqueue.py
44
-service.py
45 32
 icon.png
46 33
 kodiswift\*.py
47 34
 resources\__init__.py
@@ -49,53 +36,9 @@ resources\settings.xml
49 36
 resources\icon.png
50 37
 resources\language\English\*
51 38
 resources\lib\__init__.py
52
-resources\lib\content\__init__.py
53
-resources\lib\content\ContentSources.py
54
-resources\lib\content\playstreamproxy.py
55
-resources\lib\content\Downloader.py
56
-resources\lib\content\resolver.py
57
-resources\lib\content\util.py
58
-resources\lib\content\run.py
59
-resources\lib\content\file.py
60
-resources\lib\content\demjson.py
61
-resources\lib\content\ordereddict.py
62
-resources\lib\content\sources\__init__.py
63
-resources\lib\content\sources\SourceBase.py
64
-resources\lib\content\sources\cinemalive.py
65
-resources\lib\content\sources\config.py
66
-resources\lib\content\sources\euronews.py
67
-resources\lib\content\sources\filmix.py
68
-resources\lib\content\sources\filmon.py
69
-resources\lib\content\sources\iplayer.py
70
-resources\lib\content\sources\movieplace.py
71
-resources\lib\content\sources\ltc.py
72
-resources\lib\content\sources\mtgplay.py
73
-resources\lib\content\sources\xtv.py
74
-resources\lib\content\sources\replay.py
75
-resources\lib\content\sources\lmt.py
76
-resources\lib\content\sources\serialguru.py
77
-resources\lib\content\sources\tvdom.py
78
-resources\lib\content\sources\ustvnow.py
79
-resources\lib\content\sources\viaplay.py
80
-resources\lib\content\sources\filmas.py
81
-resources\lib\content\sources\tvplay.py
82
-resources\lib\content\sources\enigma2.py
83
-resources\lib\content\sources\YouTubeVideoUrl.py
84
-resources\lib\content\sources\jsinterp.py
85
-resources\lib\content\sources\swfinterp.py
86
-resources\lib\content\sources\streams.cfg
87
-resources\lib\content\resolvers\__init__.py
88
-resources\lib\content\resolvers\aadecode.py
89
-resources\lib\content\resolvers\hqqresolver.py
90
-resources\lib\content\resolvers\openload3.py
91
-resources\lib\content\resolvers\hdgo.py
92
-resources\lib\content\resolvers\kapnob.py
93
-resources\lib\content\resolvers\kodik.py
94
-resources\lib\content\resolvers\cloudsany.py
95
-resources\lib\content\resolvers\youtuberesolver.py
39
+resources\lib\photostation_api.py
96 40
 ) do echo f| xcopy %%f %pack_name%\%%f
97
-
98
-xcopy /y /q resources\lib\content\picons\* %pack_name%\resources\picons\
41
+pause
99 42
 
100 43
 echo Creating %release_dir%%pack_name%-%ver%.zip
101 44
 if exist  %release_dir%%pack_name%-%ver%.zip rm %release_dir%%pack_name%-%ver%.zip
@@ -107,6 +50,7 @@ if ()==(%1%) (
107 50
 )
108 51
 :=== Add to git ===
109 52
 echo Adding to git
53
+echo git add %release_dir%%pack_name%-%ver%.zip
110 54
 git add %release_dir%%pack_name%-%ver%.zip
111 55
 git commit -m %ver%
112 56
 git tag -d %ver%

+ 6
- 0
make_links.bat 查看文件

@@ -0,0 +1,6 @@
1
+mklink /h resources\lib\ContentSources.py \Data\Programming\enigma2\PlayStream2\ContentSources.py
2
+mklink /h resources\lib\util.py \Data\Programming\enigma2\PlayStream2\util.py
3
+mklink /h resources\lib\resolver.py \Data\Programming\enigma2\PlayStream2\resolver.py
4
+mklink /h resources\lib\demjson.py \Data\Programming\enigma2\PlayStream2\demjson.py
5
+mklink /j resources\lib\sources \Data\Programming\enigma2\PlayStream2\sources
6
+mklink /j resources\lib\resolvers \Data\Programming\enigma2\PlayStream2\resolvers

+ 60
- 0
picture_infotags.txt 查看文件

@@ -0,0 +1,60 @@
1
+"filename": SLIbESHOW_FILE_NAME;
2
+"path": SLIDESHOW_FILE_PATH;
3
+"filesize": SLIDESHOW_FILE_SIZE;
4
+"filedate": SLIDESHOW_FILE_DATE;
5
+"slideindex": SLIDESHOW_INDEX;
6
+"resolution": SLIDESHOW_RESOLUTION;
7
+"slidecomment": SLIDESHOW_COMMENT;
8
+"colour": SLIDESHOW_COLOUR;
9
+"process": SLIDESHOW_PROCESS;
10
+"exiftime": SLIDESHOW_EXIF_DATE_TIME;
11
+"exifdate": SLIDESHOW_EXIF_DATE;
12
+"longexiftime": SLIDESHOW_EXIF_LONG_DATE_TIME;
13
+"longexifdate": SLIDESHOW_EXIF_LONG_DATE;
14
+"exifdescription": SLIDESHOW_EXIF_DESCRIPTION;
15
+"cameramake": SLIDESHOW_EXIF_CAMERA_MAKE;
16
+"cameramodel": SLIDESHOW_EXIF_CAMERA_MODEL;
17
+"exifcomment": SLIDESHOW_EXIF_COMMENT;
18
+"exifsoftware": SLIDESHOW_EXIF_SOFTWARE;
19
+"aperture": SLIDESHOW_EXIF_APERTURE;
20
+"focallength": SLIDESHOW_EXIF_FOCAL_LENGTH;
21
+"focusdistance": SLIDESHOW_EXIF_FOCUS_DIST;
22
+"exposure": SLIDESHOW_EXIF_EXPOSURE;
23
+"exposuretime": SLIDESHOW_EXIF_EXPOSURE_TIME;
24
+"exposurebias": SLIDESHOW_EXIF_EXPOSURE_BIAS;
25
+"exposuremode": SLIDESHOW_EXIF_EXPOSURE_MODE;
26
+"flashused": SLIDESHOW_EXIF_FLASH_USED;
27
+"whitebalance": SLIDESHOW_EXIF_WHITE_BALANCE;
28
+"lightsource": SLIDESHOW_EXIF_LIGHT_SOURCE;
29
+"meteringmode": SLIDESHOW_EXIF_METERING_MODE;
30
+"isoequivalence": SLIDESHOW_EXIF_ISO_EQUIV;
31
+"digitalzoom": SLIDESHOW_EXIF_DIGITAL_ZOOM;
32
+"ccdwidth": SLIDESHOW_EXIF_CCD_WIDTH;
33
+"orientation": SLIDESHOW_EXIF_ORIENTATION;
34
+"supplementalcategories": SLIDESHOW_IPTC_SUP_CATEGORIES;
35
+"keywords": SLIDESHOW_IPTC_KEYWORDS;
36
+"caption": SLIDESHOW_IPTC_CAPTION;
37
+"author": SLIDESHOW_IPTC_AUTHOR;
38
+"headline": SLIDESHOW_IPTC_HEADLINE;
39
+"specialinstructions": SLIDESHOW_IPTC_SPEC_INSTR;
40
+"category": SLIDESHOW_IPTC_CATEGORY;
41
+"byline": SLIDESHOW_IPTC_BYLINE;
42
+"bylinetitle": SLIDESHOW_IPTC_BYLINE_TITLE;
43
+"credit": SLIDESHOW_IPTC_CREDIT;
44
+"source": SLIDESHOW_IPTC_SOURCE;
45
+"copyrightnotice": SLIDESHOW_IPTC_COPYRIGHT_NOTICE;
46
+"objectname": SLIDESHOW_IPTC_OBJECT_NAME;
47
+"city": SLIDESHOW_IPTC_CITY;
48
+"state": SLIDESHOW_IPTC_STATE;
49
+"country": SLIDESHOW_IPTC_COUNTRY;
50
+"transmissionreference": SLIDESHOW_IPTC_TX_REFERENCE;
51
+"iptcdate": SLIDESHOW_IPTC_DATE;
52
+"urgency": SLIDESHOW_IPTC_URGENCY;
53
+"countrycode": SLIDESHOW_IPTC_COUNTRY_CODE;
54
+"referenceservice": SLIDESHOW_IPTC_REF_SERVICE;
55
+"latitude": SLIDESHOW_EXIF_GPS_LATITUDE;
56
+"longitude": SLIDESHOW_EXIF_GPS_LONGITUDE;
57
+"altitude": SLIDESHOW_EXIF_GPS_ALTITUDE;
58
+"timecreated": SLIDESHOW_IPTC_TIMECREATED;
59
+"sublocation": SLIDESHOW_IPTC_SUBLOCATION;
60
+"imagetype": SLIDESHOW_IPTC_IMAGETYPE;

+ 358
- 340
project.wpr 查看文件

@@ -1,7 +1,7 @@
1 1
 #!wing
2
-#!version=6.0
2
+#!version=7.0
3 3
 ##################################################################
4
-# Wing IDE project file                                          #
4
+# Wing project file                                              #
5 5
 ##################################################################
6 6
 [project attributes]
7 7
 debug.named-entry-points = (1,
@@ -52,7 +52,11 @@ proj.launch-config = {loc('../../../../Python27/Lib/site-packages/xbmcswift2/plu
52 52
                                        (u'run interactive',
53 53
         ''))}
54 54
 [user attributes]
55
-debug.breakpoints = {loc('addon.py'): {185L: (0,
55
+debug.breakpoints = {loc('addon.py'): {187L: (0,
56
+        None,
57
+        1,
58
+        0),
59
+                                       307L: (0,
56 60
         None,
57 61
         1,
58 62
         0)},
@@ -112,11 +116,11 @@ debug.breakpoints = {loc('addon.py'): {185L: (0,
112 116
             None,
113 117
             1,
114 118
             0)},
115
-                     loc('unknown:<untitled> #1'): {10: (0,
119
+                     loc('unknown:<untitled> #2'): {2: (0,
116 120
         None,
117 121
         1,
118 122
         0)},
119
-                     loc('unknown:<untitled> #2'): {2: (0,
123
+                     loc('unknown:<untitled> #1'): {10: (0,
120 124
         None,
121 125
         1,
122 126
         0)}}
@@ -191,7 +195,7 @@ guimgr.overall-gui-state = {'windowing-policy': 'combined-window',
191 195
                  'current_pages': [0,
192 196
                                    1],
193 197
                  'full-screen': False,
194
-                 'notebook_display': 'tabs only',
198
+                 'notebook_display': 'normal',
195 199
                  'notebook_percent': 0.2797173732335827,
196 200
                  'override_title': None,
197 201
                  'pagelist': [('project',
@@ -200,7 +204,7 @@ guimgr.overall-gui-state = {'windowing-policy': 'combined-window',
200 204
                                {'tree-state': {'file-sort-method': 'by name',
201 205
         'list-files-first': 0,
202 206
         'tree-states': {'deep': {'expanded-nodes': [(0,),
203
-        (15,)],
207
+        (23,)],
204 208
                                  'selected-nodes': [],
205 209
                                  'top-node': (0,)}},
206 210
         'tree-style': 'deep'}}),
@@ -607,18 +611,18 @@ guimgr.overall-gui-state = {'windowing-policy': 'combined-window',
607 611
         'top-node': [('generic attribute',
608 612
                       loc('../../../Python27/Lib/site-packages/win32com/client/dynamic.py'),
609 613
                       'ALL_INVOKE_TYPES')]},
610
-        loc('unknown:<untitled> #5'): {'expanded-nodes': [],
614
+        loc('unknown:<untitled> #3'): {'expanded-nodes': [],
611 615
                                        'selected-nodes': [],
612 616
                                        'top-node': None},
613
-        loc('unknown:<untitled> #4'): {'column-widths': [1.0],
614
-                                       'expanded-nodes': [],
617
+        loc('unknown:<untitled> #4'): {'expanded-nodes': [],
615 618
                                        'selected-nodes': [],
616 619
                                        'top-node': None},
617
-        loc('unknown:<untitled> #3'): {'column-widths': [1.0],
620
+        loc('unknown:<untitled> #5'): {'column-widths': [1.0],
618 621
                                        'expanded-nodes': [],
619 622
                                        'selected-nodes': [],
620 623
                                        'top-node': None},
621
-        loc('unknown:<untitled> #6'): {'expanded-nodes': [],
624
+        loc('unknown:<untitled> #6'): {'column-widths': [1.0],
625
+                                       'expanded-nodes': [],
622 626
                                        'selected-nodes': [],
623 627
                                        'top-node': None}},
624 628
                                 'browse_mode': u'Current Module',
@@ -645,8 +649,8 @@ guimgr.overall-gui-state = {'windowing-policy': 'combined-window',
645 649
                                None)],
646 650
                  'primary_view_state': {'area': 'wide',
647 651
         'constraint': None,
648
-        'current_pages': [3,
649
-                          1],
652
+        'current_pages': [2,
653
+                          2],
650 654
         'notebook_display': 'normal',
651 655
         'notebook_percent': 0.40883534136546185,
652 656
         'override_title': None,
@@ -677,7 +681,7 @@ guimgr.overall-gui-state = {'windowing-policy': 'combined-window',
677 681
                                        'fRegexFlags': 42,
678 682
                                        'fReplaceText': '',
679 683
                                        'fReverse': False,
680
-                                       'fSearchText': u'page',
684
+                                       'fSearchText': u'ps',
681 685
                                        'fStartPos': 0,
682 686
                                        'fStyle': 'text',
683 687
                                        'fWholeWords': False,
@@ -747,14 +751,19 @@ guimgr.overall-gui-state = {'windowing-policy': 'combined-window',
747 751
         -1,
748 752
         -1),
749 753
                        'attrib-starts': [],
750
-                       'first-line': 0L,
754
+                       'code-line': '',
755
+                       'first-line': 35L,
751 756
                        'folded-linenos': [],
752
-                       'history': {None: ['data2\n']},
757
+                       'history': {None: ['server\n',
758
+        'self.url\n',
759
+        'ps\n',
760
+        'print url\n',
761
+        'f\n']},
753 762
                        'launch-id': None,
754
-                       'sel-line': 2L,
755
-                       'sel-line-start': 224L,
756
-                       'selection_end': 224L,
757
-                       'selection_start': 224L,
763
+                       'sel-line': 42L,
764
+                       'sel-line-start': 2433L,
765
+                       'selection_end': 2433L,
766
+                       'selection_start': 2433L,
758 767
                        'zoom': 0L}),
759 768
                      ('debug-watch',
760 769
                       'wide',
@@ -770,7 +779,7 @@ guimgr.overall-gui-state = {'windowing-policy': 'combined-window',
770 779
                                        ('eval',
771 780
         u'items')],
772 781
                        'tree-state': {'expanded-nodes': [],
773
-                                      'selected-nodes': [(4,)],
782
+                                      'selected-nodes': [],
774 783
                                       'top-node': (0,)}}),
775 784
                      ('messages',
776 785
                       'wide',
@@ -783,6 +792,7 @@ guimgr.overall-gui-state = {'windowing-policy': 'combined-window',
783 792
         -1,
784 793
         -1),
785 794
                        'attrib-starts': [],
795
+                       'code-line': '',
786 796
                        'first-line': 0L,
787 797
                        'folded-linenos': [],
788 798
                        'history': {},
@@ -804,231 +814,264 @@ guimgr.overall-gui-state = {'windowing-policy': 'combined-window',
804 814
                        'toolbox-tree-sel': ''})],
805 815
         'primary_view_state': {'editor_states': ('horizontal',
806 816
         1.0,
807
-        ({'bookmarks': ([[loc('addon.py'),
808
-                          {'attrib-starts': [('main',
809
-        37)],
810
-                           'first-line': 43L,
811
-                           'folded-linenos': [228L],
812
-                           'sel-line': 53L,
813
-                           'sel-line-start': 1807L,
814
-                           'selection_end': 1819L,
815
-                           'selection_start': 1815L,
817
+        ({'bookmarks': ([[loc('resources/language/English/strings.xml'),
818
+                          {'attrib-starts': [],
819
+                           'first-line': 15L,
820
+                           'folded-linenos': [],
821
+                           'sel-line': 21L,
822
+                           'sel-line-start': 785L,
823
+                           'selection_end': 785L,
824
+                           'selection_start': 785L,
816 825
                            'zoom': 0L},
817
-                          1531953967.755],
818
-                         [loc('addon.py'),
819
-                          {'attrib-starts': [('main',
820
-        37)],
821
-                           'first-line': 43L,
822
-                           'folded-linenos': [228L],
823
-                           'sel-line': 55L,
824
-                           'sel-line-start': 1834L,
825
-                           'selection_end': 1846L,
826
-                           'selection_start': 1842L,
826
+                          1549830249.336],
827
+                         [loc('kodiswift/plugin.py'),
828
+                          {'attrib-starts': [('Plugin',
829
+        27),
830
+        ('Plugin._parse_request',
831
+         341)],
832
+                           'first-line': 0L,
833
+                           'folded-linenos': [],
834
+                           'sel-line': 356L,
835
+                           'sel-line-start': 13246L,
836
+                           'selection_end': 13246L,
837
+                           'selection_start': 13246L,
827 838
                            'zoom': 0L},
828
-                          1531953968.128],
839
+                          1549830253.958],
829 840
                          [loc('addon.py'),
830
-                          {'attrib-starts': [('main',
831
-        37)],
832
-                           'first-line': 43L,
841
+                          {'attrib-starts': [('play_video',
842
+        300)],
843
+                           'first-line': 0L,
833 844
                            'folded-linenos': [228L],
834
-                           'sel-line': 55L,
835
-                           'sel-line-start': 1834L,
836
-                           'selection_end': 1861L,
837
-                           'selection_start': 1857L,
845
+                           'sel-line': 306L,
846
+                           'sel-line-start': 12264L,
847
+                           'selection_end': 12264L,
848
+                           'selection_start': 12264L,
838 849
                            'zoom': 0L},
839
-                          1531953968.496],
840
-                         [loc('addon.py'),
841
-                          {'attrib-starts': [('main',
842
-        37)],
843
-                           'first-line': 66L,
844
-                           'folded-linenos': [228L],
845
-                           'sel-line': 75L,
846
-                           'sel-line-start': 2620L,
847
-                           'selection_end': 2634L,
848
-                           'selection_start': 2628L,
850
+                          1549830256.066],
851
+                         [loc('resources/settings.xml'),
852
+                          {'attrib-starts': [],
853
+                           'first-line': 0L,
854
+                           'folded-linenos': [],
855
+                           'sel-line': 13L,
856
+                           'sel-line-start': 1171L,
857
+                           'selection_end': 1171L,
858
+                           'selection_start': 1171L,
849 859
                            'zoom': 0L},
850
-                          1531953988.159],
851
-                         [loc('addon.py'),
852
-                          {'attrib-starts': [('main',
853
-        37)],
854
-                           'first-line': 66L,
855
-                           'folded-linenos': [228L],
856
-                           'sel-line': 75L,
857
-                           'sel-line-start': 2620L,
858
-                           'selection_end': 2646L,
859
-                           'selection_start': 2642L,
860
+                          1549830257.499],
861
+                         [loc('changelog.md'),
862
+                          {'attrib-starts': [],
863
+                           'first-line': 0L,
864
+                           'folded-linenos': [],
865
+                           'sel-line': 0L,
866
+                           'sel-line-start': 0L,
867
+                           'selection_end': 7L,
868
+                           'selection_start': 7L,
860 869
                            'zoom': 0L},
861
-                          1531953988.55],
862
-                         [loc('addon.py'),
863
-                          {'attrib-starts': [('main',
864
-        37)],
865
-                           'first-line': 66L,
866
-                           'folded-linenos': [228L],
867
-                           'sel-line': 75L,
868
-                           'sel-line-start': 2620L,
869
-                           'selection_end': 2668L,
870
-                           'selection_start': 2664L,
870
+                          1549830258.499],
871
+                         [loc('resources/lib/photostation_api.py'),
872
+                          {'attrib-starts': [('PhotoStationAPI|0|',
873
+        7),
874
+        ('PhotoStationAPI|0|.get_album_info|0|',
875
+         90)],
876
+                           'code-line': '\r\n',
877
+                           'first-line': 92L,
878
+                           'folded-linenos': [],
879
+                           'sel-line': 94L,
880
+                           'sel-line-start': 4404L,
881
+                           'selection_end': 4404L,
882
+                           'selection_start': 4404L,
871 883
                            'zoom': 0L},
872
-                          1531953988.847],
884
+                          1555261277.383],
873 885
                          [loc('addon.py'),
874
-                          {'attrib-starts': [('main',
875
-        37)],
876
-                           'first-line': 66L,
886
+                          {'attrib-starts': [],
887
+                           'code-line': 'import os,os.path,sys, urllib, urlp'\
888
+        'arse\n',
889
+                           'first-line': 0L,
877 890
                            'folded-linenos': [228L],
878
-                           'sel-line': 75L,
879
-                           'sel-line-start': 2620L,
880
-                           'selection_end': 2680L,
881
-                           'selection_start': 2676L,
891
+                           'sel-line': 5L,
892
+                           'sel-line-start': 68L,
893
+                           'selection_end': 85L,
894
+                           'selection_start': 85L,
882 895
                            'zoom': 0L},
883
-                          1531953989.142],
896
+                          1555261621.157],
884 897
                          [loc('addon.py'),
885
-                          {'attrib-starts': [('main',
886
-        37)],
887
-                           'first-line': 66L,
898
+                          {'attrib-starts': [],
899
+                           'code-line': 'page_size = plugin.get_setting("pag'\
900
+        'e_size", str)\n',
901
+                           'first-line': 67L,
888 902
                            'folded-linenos': [228L],
889
-                           'sel-line': 76L,
890
-                           'sel-line-start': 2686L,
891
-                           'selection_end': 2712L,
892
-                           'selection_start': 2708L,
903
+                           'sel-line': 18L,
904
+                           'sel-line-start': 561L,
905
+                           'selection_end': 561L,
906
+                           'selection_start': 561L,
893 907
                            'zoom': 0L},
894
-                          1531953989.451],
908
+                          1555261732.821],
895 909
                          [loc('addon.py'),
896
-                          {'attrib-starts': [('main',
897
-        37)],
898
-                           'first-line': 66L,
910
+                          {'attrib-starts': [],
911
+                           'code-line': '    ps.login(user, password)\n',
912
+                           'first-line': 22L,
899 913
                            'folded-linenos': [228L],
900
-                           'sel-line': 76L,
901
-                           'sel-line-start': 2686L,
902
-                           'selection_end': 2733L,
903
-                           'selection_start': 2729L,
914
+                           'sel-line': 30L,
915
+                           'sel-line-start': 1110L,
916
+                           'selection_end': 1110L,
917
+                           'selection_start': 1110L,
904 918
                            'zoom': 0L},
905
-                          1531953989.779],
906
-                         [loc('addon.py'),
907
-                          {'attrib-starts': [('main',
908
-        37)],
909
-                           'first-line': 153L,
910
-                           'folded-linenos': [228L],
911
-                           'sel-line': 165L,
912
-                           'sel-line-start': 7294L,
913
-                           'selection_end': 7309L,
914
-                           'selection_start': 7305L,
919
+                          1555261785.976],
920
+                         [loc('resources/lib/photostation_api.py'),
921
+                          {'attrib-starts': [('PhotoStationAPI|0|',
922
+        7),
923
+        ('PhotoStationAPI|0|._call2|0|',
924
+         189)],
925
+                           'code-line': '            r = requests.get(url,he'\
926
+        'aders=self.headers)\r\n',
927
+                           'first-line': 195L,
928
+                           'folded-linenos': [],
929
+                           'sel-line': 200L,
930
+                           'sel-line-start': 9743L,
931
+                           'selection_end': 9743L,
932
+                           'selection_start': 9743L,
915 933
                            'zoom': 0L},
916
-                          1531953991.372],
934
+                          1555261915.377],
917 935
                          [loc('addon.py'),
918 936
                           {'attrib-starts': [],
919
-                           'first-line': 310L,
937
+                           'code-line': 'ps = photostation_api.PhotoStationA'\
938
+        'PI("http://home.blue.lv/photo")\n',
939
+                           'first-line': 17L,
920 940
                            'folded-linenos': [228L],
921
-                           'sel-line': 321L,
922
-                           'sel-line-start': 12716L,
923
-                           'selection_end': 12716L,
924
-                           'selection_start': 12716L,
941
+                           'sel-line': 28L,
942
+                           'sel-line-start': 1038L,
943
+                           'selection_end': 1038L,
944
+                           'selection_start': 1038L,
925 945
                            'zoom': 0L},
926
-                          1531954130.795],
927
-                         [loc('kodiswift/plugin.py'),
928
-                          {'attrib-starts': [('Plugin',
929
-        27),
930
-        ('Plugin._parse_request',
931
-         341)],
932
-                           'first-line': 347L,
933
-                           'folded-linenos': [],
934
-                           'sel-line': 356L,
935
-                           'sel-line-start': 13246L,
936
-                           'selection_end': 13246L,
937
-                           'selection_start': 13246L,
938
-                           'zoom': 0L},
939
-                          1531954225.148],
940
-                         [loc('kodiswift/request.py'),
941
-                          {'attrib-starts': [('Request',
942
-        20),
943
-        ('Request.__init__',
944
-         22)],
945
-                           'first-line': 16L,
946
+                          1555261927.98],
947
+                         [loc('resources/lib/photostation_api.py'),
948
+                          {'attrib-starts': [('PhotoStationAPI|0|',
949
+        7),
950
+        ('PhotoStationAPI|0|._call2|0|',
951
+         189)],
952
+                           'code-line': '            r = requests.get(url,he'\
953
+        'aders=self.headers)\r\n',
954
+                           'first-line': 195L,
946 955
                            'folded-linenos': [],
947
-                           'sel-line': 37L,
948
-                           'sel-line-start': 1056L,
949
-                           'selection_end': 1056L,
950
-                           'selection_start': 1056L,
956
+                           'sel-line': 200L,
957
+                           'sel-line-start': 9743L,
958
+                           'selection_end': 9743L,
959
+                           'selection_start': 9743L,
951 960
                            'zoom': 0L},
952
-                          1531954245.475],
961
+                          1555262034.05],
953 962
                          [loc('addon.py'),
954
-                          {'attrib-starts': [],
955
-                           'first-line': 309L,
963
+                          {'attrib-starts': [('main|0|',
964
+        37)],
965
+                           'code-line': '                    "plotline":f["a'\
966
+        'dditional"]["file_location"] + "\\n" + label2 if "file_location" in'\
967
+        ' f["additional"] else "",\n',
968
+                           'first-line': 94L,
956 969
                            'folded-linenos': [228L],
957
-                           'sel-line': 321L,
958
-                           'sel-line-start': 12716L,
959
-                           'selection_end': 12716L,
960
-                           'selection_start': 12716L,
970
+                           'sel-line': 105L,
971
+                           'sel-line-start': 4307L,
972
+                           'selection_end': 4307L,
973
+                           'selection_start': 4307L,
961 974
                            'zoom': 0L},
962
-                          1531954266.378],
963
-                         [loc('kodiswift/plugin.py'),
964
-                          {'attrib-starts': [('Plugin',
965
-        27),
966
-        ('Plugin._parse_request',
967
-         341)],
968
-                           'first-line': 342L,
975
+                          1555262042.934],
976
+                         [loc('resources/lib/photostation_api.py'),
977
+                          {'attrib-starts': [('PhotoStationAPI|0|',
978
+        7),
979
+        ('PhotoStationAPI|0|._call2|0|',
980
+         189)],
981
+                           'code-line': '            r = requests.get(url,he'\
982
+        'aders=self.headers)\r\n',
983
+                           'first-line': 180L,
969 984
                            'folded-linenos': [],
970
-                           'sel-line': 352L,
971
-                           'sel-line-start': 13112L,
972
-                           'selection_end': 13143L,
973
-                           'selection_start': 13143L,
985
+                           'sel-line': 200L,
986
+                           'sel-line-start': 9743L,
987
+                           'selection_end': 9743L,
988
+                           'selection_start': 9743L,
974 989
                            'zoom': 0L},
975
-                          1531954319.585],
990
+                          1555262047.625],
976 991
                          [loc('addon.py'),
977
-                          {'attrib-starts': [],
978
-                           'first-line': 309L,
992
+                          {'attrib-starts': [('main|0|',
993
+        37)],
994
+                           'code-line': '                    "plotline":f["a'\
995
+        'dditional"]["file_location"] + "\\n" + label2 if "file_location" in'\
996
+        ' f["additional"] else "",\n',
997
+                           'first-line': 94L,
979 998
                            'folded-linenos': [228L],
980
-                           'sel-line': 321L,
981
-                           'sel-line-start': 12716L,
982
-                           'selection_end': 12716L,
983
-                           'selection_start': 12716L,
999
+                           'sel-line': 105L,
1000
+                           'sel-line-start': 4307L,
1001
+                           'selection_end': 4307L,
1002
+                           'selection_start': 4307L,
984 1003
                            'zoom': 0L},
985
-                          1531954326.398],
986
-                         [loc('kodiswift/plugin.py'),
987
-                          {'attrib-starts': [('Plugin',
988
-        27),
989
-        ('Plugin._parse_request',
990
-         341)],
991
-                           'first-line': 347L,
1004
+                          1555262048.261],
1005
+                         [loc('resources/lib/photostation_api.py'),
1006
+                          {'attrib-starts': [('PhotoStationAPI|0|',
1007
+        7),
1008
+        ('PhotoStationAPI|0|._call2|0|',
1009
+         189)],
1010
+                           'code-line': '            r = requests.get(url,he'\
1011
+        'aders=self.headers)\r\n',
1012
+                           'first-line': 180L,
992 1013
                            'folded-linenos': [],
993
-                           'sel-line': 356L,
994
-                           'sel-line-start': 13246L,
995
-                           'selection_end': 13246L,
996
-                           'selection_start': 13246L,
1014
+                           'sel-line': 200L,
1015
+                           'sel-line-start': 9743L,
1016
+                           'selection_end': 9743L,
1017
+                           'selection_start': 9743L,
1018
+                           'zoom': 0L},
1019
+                          1555262052.669],
1020
+                         [loc('../../../../Users/user/AppData/Roaming/Kodi/addons/plugin.image.photostation/addon.py'),
1021
+                          {'attrib-starts': [],
1022
+                           'code-line': '',
1023
+                           'first-line': 0L,
1024
+                           'folded-linenos': [],
1025
+                           'sel-line': 0L,
1026
+                           'sel-line-start': 0L,
1027
+                           'selection_end': 0L,
1028
+                           'selection_start': 0L,
997 1029
                            'zoom': 0L},
998
-                          1531954347.816],
999
-                         [loc('kodiswift/request.py'),
1000
-                          {'attrib-starts': [('Request',
1001
-        20),
1002
-        ('Request.__init__',
1003
-         22)],
1004
-                           'first-line': 33L,
1030
+                          1555262053.808],
1031
+                         [loc('resources/lib/photostation_api.py'),
1032
+                          {'attrib-starts': [('PhotoStationAPI|0|',
1033
+        7),
1034
+        ('PhotoStationAPI|0|._call2|0|',
1035
+         189)],
1036
+                           'code-line': '            r = requests.get(url,he'\
1037
+        'aders=self.headers)\r\n',
1038
+                           'first-line': 190L,
1005 1039
                            'folded-linenos': [],
1006
-                           'sel-line': 45L,
1007
-                           'sel-line-start': 1422L,
1008
-                           'selection_end': 1422L,
1009
-                           'selection_start': 1422L,
1040
+                           'sel-line': 200L,
1041
+                           'sel-line-start': 9743L,
1042
+                           'selection_end': 9743L,
1043
+                           'selection_start': 9743L,
1010 1044
                            'zoom': 0L},
1011
-                          1531954379.77],
1045
+                          1555262110.581],
1012 1046
                          [loc('addon.py'),
1013
-                          {'attrib-starts': [],
1014
-                           'first-line': 9L,
1015
-                           'folded-linenos': [228L],
1016
-                           'sel-line': 14L,
1017
-                           'sel-line-start': 384L,
1018
-                           'selection_end': 384L,
1019
-                           'selection_start': 384L,
1047
+                          {'attrib-starts': [('main|0|',
1048
+        37)],
1049
+                           'code-line': '            content = ps.get_album_'\
1050
+        'list(oid, offset=offset, limit=limit, sort_by=sorting, sort_directi'\
1051
+        'on=order )\n',
1052
+                           'first-line': 72L,
1053
+                           'folded-linenos': [],
1054
+                           'sel-line': 80L,
1055
+                           'sel-line-start': 2831L,
1056
+                           'selection_end': 2831L,
1057
+                           'selection_start': 2831L,
1020 1058
                            'zoom': 0L},
1021
-                          1531954540.55],
1022
-                         [loc('addon.xml'),
1023
-                          {'attrib-starts': [],
1024
-                           'first-line': 0L,
1059
+                          1555262284.715],
1060
+                         [loc('resources/lib/photostation_api.py'),
1061
+                          {'attrib-starts': [('PhotoStationAPI|0|',
1062
+        7),
1063
+        ('PhotoStationAPI|0|.get_album_list|0|',
1064
+         32)],
1065
+                           'code-line': '        return self._call2("album.p'\
1066
+        'hp",data)\r\n',
1067
+                           'first-line': 35L,
1025 1068
                            'folded-linenos': [],
1026
-                           'sel-line': 1L,
1027
-                           'sel-line-start': 57L,
1028
-                           'selection_end': 79L,
1029
-                           'selection_start': 79L,
1069
+                           'sel-line': 40L,
1070
+                           'sel-line-start': 1786L,
1071
+                           'selection_end': 1786L,
1072
+                           'selection_start': 1786L,
1030 1073
                            'zoom': 0L},
1031
-                          1549828587.4]],
1074
+                          1555262367.881]],
1032 1075
                         20),
1033 1076
           'current-loc': loc('addon.py'),
1034 1077
           'editor-state-list': [(loc('addon.xml'),
@@ -1040,27 +1083,19 @@ guimgr.overall-gui-state = {'windowing-policy': 'combined-window',
1040 1083
                                   'selection_end': 79L,
1041 1084
                                   'selection_start': 79L,
1042 1085
                                   'zoom': 0L}),
1043
-                                (loc('addon.py'),
1044
-                                 {'attrib-starts': [('play_video',
1045
-        300)],
1046
-                                  'first-line': 297L,
1047
-                                  'folded-linenos': [228L],
1048
-                                  'sel-line': 306L,
1049
-                                  'sel-line-start': 12264L,
1050
-                                  'selection_end': 12264L,
1051
-                                  'selection_start': 12264L,
1052
-                                  'zoom': 0L}),
1053 1086
                                 (loc('resources/lib/photostation_api.py'),
1054
-                                 {'attrib-starts': [('PhotoStationAPI',
1087
+                                 {'attrib-starts': [('PhotoStationAPI|0|',
1055 1088
         7),
1056
-        ('PhotoStationAPI.get_category_list',
1057
-         98)],
1058
-                                  'first-line': 92L,
1089
+        ('PhotoStationAPI|0|.get_album_list|0|',
1090
+         32)],
1091
+                                  'code-line': '        return self._call2("'\
1092
+        'album.php",data)\r\n',
1093
+                                  'first-line': 35L,
1059 1094
                                   'folded-linenos': [],
1060
-                                  'sel-line': 104L,
1061
-                                  'sel-line-start': 5012L,
1062
-                                  'selection_end': 5012L,
1063
-                                  'selection_start': 5012L,
1095
+                                  'sel-line': 40L,
1096
+                                  'sel-line-start': 1786L,
1097
+                                  'selection_end': 1786L,
1098
+                                  'selection_start': 1786L,
1064 1099
                                   'zoom': 0L}),
1065 1100
                                 (loc('changelog.md'),
1066 1101
                                  {'attrib-starts': [],
@@ -1073,6 +1108,8 @@ guimgr.overall-gui-state = {'windowing-policy': 'combined-window',
1073 1108
                                   'zoom': 0L}),
1074 1109
                                 (loc('resources/settings.xml'),
1075 1110
                                  {'attrib-starts': [],
1111
+                                  'code-line': '        <setting label="5000'\
1112
+        '8" id="repeat" type="bool" default="false" />\r\n',
1076 1113
                                   'first-line': 0L,
1077 1114
                                   'folded-linenos': [],
1078 1115
                                   'sel-line': 13L,
@@ -1080,83 +1117,26 @@ guimgr.overall-gui-state = {'windowing-policy': 'combined-window',
1080 1117
                                   'selection_end': 1171L,
1081 1118
                                   'selection_start': 1171L,
1082 1119
                                   'zoom': 0L}),
1083
-                                (loc('kodiswift/listitem.py'),
1084
-                                 {'attrib-starts': [],
1085
-                                  'first-line': 0L,
1086
-                                  'folded-linenos': [],
1087
-                                  'sel-line': 0L,
1088
-                                  'sel-line-start': 0L,
1089
-                                  'selection_end': 28L,
1090
-                                  'selection_start': 0L,
1091
-                                  'zoom': 0L}),
1092
-                                (loc('kodiswift/xbmcmixin.py'),
1093
-                                 {'attrib-starts': [('XBMCMixin',
1094
-        21),
1095
-        ('XBMCMixin._listitemify',
1096
-         531)],
1097
-                                  'first-line': 532L,
1098
-                                  'folded-linenos': [],
1099
-                                  'sel-line': 543L,
1100
-                                  'sel-line-start': 23072L,
1101
-                                  'selection_end': 23119L,
1102
-                                  'selection_start': 23110L,
1103
-                                  'zoom': 0L}),
1104
-                                (loc('kodiswift/mockxbmc/xbmcgui.py'),
1105
-                                 {'attrib-starts': [('ListItem',
1106
-        4),
1107
-        ('ListItem.__init__',
1108
-         5)],
1109
-                                  'first-line': 45L,
1110
-                                  'folded-linenos': [],
1111
-                                  'sel-line': 6L,
1112
-                                  'sel-line-start': 162L,
1113
-                                  'selection_end': 211L,
1114
-                                  'selection_start': 211L,
1115
-                                  'zoom': 0L}),
1116
-                                (loc('kodiswift/cli/app.py'),
1117
-                                 {'attrib-starts': [('interactive',
1118
-        171)],
1119
-                                  'first-line': 183L,
1120
-                                  'folded-linenos': [],
1121
-                                  'sel-line': 184L,
1122
-                                  'sel-line-start': 5990L,
1123
-                                  'selection_end': 6010L,
1124
-                                  'selection_start': 6010L,
1125
-                                  'zoom': 0L}),
1126
-                                (loc('resources/language/English/strings.xml'),
1127
-                                 {'attrib-starts': [],
1128
-                                  'first-line': 15L,
1129
-                                  'folded-linenos': [],
1130
-                                  'sel-line': 21L,
1131
-                                  'sel-line-start': 785L,
1132
-                                  'selection_end': 785L,
1133
-                                  'selection_start': 785L,
1134
-                                  'zoom': 0L}),
1135
-                                (loc('kodiswift/plugin.py'),
1136
-                                 {'attrib-starts': [('Plugin',
1137
-        27),
1138
-        ('Plugin._parse_request',
1139
-         341)],
1140
-                                  'first-line': 347L,
1120
+                                (loc('addon.py'),
1121
+                                 {'attrib-starts': [('main|0|',
1122
+        37)],
1123
+                                  'code-line': '            content = ps.get'\
1124
+        '_album_list(oid, offset=offset, limit=limit, sort_by=sorting, sort_'\
1125
+        'direction=order )\n',
1126
+                                  'first-line': 73L,
1141 1127
                                   'folded-linenos': [],
1142
-                                  'sel-line': 356L,
1143
-                                  'sel-line-start': 13246L,
1144
-                                  'selection_end': 13246L,
1145
-                                  'selection_start': 13246L,
1128
+                                  'sel-line': 80L,
1129
+                                  'sel-line-start': 2831L,
1130
+                                  'selection_end': 2831L,
1131
+                                  'selection_start': 2831L,
1146 1132
                                   'zoom': 0L})],
1147
-          'has-focus': False,
1133
+          'has-focus': True,
1148 1134
           'locked': False},
1149 1135
          [loc('addon.xml'),
1150
-          loc('addon.py'),
1151 1136
           loc('resources/lib/photostation_api.py'),
1152 1137
           loc('changelog.md'),
1153 1138
           loc('resources/settings.xml'),
1154
-          loc('kodiswift/listitem.py'),
1155
-          loc('kodiswift/xbmcmixin.py'),
1156
-          loc('kodiswift/mockxbmc/xbmcgui.py'),
1157
-          loc('kodiswift/cli/app.py'),
1158
-          loc('resources/language/English/strings.xml'),
1159
-          loc('kodiswift/plugin.py')]),
1139
+          loc('addon.py')]),
1160 1140
         ({'bookmarks': ([[loc('iubx2.py'),
1161 1141
                           {'attrib-starts': [('main',
1162 1142
         42)],
@@ -1357,6 +1337,8 @@ guimgr.overall-gui-state = {'windowing-policy': 'combined-window',
1357 1337
                                  {}),
1358 1338
                                 (loc('resources/settings.xml'),
1359 1339
                                  {'attrib-starts': [],
1340
+                                  'code-line': '<?xml version="1.0" encoding'\
1341
+        '="UTF-8" standalone="yes"?>\r\n',
1360 1342
                                   'first-line': 0L,
1361 1343
                                   'folded-linenos': [],
1362 1344
                                   'sel-line': 0L,
@@ -1395,14 +1377,8 @@ guimgr.overall-gui-state = {'windowing-policy': 'combined-window',
1395 1377
           loc('../../../../Python27/Lib/site-packages/kodiswift/listitem.py')])),
1396 1378
                                'open_files': [u'addon.xml',
1397 1379
         u'changelog.md',
1398
-        u'kodiswift/cli/app.py',
1399
-        u'kodiswift/listitem.py',
1400
-        u'kodiswift/mockxbmc/xbmcgui.py',
1401
-        u'kodiswift/plugin.py',
1402
-        u'kodiswift/xbmcmixin.py',
1403
-        u'resources/language/English/strings.xml',
1404
-        u'resources/lib/photostation_api.py',
1405 1380
         u'resources/settings.xml',
1381
+        u'resources/lib/photostation_api.py',
1406 1382
         u'addon.py',
1407 1383
         u'../../../../Python27/Lib/site-packages/kodiswift/listitem.py',
1408 1384
         u'../../../../Python27/Lib/site-packages/xbmcswift2/mockxbmc/xbmcgui.py',
@@ -1412,28 +1388,30 @@ guimgr.overall-gui-state = {'windowing-policy': 'combined-window',
1412 1388
         u'resources/settings.xml',
1413 1389
         u'settings.xml']},
1414 1390
         'saved_notebook_display': None,
1415
-        'split_percents': {0: 0.3710232158211522},
1391
+        'split_percents': {0: 0.43594153052450557},
1416 1392
         'splits': 2,
1417 1393
         'tab_location': 'top',
1394
+        'traversal_pos': ((1,
1395
+                           2),
1396
+                          1555262480.568),
1418 1397
         'user_data': {}},
1419 1398
                  'saved_notebook_display': None,
1420 1399
                  'split_percents': {0: 0.5},
1421 1400
                  'splits': 2,
1422 1401
                  'tab_location': 'left',
1402
+                 'traversal_pos': ((-1,
1403
+                                    -1),
1404
+                                   0),
1423 1405
                  'user_data': {}},
1424 1406
         'window-alloc': (167,
1425 1407
                          0,
1426 1408
                          2441,
1427 1409
                          1462)}]}
1428 1410
 guimgr.recent-documents = [loc('addon.py'),
1429
-                           loc('resources/settings.xml'),
1430
-                           loc('addon.xml'),
1431
-                           loc('kodiswift/plugin.py'),
1432 1411
                            loc('resources/lib/photostation_api.py'),
1412
+                           loc('resources/settings.xml'),
1433 1413
                            loc('changelog.md'),
1434
-                           loc('kodiswift/xbmcmixin.py'),
1435
-                           loc('resources/language/English/strings.xml'),
1436
-                           loc('kodiswift/listitem.py')]
1414
+                           loc('addon.xml')]
1437 1415
 guimgr.visual-state = {loc('../../../../Python27/Lib/site-packages/kodiswift/listitem.py'): {'a'\
1438 1416
         'ttrib-starts': [],
1439 1417
         'first-line': 0L,
@@ -1523,14 +1501,17 @@ guimgr.visual-state = {loc('../../../../Python27/Lib/site-packages/kodiswift/lis
1523 1501
         'sel-line-start': 20L,
1524 1502
         'selection_end': 40L,
1525 1503
         'selection_start': 40L},
1526
-                       loc('addon.py'): {'attrib-starts': [('main',
1527
-        23)],
1528
-        'first-line': 92L,
1529
-        'folded-linenos': [],
1504
+                       loc('addon.py'): {'attrib-starts': [('main|0|',
1505
+        37)],
1506
+        'code-line': '                    "plotline":f["additional"]["file_l'\
1507
+                     'ocation"] + "\\n" + label2 if "file_location" in f["ad'\
1508
+                     'ditional"] else "",\n',
1509
+        'first-line': 94L,
1510
+        'folded-linenos': [228L],
1530 1511
         'sel-line': 105L,
1531
-        'sel-line-start': 4157L,
1532
-        'selection_end': 4171L,
1533
-        'selection_start': 4157L,
1512
+        'sel-line-start': 4307L,
1513
+        'selection_end': 4307L,
1514
+        'selection_start': 4307L,
1534 1515
         'zoom': 0L},
1535 1516
                        loc('addon.xml'): {'attrib-starts': [],
1536 1517
         'first-line': 0L,
@@ -1571,6 +1552,16 @@ guimgr.visual-state = {loc('../../../../Python27/Lib/site-packages/kodiswift/lis
1571 1552
         'sel-line-start': 3404,
1572 1553
         'selection_end': 3450,
1573 1554
         'selection_start': 3449},
1555
+                       loc('kodiswift/cli/app.py'): {'attrib-starts': [('int'\
1556
+        'eractive',
1557
+        171)],
1558
+        'first-line': 183L,
1559
+        'folded-linenos': [],
1560
+        'sel-line': 184L,
1561
+        'sel-line-start': 5990L,
1562
+        'selection_end': 6010L,
1563
+        'selection_start': 6010L,
1564
+        'zoom': 0L},
1574 1565
                        loc('kodiswift/cli/data/resources/language/English/strings.po'): {'a'\
1575 1566
         'ttrib-starts': [],
1576 1567
         'first-line': 0L,
@@ -1580,41 +1571,49 @@ guimgr.visual-state = {loc('../../../../Python27/Lib/site-packages/kodiswift/lis
1580 1571
         'selection_end': 0L,
1581 1572
         'selection_start': 0L,
1582 1573
         'zoom': 0L},
1583
-                       loc('kodiswift/listitem.py'): {'attrib-starts': [('Li'\
1584
-        'stItem',
1585
-        20),
1586
-        ('ListItem.get_label2',
1587
-         93)],
1588
-        'first-line': 291L,
1574
+                       loc('kodiswift/listitem.py'): {'attrib-starts': [],
1575
+        'first-line': 0L,
1589 1576
         'folded-linenos': [],
1590
-        'sel-line': 96L,
1591
-        'sel-line-start': 2943L,
1592
-        'selection_end': 2967L,
1593
-        'selection_start': 2967L,
1577
+        'sel-line': 0L,
1578
+        'sel-line-start': 0L,
1579
+        'selection_end': 28L,
1580
+        'selection_start': 0L,
1581
+        'zoom': 0L},
1582
+                       loc('kodiswift/mockxbmc/xbmcgui.py'): {'attrib-starts': [('L'\
1583
+        'istItem',
1584
+        4),
1585
+        ('ListItem.__init__',
1586
+         5)],
1587
+        'first-line': 45L,
1588
+        'folded-linenos': [],
1589
+        'sel-line': 6L,
1590
+        'sel-line-start': 162L,
1591
+        'selection_end': 211L,
1592
+        'selection_start': 211L,
1594 1593
         'zoom': 0L},
1595 1594
                        loc('kodiswift/plugin.py'): {'attrib-starts': [('Plug'\
1596 1595
         'in',
1597 1596
         27),
1598
-        ('Plugin._dispatch',
1599
-         318)],
1600
-        'first-line': 317L,
1597
+        ('Plugin._parse_request',
1598
+         341)],
1599
+        'first-line': 0L,
1601 1600
         'folded-linenos': [],
1602
-        'sel-line': 331L,
1603
-        'sel-line-start': 12298L,
1604
-        'selection_end': 12298L,
1605
-        'selection_start': 12298L,
1601
+        'sel-line': 356L,
1602
+        'sel-line-start': 13246L,
1603
+        'selection_end': 13246L,
1604
+        'selection_start': 13246L,
1606 1605
         'zoom': 0L},
1607 1606
                        loc('kodiswift/xbmcmixin.py'): {'attrib-starts': [('X'\
1608 1607
         'BMCMixin',
1609 1608
         21),
1610
-        ('XBMCMixin.finish',
1611
-         473)],
1612
-        'first-line': 516L,
1609
+        ('XBMCMixin._listitemify',
1610
+         531)],
1611
+        'first-line': 532L,
1613 1612
         'folded-linenos': [],
1614
-        'sel-line': 526L,
1615
-        'sel-line-start': 22326L,
1616
-        'selection_end': 22326L,
1617
-        'selection_start': 22326L,
1613
+        'sel-line': 543L,
1614
+        'sel-line-start': 23072L,
1615
+        'selection_end': 23119L,
1616
+        'selection_start': 23110L,
1618 1617
         'zoom': 0L},
1619 1618
                        loc('plugin.image.photostation/resources/language/English/strings.xml'): {'a'\
1620 1619
         'ttrib-starts': [],
@@ -1661,6 +1660,15 @@ guimgr.visual-state = {loc('../../../../Python27/Lib/site-packages/kodiswift/lis
1661 1660
         'sel-line-start': 0L,
1662 1661
         'selection_end': 0L,
1663 1662
         'selection_start': 0L},
1663
+                       loc('resources/language/English/strings.xml'): {'attr'\
1664
+        'ib-starts': [],
1665
+        'first-line': 15L,
1666
+        'folded-linenos': [],
1667
+        'sel-line': 21L,
1668
+        'sel-line-start': 785L,
1669
+        'selection_end': 785L,
1670
+        'selection_start': 785L,
1671
+        'zoom': 0L},
1664 1672
                        loc('run.py'): {'attrib-starts': [],
1665 1673
                                        'first-line': 0L,
1666 1674
                                        'folded-linenos': [],
@@ -1859,6 +1867,16 @@ guimgr.visual-state = {loc('../../../../Python27/Lib/site-packages/kodiswift/lis
1859 1867
         'sel-line-start': 12403,
1860 1868
         'selection_end': 12403,
1861 1869
         'selection_start': 12403},
1870
+                       loc('../../../../Users/user/AppData/Roaming/Kodi/addons/plugin.image.photostation/addon.py'): {'a'\
1871
+        'ttrib-starts': [],
1872
+        'code-line': '',
1873
+        'first-line': 0L,
1874
+        'folded-linenos': [],
1875
+        'sel-line': 0L,
1876
+        'sel-line-start': 0L,
1877
+        'selection_end': 0L,
1878
+        'selection_start': 0L,
1879
+        'zoom': 0L},
1862 1880
                        loc('x-wingide-zip://C:/Python25/Lib/site-packages/argparse-1.1-py2.5.egg//argparse.py'): {'a'\
1863 1881
         'ttrib-starts': [('_ActionsContainer',
1864 1882
                           1187),

二進制
release/plugin.image.photostation-0.1.1.zip 查看文件


二進制
release/plugin.image.photostation-0.1.10.zip 查看文件


二進制
release/plugin.image.photostation-0.1.13.zip 查看文件


二進制
release/plugin.image.photostation-0.1.14.zip 查看文件


二進制
release/plugin.image.photostation-0.1.15.zip 查看文件


二進制
release/plugin.image.photostation-0.1.17.zip 查看文件


二進制
release/plugin.image.photostation-0.1.2.zip 查看文件


+ 0
- 0
release/plugin.image.photostation-0.1.3.zip 查看文件


部分文件因文件數量過多而無法顯示