drg_elfinder 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (185) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/Rakefile +8 -0
  5. data/app/assets/config/drg_elfinder_manifest.js +1 -0
  6. data/app/assets/javascripts/drg_elfinder.js +15 -0
  7. data/app/assets/javascripts/elfinder/Changelog +1283 -0
  8. data/app/assets/javascripts/elfinder/LICENSE.md +15 -0
  9. data/app/assets/javascripts/elfinder/README.md +216 -0
  10. data/app/assets/javascripts/elfinder/bower.json +28 -0
  11. data/app/assets/javascripts/elfinder/composer.json +39 -0
  12. data/app/assets/javascripts/elfinder/css/elfinder.full.css +5350 -0
  13. data/app/assets/javascripts/elfinder/css/elfinder.min.css +9 -0
  14. data/app/assets/javascripts/elfinder/css/theme.css +430 -0
  15. data/app/assets/javascripts/elfinder/elfinder.html +74 -0
  16. data/app/assets/javascripts/elfinder/elfinder.legacy.html +94 -0
  17. data/app/assets/javascripts/elfinder/img/arrows-active.png +0 -0
  18. data/app/assets/javascripts/elfinder/img/arrows-normal.png +0 -0
  19. data/app/assets/javascripts/elfinder/img/crop.gif +0 -0
  20. data/app/assets/javascripts/elfinder/img/dialogs.png +0 -0
  21. data/app/assets/javascripts/elfinder/img/edit_aceeditor.png +0 -0
  22. data/app/assets/javascripts/elfinder/img/edit_ckeditor.png +0 -0
  23. data/app/assets/javascripts/elfinder/img/edit_ckeditor5.png +0 -0
  24. data/app/assets/javascripts/elfinder/img/edit_codemirror.png +0 -0
  25. data/app/assets/javascripts/elfinder/img/edit_creativecloud.png +0 -0
  26. data/app/assets/javascripts/elfinder/img/edit_onlineconvert.png +0 -0
  27. data/app/assets/javascripts/elfinder/img/edit_pixlreditor.png +0 -0
  28. data/app/assets/javascripts/elfinder/img/edit_pixlrexpress.png +0 -0
  29. data/app/assets/javascripts/elfinder/img/edit_simplemde.png +0 -0
  30. data/app/assets/javascripts/elfinder/img/edit_tinymce.png +0 -0
  31. data/app/assets/javascripts/elfinder/img/edit_tuiimgedit.png +0 -0
  32. data/app/assets/javascripts/elfinder/img/edit_zohooffice.png +0 -0
  33. data/app/assets/javascripts/elfinder/img/editor-icons.png +0 -0
  34. data/app/assets/javascripts/elfinder/img/icons-big.png +0 -0
  35. data/app/assets/javascripts/elfinder/img/icons-big.svg +1 -0
  36. data/app/assets/javascripts/elfinder/img/icons-small.png +0 -0
  37. data/app/assets/javascripts/elfinder/img/logo.png +0 -0
  38. data/app/assets/javascripts/elfinder/img/progress.gif +0 -0
  39. data/app/assets/javascripts/elfinder/img/quicklook-bg.png +0 -0
  40. data/app/assets/javascripts/elfinder/img/quicklook-icons.png +0 -0
  41. data/app/assets/javascripts/elfinder/img/resize.png +0 -0
  42. data/app/assets/javascripts/elfinder/img/spinner-mini.gif +0 -0
  43. data/app/assets/javascripts/elfinder/img/toolbar.png +0 -0
  44. data/app/assets/javascripts/elfinder/img/trashmesh.png +0 -0
  45. data/app/assets/javascripts/elfinder/img/tui-icon-a.svg +235 -0
  46. data/app/assets/javascripts/elfinder/img/tui-icon-b.svg +224 -0
  47. data/app/assets/javascripts/elfinder/img/tui-icon-c.svg +224 -0
  48. data/app/assets/javascripts/elfinder/img/tui-icon-d.svg +224 -0
  49. data/app/assets/javascripts/elfinder/img/ui-icons_ffffff_256x240.png +0 -0
  50. data/app/assets/javascripts/elfinder/img/volume_icon_box.png +0 -0
  51. data/app/assets/javascripts/elfinder/img/volume_icon_box.svg +1 -0
  52. data/app/assets/javascripts/elfinder/img/volume_icon_dropbox.png +0 -0
  53. data/app/assets/javascripts/elfinder/img/volume_icon_dropbox.svg +1 -0
  54. data/app/assets/javascripts/elfinder/img/volume_icon_ftp.png +0 -0
  55. data/app/assets/javascripts/elfinder/img/volume_icon_ftp.svg +1 -0
  56. data/app/assets/javascripts/elfinder/img/volume_icon_googledrive.png +0 -0
  57. data/app/assets/javascripts/elfinder/img/volume_icon_googledrive.svg +1 -0
  58. data/app/assets/javascripts/elfinder/img/volume_icon_local.png +0 -0
  59. data/app/assets/javascripts/elfinder/img/volume_icon_local.svg +1 -0
  60. data/app/assets/javascripts/elfinder/img/volume_icon_network.png +0 -0
  61. data/app/assets/javascripts/elfinder/img/volume_icon_network.svg +1 -0
  62. data/app/assets/javascripts/elfinder/img/volume_icon_onedrive.png +0 -0
  63. data/app/assets/javascripts/elfinder/img/volume_icon_onedrive.svg +1 -0
  64. data/app/assets/javascripts/elfinder/img/volume_icon_sql.png +0 -0
  65. data/app/assets/javascripts/elfinder/img/volume_icon_sql.svg +1 -0
  66. data/app/assets/javascripts/elfinder/img/volume_icon_trash.png +0 -0
  67. data/app/assets/javascripts/elfinder/img/volume_icon_trash.svg +1 -0
  68. data/app/assets/javascripts/elfinder/img/volume_icon_zip.png +0 -0
  69. data/app/assets/javascripts/elfinder/img/volume_icon_zip.svg +1 -0
  70. data/app/assets/javascripts/elfinder/js/elfinder.full.js +36019 -0
  71. data/app/assets/javascripts/elfinder/js/elfinder.min.js +25 -0
  72. data/app/assets/javascripts/elfinder/js/extras/editors.default.js +2643 -0
  73. data/app/assets/javascripts/elfinder/js/extras/editors.default.min.js +2 -0
  74. data/app/assets/javascripts/elfinder/js/extras/quicklook.googledocs.js +75 -0
  75. data/app/assets/javascripts/elfinder/js/extras/quicklook.googledocs.min.js +1 -0
  76. data/app/assets/javascripts/elfinder/js/i18n/elfinder.LANG.js +587 -0
  77. data/app/assets/javascripts/elfinder/js/i18n/elfinder.ar.js +580 -0
  78. data/app/assets/javascripts/elfinder/js/i18n/elfinder.bg.js +559 -0
  79. data/app/assets/javascripts/elfinder/js/i18n/elfinder.ca.js +375 -0
  80. data/app/assets/javascripts/elfinder/js/i18n/elfinder.cs.js +581 -0
  81. data/app/assets/javascripts/elfinder/js/i18n/elfinder.da.js +580 -0
  82. data/app/assets/javascripts/elfinder/js/i18n/elfinder.de.js +582 -0
  83. data/app/assets/javascripts/elfinder/js/i18n/elfinder.el.js +374 -0
  84. data/app/assets/javascripts/elfinder/js/i18n/elfinder.es.js +546 -0
  85. data/app/assets/javascripts/elfinder/js/i18n/elfinder.fa.js +580 -0
  86. data/app/assets/javascripts/elfinder/js/i18n/elfinder.fallback.js +11 -0
  87. data/app/assets/javascripts/elfinder/js/i18n/elfinder.fo.js +419 -0
  88. data/app/assets/javascripts/elfinder/js/i18n/elfinder.fr.js +578 -0
  89. data/app/assets/javascripts/elfinder/js/i18n/elfinder.fr_CA.js +580 -0
  90. data/app/assets/javascripts/elfinder/js/i18n/elfinder.he.js +375 -0
  91. data/app/assets/javascripts/elfinder/js/i18n/elfinder.hr.js +434 -0
  92. data/app/assets/javascripts/elfinder/js/i18n/elfinder.hu.js +580 -0
  93. data/app/assets/javascripts/elfinder/js/i18n/elfinder.id.js +498 -0
  94. data/app/assets/javascripts/elfinder/js/i18n/elfinder.it.js +552 -0
  95. data/app/assets/javascripts/elfinder/js/i18n/elfinder.ja.js +581 -0
  96. data/app/assets/javascripts/elfinder/js/i18n/elfinder.ko.js +582 -0
  97. data/app/assets/javascripts/elfinder/js/i18n/elfinder.nl.js +581 -0
  98. data/app/assets/javascripts/elfinder/js/i18n/elfinder.no.js +374 -0
  99. data/app/assets/javascripts/elfinder/js/i18n/elfinder.pl.js +580 -0
  100. data/app/assets/javascripts/elfinder/js/i18n/elfinder.pt_BR.js +580 -0
  101. data/app/assets/javascripts/elfinder/js/i18n/elfinder.ro.js +417 -0
  102. data/app/assets/javascripts/elfinder/js/i18n/elfinder.ru.js +582 -0
  103. data/app/assets/javascripts/elfinder/js/i18n/elfinder.si.js +537 -0
  104. data/app/assets/javascripts/elfinder/js/i18n/elfinder.sk.js +581 -0
  105. data/app/assets/javascripts/elfinder/js/i18n/elfinder.sl.js +374 -0
  106. data/app/assets/javascripts/elfinder/js/i18n/elfinder.sr.js +374 -0
  107. data/app/assets/javascripts/elfinder/js/i18n/elfinder.sv.js +375 -0
  108. data/app/assets/javascripts/elfinder/js/i18n/elfinder.tr.js +583 -0
  109. data/app/assets/javascripts/elfinder/js/i18n/elfinder.ug_CN.js +374 -0
  110. data/app/assets/javascripts/elfinder/js/i18n/elfinder.uk.js +580 -0
  111. data/app/assets/javascripts/elfinder/js/i18n/elfinder.vi.js +579 -0
  112. data/app/assets/javascripts/elfinder/js/i18n/elfinder.zh_CN.js +585 -0
  113. data/app/assets/javascripts/elfinder/js/i18n/elfinder.zh_TW.js +582 -0
  114. data/app/assets/javascripts/elfinder/js/i18n/help/cs.html.js +10 -0
  115. data/app/assets/javascripts/elfinder/js/i18n/help/de.html.js +15 -0
  116. data/app/assets/javascripts/elfinder/js/i18n/help/en.html.js +10 -0
  117. data/app/assets/javascripts/elfinder/js/i18n/help/es.html.js +10 -0
  118. data/app/assets/javascripts/elfinder/js/i18n/help/ja.html.js +10 -0
  119. data/app/assets/javascripts/elfinder/js/i18n/help/ko.html.js +10 -0
  120. data/app/assets/javascripts/elfinder/js/i18n/help/pl.html.js +10 -0
  121. data/app/assets/javascripts/elfinder/js/i18n/help/ru.html.js +10 -0
  122. data/app/assets/javascripts/elfinder/js/i18n/help/sk.html.js +10 -0
  123. data/app/assets/javascripts/elfinder/js/i18n/help/tr.html.js +10 -0
  124. data/app/assets/javascripts/elfinder/js/proxy/elFinderSupportVer1.js +408 -0
  125. data/app/assets/javascripts/elfinder/js/worker/calcfilehash.js +20 -0
  126. data/app/assets/javascripts/elfinder/js/worker/quicklook.tiff.js +7 -0
  127. data/app/assets/javascripts/elfinder/js/worker/quicklook.unzip.js +55 -0
  128. data/app/assets/javascripts/elfinder/main.default.js +175 -0
  129. data/app/assets/javascripts/elfinder/package.json +13 -0
  130. data/app/assets/javascripts/elfinder/php/MySQLStorage.sql +47 -0
  131. data/app/assets/javascripts/elfinder/php/autoload.php +56 -0
  132. data/app/assets/javascripts/elfinder/php/connector.maximal.php-dist +449 -0
  133. data/app/assets/javascripts/elfinder/php/connector.minimal.php-dist +180 -0
  134. data/app/assets/javascripts/elfinder/php/editors/OnlineConvert/editor.php +113 -0
  135. data/app/assets/javascripts/elfinder/php/editors/ZipArchive/editor.php +12 -0
  136. data/app/assets/javascripts/elfinder/php/editors/ZohoOffice/editor.php +206 -0
  137. data/app/assets/javascripts/elfinder/php/editors/editor.php +79 -0
  138. data/app/assets/javascripts/elfinder/php/elFinder.class.php +5401 -0
  139. data/app/assets/javascripts/elfinder/php/elFinderConnector.class.php +380 -0
  140. data/app/assets/javascripts/elfinder/php/elFinderFlysystemGoogleDriveNetmount.php +380 -0
  141. data/app/assets/javascripts/elfinder/php/elFinderPlugin.php +113 -0
  142. data/app/assets/javascripts/elfinder/php/elFinderSession.php +335 -0
  143. data/app/assets/javascripts/elfinder/php/elFinderSessionInterface.php +57 -0
  144. data/app/assets/javascripts/elfinder/php/elFinderVolumeBox.class.php +1972 -0
  145. data/app/assets/javascripts/elfinder/php/elFinderVolumeDriver.class.php +7651 -0
  146. data/app/assets/javascripts/elfinder/php/elFinderVolumeDropbox.class.php +1464 -0
  147. data/app/assets/javascripts/elfinder/php/elFinderVolumeDropbox2.class.php +1516 -0
  148. data/app/assets/javascripts/elfinder/php/elFinderVolumeFTP.class.php +1810 -0
  149. data/app/assets/javascripts/elfinder/php/elFinderVolumeGoogleDrive.class.php +2163 -0
  150. data/app/assets/javascripts/elfinder/php/elFinderVolumeGroup.class.php +315 -0
  151. data/app/assets/javascripts/elfinder/php/elFinderVolumeLocalFileSystem.class.php +1477 -0
  152. data/app/assets/javascripts/elfinder/php/elFinderVolumeMySQL.class.php +1022 -0
  153. data/app/assets/javascripts/elfinder/php/elFinderVolumeOneDrive.class.php +2131 -0
  154. data/app/assets/javascripts/elfinder/php/elFinderVolumeSFTPphpseclib.class.php +838 -0
  155. data/app/assets/javascripts/elfinder/php/elFinderVolumeTrash.class.php +51 -0
  156. data/app/assets/javascripts/elfinder/php/elFinderVolumeTrashMySQL.class.php +51 -0
  157. data/app/assets/javascripts/elfinder/php/libs/GdBmp.php +473 -0
  158. data/app/assets/javascripts/elfinder/php/mime.types +781 -0
  159. data/app/assets/javascripts/elfinder/php/plugins/AutoResize/plugin.php +151 -0
  160. data/app/assets/javascripts/elfinder/php/plugins/AutoRotate/plugin.php +148 -0
  161. data/app/assets/javascripts/elfinder/php/plugins/Normalizer/plugin.php +204 -0
  162. data/app/assets/javascripts/elfinder/php/plugins/Sanitizer/plugin.php +157 -0
  163. data/app/assets/javascripts/elfinder/php/plugins/Watermark/logo.png +0 -0
  164. data/app/assets/javascripts/elfinder/php/plugins/Watermark/plugin.php +432 -0
  165. data/app/assets/javascripts/elfinder/php/plugins/WinRemoveTailDots/plugin.php +114 -0
  166. data/app/assets/javascripts/elfinder/php/resources/image.png +0 -0
  167. data/app/assets/javascripts/elfinder/php/resources/video.png +0 -0
  168. data/app/assets/javascripts/elfinder/sounds/rm.wav +0 -0
  169. data/app/controllers/dc_elfinder_controller.rb +63 -0
  170. data/app/models/drgcms_form_fields/elfinder.rb +85 -0
  171. data/changelog.md +0 -0
  172. data/lib/drg_elfinder/engine.rb +5 -0
  173. data/lib/drg_elfinder/version.rb +3 -0
  174. data/lib/drg_elfinder.rb +9 -0
  175. data/lib/el_finder/action.rb +19 -0
  176. data/lib/el_finder/base64.rb +24 -0
  177. data/lib/el_finder/connector.rb +625 -0
  178. data/lib/el_finder/image.rb +33 -0
  179. data/lib/el_finder/mime_type.rb +86 -0
  180. data/lib/el_finder/pathname.rb +185 -0
  181. data/lib/el_finder/version.rb +5 -0
  182. data/lib/el_finder.rb +8 -0
  183. data/lib/patches/patch_for_elfinder.rb +115 -0
  184. data/lib/tasks/drg_elfinder_tasks.rake +10 -0
  185. metadata +245 -0
@@ -0,0 +1,2163 @@
1
+ <?php
2
+
3
+ /**
4
+ * Simple elFinder driver for GoogleDrive
5
+ * google-api-php-client-2.x or above.
6
+ *
7
+ * @author Dmitry (dio) Levashov
8
+ * @author Cem (discofever)
9
+ **/
10
+ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
11
+ {
12
+ /**
13
+ * Driver id
14
+ * Must be started from letter and contains [a-z0-9]
15
+ * Used as part of volume id.
16
+ *
17
+ * @var string
18
+ **/
19
+ protected $driverId = 'gd';
20
+
21
+ /**
22
+ * Google API client object.
23
+ *
24
+ * @var object
25
+ **/
26
+ protected $client = null;
27
+
28
+ /**
29
+ * GoogleDrive service object.
30
+ *
31
+ * @var object
32
+ **/
33
+ protected $service = null;
34
+
35
+ /**
36
+ * Cache of parents of each directories.
37
+ *
38
+ * @var array
39
+ */
40
+ protected $parents = [];
41
+
42
+ /**
43
+ * Cache of chiled directories of each directories.
44
+ *
45
+ * @var array
46
+ */
47
+ protected $directories = null;
48
+
49
+ /**
50
+ * Cache of itemID => name of each items.
51
+ *
52
+ * @var array
53
+ */
54
+ protected $names = [];
55
+
56
+ /**
57
+ * MIME tyoe of directory.
58
+ *
59
+ * @var string
60
+ */
61
+ const DIRMIME = 'application/vnd.google-apps.folder';
62
+
63
+ /**
64
+ * Fetch fields for list.
65
+ *
66
+ * @var string
67
+ */
68
+ const FETCHFIELDS_LIST = 'files(id,name,mimeType,modifiedTime,parents,permissions,size,imageMediaMetadata(height,width),thumbnailLink,webContentLink,webViewLink),nextPageToken';
69
+
70
+ /**
71
+ * Fetch fields for get.
72
+ *
73
+ * @var string
74
+ */
75
+ const FETCHFIELDS_GET = 'id,name,mimeType,modifiedTime,parents,permissions,size,imageMediaMetadata(height,width),thumbnailLink,webContentLink,webViewLink';
76
+
77
+ /**
78
+ * Directory for tmp files
79
+ * If not set driver will try to use tmbDir as tmpDir.
80
+ *
81
+ * @var string
82
+ **/
83
+ protected $tmp = '';
84
+
85
+ /**
86
+ * Net mount key.
87
+ *
88
+ * @var string
89
+ **/
90
+ public $netMountKey = '';
91
+
92
+ /**
93
+ * Current token expires
94
+ *
95
+ * @var integer
96
+ **/
97
+ private $expires;
98
+
99
+ /**
100
+ * Constructor
101
+ * Extend options with required fields.
102
+ *
103
+ * @author Dmitry (dio) Levashov
104
+ * @author Cem (DiscoFever)
105
+ **/
106
+ public function __construct()
107
+ {
108
+ $opts = [
109
+ 'client_id' => '',
110
+ 'client_secret' => '',
111
+ 'access_token' => [],
112
+ 'refresh_token' => '',
113
+ 'serviceAccountConfigFile' => '',
114
+ 'root' => 'My Drive',
115
+ 'gdAlias' => '%s@GDrive',
116
+ 'googleApiClient' => '',
117
+ 'path' => '/',
118
+ 'tmbPath' => '',
119
+ 'separator' => '/',
120
+ 'useGoogleTmb' => true,
121
+ 'acceptedName' => '#.#',
122
+ 'rootCssClass' => 'elfinder-navbar-root-googledrive',
123
+ 'publishPermission' => [
124
+ 'type' => 'anyone',
125
+ 'role' => 'reader',
126
+ 'withLink' => true,
127
+ ],
128
+ 'appsExportMap' => [
129
+ 'application/vnd.google-apps.document' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
130
+ 'application/vnd.google-apps.spreadsheet' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
131
+ 'application/vnd.google-apps.drawing' => 'application/pdf',
132
+ 'application/vnd.google-apps.presentation' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
133
+ 'application/vnd.google-apps.script' => 'application/vnd.google-apps.script+json',
134
+ 'default' => 'application/pdf',
135
+ ],
136
+ ];
137
+ $this->options = array_merge($this->options, $opts);
138
+ $this->options['mimeDetect'] = 'internal';
139
+ }
140
+
141
+ /*********************************************************************/
142
+ /* ORIGINAL FUNCTIONS */
143
+ /*********************************************************************/
144
+
145
+ /**
146
+ * Get Parent ID, Item ID, Parent Path as an array from path.
147
+ *
148
+ * @param string $path
149
+ *
150
+ * @return array
151
+ */
152
+ protected function _gd_splitPath($path)
153
+ {
154
+ $path = trim($path, '/');
155
+ $pid = '';
156
+ if ($path === '') {
157
+ $id = 'root';
158
+ $parent = '';
159
+ } else {
160
+ $path = str_replace('\\/', chr(0), $path);
161
+ $paths = explode('/', $path);
162
+ $id = array_pop($paths);
163
+ $id = str_replace(chr(0), '/', $id);
164
+ if ($paths) {
165
+ $parent = '/' . implode('/', $paths);
166
+ $pid = array_pop($paths);
167
+ } else {
168
+ $rootid = ($this->root === '/') ? 'root' : trim($this->root, '/');
169
+ if ($id === $rootid) {
170
+ $parent = '';
171
+ } else {
172
+ $parent = $this->root;
173
+ $pid = $rootid;
174
+ }
175
+ }
176
+ }
177
+
178
+ return array($pid, $id, $parent);
179
+ }
180
+
181
+ /**
182
+ * Drive query and fetchAll.
183
+ *
184
+ * @param string $sql
185
+ *
186
+ * @return bool|array
187
+ */
188
+ private function _gd_query($opts)
189
+ {
190
+ $result = [];
191
+ $pageToken = null;
192
+ $parameters = [
193
+ 'fields' => self::FETCHFIELDS_LIST,
194
+ 'pageSize' => 1000,
195
+ 'spaces' => 'drive',
196
+ ];
197
+
198
+ if (is_array($opts)) {
199
+ $parameters = array_merge($parameters, $opts);
200
+ }
201
+ do {
202
+ try {
203
+ if ($pageToken) {
204
+ $parameters['pageToken'] = $pageToken;
205
+ }
206
+ $files = $this->service->files->listFiles($parameters);
207
+
208
+ $result = array_merge($result, $files->getFiles());
209
+ $pageToken = $files->getNextPageToken();
210
+ } catch (Exception $e) {
211
+ $pageToken = null;
212
+ }
213
+ } while ($pageToken);
214
+
215
+ return $result;
216
+ }
217
+
218
+ /**
219
+ * Get dat(googledrive metadata) from GoogleDrive.
220
+ *
221
+ * @param string $path
222
+ *
223
+ * @return array googledrive metadata
224
+ */
225
+ private function _gd_getFile($path, $fields = '')
226
+ {
227
+ list(, $itemId) = $this->_gd_splitPath($path);
228
+ if (!$fields) {
229
+ $fields = self::FETCHFIELDS_GET;
230
+ }
231
+ try {
232
+ $file = $this->service->files->get($itemId, ['fields' => $fields]);
233
+ if ($file instanceof Google_Service_Drive_DriveFile) {
234
+ return $file;
235
+ } else {
236
+ return [];
237
+ }
238
+ } catch (Exception $e) {
239
+ return [];
240
+ }
241
+ }
242
+
243
+ /**
244
+ * Parse line from googledrive metadata output and return file stat (array).
245
+ *
246
+ * @param array $raw line from ftp_rawlist() output
247
+ *
248
+ * @return array
249
+ * @author Dmitry Levashov
250
+ **/
251
+ protected function _gd_parseRaw($raw)
252
+ {
253
+ $stat = [];
254
+
255
+ $stat['iid'] = isset($raw['id']) ? $raw['id'] : 'root';
256
+ $stat['name'] = isset($raw['name']) ? $raw['name'] : '';
257
+ if (isset($raw['modifiedTime'])) {
258
+ $stat['ts'] = strtotime($raw['modifiedTime']);
259
+ }
260
+
261
+ if ($raw['mimeType'] === self::DIRMIME) {
262
+ $stat['mime'] = 'directory';
263
+ $stat['size'] = 0;
264
+ } else {
265
+ $stat['mime'] = $raw['mimeType'] == 'image/bmp' ? 'image/x-ms-bmp' : $raw['mimeType'];
266
+ $stat['size'] = (int)$raw['size'];
267
+ if ($size = $raw->getImageMediaMetadata()) {
268
+ $stat['width'] = $size['width'];
269
+ $stat['height'] = $size['height'];
270
+ }
271
+
272
+ $published = $this->_gd_isPublished($raw);
273
+
274
+ if ($this->options['useGoogleTmb']) {
275
+ if (isset($raw['thumbnailLink'])) {
276
+ if ($published) {
277
+ $stat['tmb'] = 'drive.google.com/thumbnail?authuser=0&sz=s' . $this->options['tmbSize'] . '&id=' . $raw['id'];
278
+ } else {
279
+ $stat['tmb'] = substr($raw['thumbnailLink'], 8); // remove "https://"
280
+ }
281
+ } else {
282
+ $stat['tmb'] = '';
283
+ }
284
+ }
285
+
286
+ if ($published) {
287
+ $stat['url'] = $this->_gd_getLink($raw);
288
+ } elseif (!$this->disabledGetUrl) {
289
+ $stat['url'] = '1';
290
+ }
291
+ }
292
+
293
+ return $stat;
294
+ }
295
+
296
+ /**
297
+ * Get dat(googledrive metadata) from GoogleDrive.
298
+ *
299
+ * @param string $path
300
+ *
301
+ * @return array googledrive metadata
302
+ */
303
+ private function _gd_getNameByPath($path)
304
+ {
305
+ list(, $itemId) = $this->_gd_splitPath($path);
306
+ if (!$this->names) {
307
+ $this->_gd_getDirectoryData();
308
+ }
309
+
310
+ return isset($this->names[$itemId]) ? $this->names[$itemId] : '';
311
+ }
312
+
313
+ /**
314
+ * Make cache of $parents, $names and $directories.
315
+ *
316
+ * @param bool $usecache
317
+ */
318
+ protected function _gd_getDirectoryData($usecache = true)
319
+ {
320
+ if ($usecache) {
321
+ $cache = $this->session->get($this->id . $this->netMountKey, []);
322
+ if ($cache) {
323
+ $this->parents = $cache['parents'];
324
+ $this->names = $cache['names'];
325
+ $this->directories = $cache['directories'];
326
+
327
+ return;
328
+ }
329
+ }
330
+
331
+ $root = '';
332
+ if ($this->root === '/') {
333
+ // get root id
334
+ if ($res = $this->_gd_getFile('/', 'id')) {
335
+ $root = $res->getId();
336
+ }
337
+ }
338
+
339
+ $data = [];
340
+ $opts = [
341
+ 'fields' => 'files(id, name, parents)',
342
+ 'q' => sprintf('trashed=false and mimeType="%s"', self::DIRMIME),
343
+ ];
344
+ $res = $this->_gd_query($opts);
345
+ foreach ($res as $raw) {
346
+ if ($parents = $raw->getParents()) {
347
+ $id = $raw->getId();
348
+ $this->parents[$id] = $parents;
349
+ $this->names[$id] = $raw->getName();
350
+ foreach ($parents as $p) {
351
+ if (isset($data[$p])) {
352
+ $data[$p][] = $id;
353
+ } else {
354
+ $data[$p] = [$id];
355
+ }
356
+ }
357
+ }
358
+ }
359
+ if ($root && isset($data[$root])) {
360
+ $data['root'] = $data[$root];
361
+ }
362
+ $this->directories = $data;
363
+ $this->session->set($this->id . $this->netMountKey, [
364
+ 'parents' => $this->parents,
365
+ 'names' => $this->names,
366
+ 'directories' => $this->directories,
367
+ ]);
368
+ }
369
+
370
+ /**
371
+ * Get descendants directories.
372
+ *
373
+ * @param string $itemId
374
+ *
375
+ * @return array
376
+ */
377
+ protected function _gd_getDirectories($itemId)
378
+ {
379
+ $ret = [];
380
+ if ($this->directories === null) {
381
+ $this->_gd_getDirectoryData();
382
+ }
383
+ $data = $this->directories;
384
+ if (isset($data[$itemId])) {
385
+ $ret = $data[$itemId];
386
+ foreach ($data[$itemId] as $cid) {
387
+ $ret = array_merge($ret, $this->_gd_getDirectories($cid));
388
+ }
389
+ }
390
+
391
+ return $ret;
392
+ }
393
+
394
+ /**
395
+ * Get ID based path from item ID.
396
+ *
397
+ * @param string $id
398
+ *
399
+ * @return array
400
+ */
401
+ protected function _gd_getMountPaths($id)
402
+ {
403
+ $root = false;
404
+ if ($this->directories === null) {
405
+ $this->_gd_getDirectoryData();
406
+ }
407
+ list($pid) = explode('/', $id, 2);
408
+ $path = $id;
409
+ if ('/' . $pid === $this->root) {
410
+ $root = true;
411
+ } elseif (!isset($this->parents[$pid])) {
412
+ $root = true;
413
+ $path = ltrim(substr($path, strlen($pid)), '/');
414
+ }
415
+ $res = [];
416
+ if ($root) {
417
+ if ($this->root === '/' || strpos('/' . $path, $this->root) === 0) {
418
+ $res = [(strpos($path, '/') === false) ? '/' : ('/' . $path)];
419
+ }
420
+ } else {
421
+ foreach ($this->parents[$pid] as $p) {
422
+ $_p = $p . '/' . $path;
423
+ $res = array_merge($res, $this->_gd_getMountPaths($_p));
424
+ }
425
+ }
426
+
427
+ return $res;
428
+ }
429
+
430
+ /**
431
+ * Return is published.
432
+ *
433
+ * @param object $file
434
+ *
435
+ * @return bool
436
+ */
437
+ protected function _gd_isPublished($file)
438
+ {
439
+ $res = false;
440
+ $pType = $this->options['publishPermission']['type'];
441
+ $pRole = $this->options['publishPermission']['role'];
442
+ if ($permissions = $file->getPermissions()) {
443
+ foreach ($permissions as $permission) {
444
+ if ($permission->type === $pType && $permission->role === $pRole) {
445
+ $res = true;
446
+ break;
447
+ }
448
+ }
449
+ }
450
+
451
+ return $res;
452
+ }
453
+
454
+ /**
455
+ * return item URL link.
456
+ *
457
+ * @param object $file
458
+ *
459
+ * @return string
460
+ */
461
+ protected function _gd_getLink($file)
462
+ {
463
+ if (strpos($file->mimeType, 'application/vnd.google-apps.') !== 0) {
464
+ if ($url = $file->getWebContentLink()) {
465
+ return str_replace('export=download', 'export=media', $url);
466
+ }
467
+ }
468
+ if ($url = $file->getWebViewLink()) {
469
+ return $url;
470
+ }
471
+
472
+ return '';
473
+ }
474
+
475
+ /**
476
+ * Get download url.
477
+ *
478
+ * @param Google_Service_Drive_DriveFile $file
479
+ *
480
+ * @return string|false
481
+ */
482
+ protected function _gd_getDownloadUrl($file)
483
+ {
484
+ if (strpos($file->mimeType, 'application/vnd.google-apps.') !== 0) {
485
+ return 'https://www.googleapis.com/drive/v3/files/' . $file->getId() . '?alt=media';
486
+ } else {
487
+ $mimeMap = $this->options['appsExportMap'];
488
+ if (isset($mimeMap[$file->getMimeType()])) {
489
+ $mime = $mimeMap[$file->getMimeType()];
490
+ } else {
491
+ $mime = $mimeMap['default'];
492
+ }
493
+ $mime = rawurlencode($mime);
494
+
495
+ return 'https://www.googleapis.com/drive/v3/files/' . $file->getId() . '/export?mimeType=' . $mime;
496
+ }
497
+
498
+ return false;
499
+ }
500
+
501
+ /**
502
+ * Get thumbnail from GoogleDrive.com.
503
+ *
504
+ * @param string $path
505
+ *
506
+ * @return string | boolean
507
+ */
508
+ protected function _gd_getThumbnail($path)
509
+ {
510
+ list(, $itemId) = $this->_gd_splitPath($path);
511
+
512
+ try {
513
+ $contents = $this->service->files->get($itemId, [
514
+ 'alt' => 'media',
515
+ ]);
516
+ $contents = $contents->getBody()->detach();
517
+ rewind($contents);
518
+
519
+ return $contents;
520
+ } catch (Exception $e) {
521
+ return false;
522
+ }
523
+ }
524
+
525
+ /**
526
+ * Publish permissions specified path item.
527
+ *
528
+ * @param string $path
529
+ *
530
+ * @return bool
531
+ */
532
+ protected function _gd_publish($path)
533
+ {
534
+ if ($file = $this->_gd_getFile($path)) {
535
+ if ($this->_gd_isPublished($file)) {
536
+ return true;
537
+ }
538
+ try {
539
+ if ($this->service->permissions->create($file->getId(), new \Google_Service_Drive_Permission($this->options['publishPermission']))) {
540
+ return true;
541
+ }
542
+ } catch (Exception $e) {
543
+ return false;
544
+ }
545
+ }
546
+
547
+ return false;
548
+ }
549
+
550
+ /**
551
+ * unPublish permissions specified path.
552
+ *
553
+ * @param string $path
554
+ *
555
+ * @return bool
556
+ */
557
+ protected function _gd_unPublish($path)
558
+ {
559
+ if ($file = $this->_gd_getFile($path)) {
560
+ if (!$this->_gd_isPublished($file)) {
561
+ return true;
562
+ }
563
+ $permissions = $file->getPermissions();
564
+ $pType = $this->options['publishPermission']['type'];
565
+ $pRole = $this->options['publishPermission']['role'];
566
+ try {
567
+ foreach ($permissions as $permission) {
568
+ if ($permission->type === $pType && $permission->role === $pRole) {
569
+ $this->service->permissions->delete($file->getId(), $permission->getId());
570
+
571
+ return true;
572
+ break;
573
+ }
574
+ }
575
+ } catch (Exception $e) {
576
+ return false;
577
+ }
578
+ }
579
+
580
+ return false;
581
+ }
582
+
583
+ /**
584
+ * Read file chunk.
585
+ *
586
+ * @param resource $handle
587
+ * @param int $chunkSize
588
+ *
589
+ * @return string
590
+ */
591
+ protected function _gd_readFileChunk($handle, $chunkSize)
592
+ {
593
+ $byteCount = 0;
594
+ $giantChunk = '';
595
+ while (!feof($handle)) {
596
+ // fread will never return more than 8192 bytes if the stream is read buffered and it does not represent a plain file
597
+ $chunk = fread($handle, 8192);
598
+ $byteCount += strlen($chunk);
599
+ $giantChunk .= $chunk;
600
+ if ($byteCount >= $chunkSize) {
601
+ return $giantChunk;
602
+ }
603
+ }
604
+
605
+ return $giantChunk;
606
+ }
607
+
608
+ /*********************************************************************/
609
+ /* EXTENDED FUNCTIONS */
610
+ /*********************************************************************/
611
+
612
+ /**
613
+ * Prepare
614
+ * Call from elFinder::netmout() before volume->mount().
615
+ *
616
+ * @return array
617
+ * @author Naoki Sawada
618
+ * @author Raja Sharma updating for GoogleDrive
619
+ **/
620
+ public function netmountPrepare($options)
621
+ {
622
+ if (empty($options['client_id']) && defined('ELFINDER_GOOGLEDRIVE_CLIENTID')) {
623
+ $options['client_id'] = ELFINDER_GOOGLEDRIVE_CLIENTID;
624
+ }
625
+ if (empty($options['client_secret']) && defined('ELFINDER_GOOGLEDRIVE_CLIENTSECRET')) {
626
+ $options['client_secret'] = ELFINDER_GOOGLEDRIVE_CLIENTSECRET;
627
+ }
628
+ if (empty($options['googleApiClient']) && defined('ELFINDER_GOOGLEDRIVE_GOOGLEAPICLIENT')) {
629
+ $options['googleApiClient'] = ELFINDER_GOOGLEDRIVE_GOOGLEAPICLIENT;
630
+ include_once $options['googleApiClient'];
631
+ }
632
+
633
+ if (!isset($options['pass'])) {
634
+ $options['pass'] = '';
635
+ }
636
+
637
+ try {
638
+ $client = new \Google_Client();
639
+ $client->setClientId($options['client_id']);
640
+ $client->setClientSecret($options['client_secret']);
641
+
642
+ if ($options['pass'] === 'reauth') {
643
+ $options['pass'] = '';
644
+ $this->session->set('GoogleDriveAuthParams', [])->set('GoogleDriveTokens', []);
645
+ } elseif ($options['pass'] === 'googledrive') {
646
+ $options['pass'] = '';
647
+ }
648
+
649
+ $options = array_merge($this->session->get('GoogleDriveAuthParams', []), $options);
650
+
651
+ if (!isset($options['access_token'])) {
652
+ $options['access_token'] = $this->session->get('GoogleDriveTokens', []);
653
+ $this->session->remove('GoogleDriveTokens');
654
+ }
655
+ $aToken = $options['access_token'];
656
+
657
+ $rootObj = $service = null;
658
+ if ($aToken) {
659
+ try {
660
+ $client->setAccessToken($aToken);
661
+ if ($client->isAccessTokenExpired()) {
662
+ $aToken = array_merge($aToken, $client->fetchAccessTokenWithRefreshToken());
663
+ $client->setAccessToken($aToken);
664
+ }
665
+ $service = new \Google_Service_Drive($client);
666
+ $rootObj = $service->files->get('root');
667
+
668
+ $options['access_token'] = $aToken;
669
+ $this->session->set('GoogleDriveAuthParams', $options);
670
+ } catch (Exception $e) {
671
+ $aToken = [];
672
+ $options['access_token'] = [];
673
+ if ($options['user'] !== 'init') {
674
+ $this->session->set('GoogleDriveAuthParams', $options);
675
+
676
+ return ['exit' => true, 'error' => elFinder::ERROR_REAUTH_REQUIRE];
677
+ }
678
+ }
679
+ }
680
+
681
+ $itpCare = isset($options['code']);
682
+ $code = $itpCare? $options['code'] : (isset($_GET['code'])? $_GET['code'] : '');
683
+ if ($code || (isset($options['user']) && $options['user'] === 'init')) {
684
+ if (empty($options['url'])) {
685
+ $options['url'] = elFinder::getConnectorUrl();
686
+ }
687
+
688
+ if (isset($options['id'])) {
689
+ $callback = $options['url']
690
+ . (strpos($options['url'], '?') !== false? '&' : '?') . 'cmd=netmount&protocol=googledrive&host=' . ($options['id'] === 'elfinder'? '1' : $options['id']);
691
+ $client->setRedirectUri($callback);
692
+ }
693
+
694
+ if (!$aToken && empty($code)) {
695
+ $client->setScopes([Google_Service_Drive::DRIVE]);
696
+ if (!empty($options['offline'])) {
697
+ $client->setApprovalPrompt('force');
698
+ $client->setAccessType('offline');
699
+ }
700
+ $url = $client->createAuthUrl();
701
+
702
+ $html = '<input id="elf-volumedriver-googledrive-host-btn" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" value="{msg:btnApprove}" type="button">';
703
+ $html .= '<script>
704
+ $("#' . $options['id'] . '").elfinder("instance").trigger("netmount", {protocol: "googledrive", mode: "makebtn", url: "' . $url . '"});
705
+ </script>';
706
+ if (empty($options['pass']) && $options['host'] !== '1') {
707
+ $options['pass'] = 'return';
708
+ $this->session->set('GoogleDriveAuthParams', $options);
709
+
710
+ return ['exit' => true, 'body' => $html];
711
+ } else {
712
+ $out = [
713
+ 'node' => $options['id'],
714
+ 'json' => '{"protocol": "googledrive", "mode": "makebtn", "body" : "' . str_replace($html, '"', '\\"') . '", "error" : "' . elFinder::ERROR_ACCESS_DENIED . '"}',
715
+ 'bind' => 'netmount',
716
+ ];
717
+
718
+ return ['exit' => 'callback', 'out' => $out];
719
+ }
720
+ } else {
721
+ if ($code) {
722
+ if (!empty($options['id'])) {
723
+ $aToken = $client->fetchAccessTokenWithAuthCode($code);
724
+ $options['access_token'] = $aToken;
725
+ unset($options['code']);
726
+ $this->session->set('GoogleDriveTokens', $aToken)->set('GoogleDriveAuthParams', $options);
727
+ $out = [
728
+ 'node' => $options['id'],
729
+ 'json' => '{"protocol": "googledrive", "mode": "done", "reset": 1}',
730
+ 'bind' => 'netmount',
731
+ ];
732
+ } else {
733
+ $nodeid = ($_GET['host'] === '1')? 'elfinder' : $_GET['host'];
734
+ $out = array(
735
+ 'node' => $nodeid,
736
+ 'json' => json_encode(array(
737
+ 'protocol' => 'googledrive',
738
+ 'host' => $nodeid,
739
+ 'mode' => 'redirect',
740
+ 'options' => array(
741
+ 'id' => $nodeid,
742
+ 'code'=> $code
743
+ )
744
+ )),
745
+ 'bind' => 'netmount'
746
+ );
747
+ }
748
+ if (!$itpCare) {
749
+ return array('exit' => 'callback', 'out' => $out);
750
+ } else {
751
+ return array('exit' => true, 'body' => $out['json']);
752
+ }
753
+ }
754
+ $path = $options['path'];
755
+ if ($path === '/') {
756
+ $path = 'root';
757
+ }
758
+ $folders = [];
759
+ foreach ($service->files->listFiles([
760
+ 'pageSize' => 1000,
761
+ 'q' => sprintf('trashed = false and "%s" in parents and mimeType = "application/vnd.google-apps.folder"', $path),
762
+ ]) as $f) {
763
+ $folders[$f->getId()] = $f->getName();
764
+ }
765
+ natcasesort($folders);
766
+
767
+ if ($options['pass'] === 'folders') {
768
+ return ['exit' => true, 'folders' => $folders];
769
+ }
770
+
771
+ $folders = ['root' => $rootObj->getName()] + $folders;
772
+ $folders = json_encode($folders);
773
+ $expires = empty($aToken['refresh_token']) ? $aToken['created'] + $aToken['expires_in'] - 30 : 0;
774
+ $mnt2res = empty($aToken['refresh_token']) ? '' : ', "mnt2res": 1';
775
+ $json = '{"protocol": "googledrive", "mode": "done", "folders": ' . $folders . ', "expires": ' . $expires . $mnt2res . '}';
776
+ $options['pass'] = 'return';
777
+ $html = 'Google.com';
778
+ $html .= '<script>
779
+ $("#' . $options['id'] . '").elfinder("instance").trigger("netmount", ' . $json . ');
780
+ </script>';
781
+ $this->session->set('GoogleDriveAuthParams', $options);
782
+
783
+ return ['exit' => true, 'body' => $html];
784
+ }
785
+ }
786
+ } catch (Exception $e) {
787
+ $this->session->remove('GoogleDriveAuthParams')->remove('GoogleDriveTokens');
788
+ if (empty($options['pass'])) {
789
+ return ['exit' => true, 'body' => '{msg:' . elFinder::ERROR_ACCESS_DENIED . '}' . ' ' . $e->getMessage()];
790
+ } else {
791
+ return ['exit' => true, 'error' => [elFinder::ERROR_ACCESS_DENIED, $e->getMessage()]];
792
+ }
793
+ }
794
+
795
+ if (!$aToken) {
796
+ return ['exit' => true, 'error' => elFinder::ERROR_REAUTH_REQUIRE];
797
+ }
798
+
799
+ if ($options['path'] === '/') {
800
+ $options['path'] = 'root';
801
+ }
802
+
803
+ try {
804
+ $file = $service->files->get($options['path']);
805
+ $options['alias'] = sprintf($this->options['gdAlias'], $file->getName());
806
+ } catch (Google_Service_Exception $e) {
807
+ $err = json_decode($e->getMessage(), true);
808
+ if (isset($err['error']) && $err['error']['code'] == 404) {
809
+ return ['exit' => true, 'error' => [elFinder::ERROR_TRGDIR_NOT_FOUND, $options['path']]];
810
+ } else {
811
+ return ['exit' => true, 'error' => $e->getMessage()];
812
+ }
813
+ } catch (Exception $e) {
814
+ return ['exit' => true, 'error' => $e->getMessage()];
815
+ }
816
+
817
+ foreach (['host', 'user', 'pass', 'id', 'offline'] as $key) {
818
+ unset($options[$key]);
819
+ }
820
+
821
+ return $options;
822
+ }
823
+
824
+ /**
825
+ * process of on netunmount
826
+ * Drop `googledrive` & rm thumbs.
827
+ *
828
+ * @param $netVolumes
829
+ * @param $key
830
+ *
831
+ * @return bool
832
+ */
833
+ public function netunmount($netVolumes, $key)
834
+ {
835
+ if (!$this->options['useGoogleTmb']) {
836
+ if ($tmbs = glob(rtrim($this->options['tmbPath'], '\\/') . DIRECTORY_SEPARATOR . $this->netMountKey . '*.png')) {
837
+ foreach ($tmbs as $file) {
838
+ unlink($file);
839
+ }
840
+ }
841
+ }
842
+ $this->session->remove($this->id . $this->netMountKey);
843
+
844
+ return true;
845
+ }
846
+
847
+ /**
848
+ * Return fileinfo based on filename
849
+ * For item ID based path file system
850
+ * Please override if needed on each drivers.
851
+ *
852
+ * @param string $path file cache
853
+ *
854
+ * @return array
855
+ */
856
+ protected function isNameExists($path)
857
+ {
858
+ list($parentId, $name) = $this->_gd_splitPath($path);
859
+ $opts = [
860
+ 'q' => sprintf('trashed=false and "%s" in parents and name="%s"', $parentId, $name),
861
+ 'fields' => self::FETCHFIELDS_LIST,
862
+ ];
863
+ $srcFile = $this->_gd_query($opts);
864
+
865
+ return empty($srcFile) ? false : $this->_gd_parseRaw($srcFile[0]);
866
+ }
867
+
868
+ /*********************************************************************/
869
+ /* INIT AND CONFIGURE */
870
+ /*********************************************************************/
871
+
872
+ /**
873
+ * Prepare FTP connection
874
+ * Connect to remote server and check if credentials are correct, if so, store the connection id in $ftp_conn.
875
+ *
876
+ * @return bool
877
+ * @author Dmitry (dio) Levashov
878
+ * @author Cem (DiscoFever)
879
+ **/
880
+ protected function init()
881
+ {
882
+ $serviceAccountConfig = '';
883
+ if (empty($this->options['serviceAccountConfigFile'])) {
884
+ if (empty($options['client_id'])) {
885
+ if (defined('ELFINDER_GOOGLEDRIVE_CLIENTID') && ELFINDER_GOOGLEDRIVE_CLIENTID) {
886
+ $this->options['client_id'] = ELFINDER_GOOGLEDRIVE_CLIENTID;
887
+ } else {
888
+ return $this->setError('Required option "client_id" is undefined.');
889
+ }
890
+ }
891
+ if (empty($options['client_secret'])) {
892
+ if (defined('ELFINDER_GOOGLEDRIVE_CLIENTSECRET') && ELFINDER_GOOGLEDRIVE_CLIENTSECRET) {
893
+ $this->options['client_secret'] = ELFINDER_GOOGLEDRIVE_CLIENTSECRET;
894
+ } else {
895
+ return $this->setError('Required option "client_secret" is undefined.');
896
+ }
897
+ }
898
+ if (!$this->options['access_token'] && !$this->options['refresh_token']) {
899
+ return $this->setError('Required option "access_token" or "refresh_token" is undefined.');
900
+ }
901
+ } else {
902
+ if (!is_readable($this->options['serviceAccountConfigFile'])) {
903
+ return $this->setError('Option "serviceAccountConfigFile" file is not readable.');
904
+ }
905
+ $serviceAccountConfig = $this->options['serviceAccountConfigFile'];
906
+ }
907
+
908
+ try {
909
+ if (!$serviceAccountConfig) {
910
+ $aTokenFile = '';
911
+ if ($this->options['refresh_token']) {
912
+ // permanent mount
913
+ $aToken = $this->options['refresh_token'];
914
+ $this->options['access_token'] = '';
915
+ $tmp = elFinder::getStaticVar('commonTempPath');
916
+ if (!$tmp) {
917
+ $tmp = $this->getTempPath();
918
+ }
919
+ if ($tmp) {
920
+ $aTokenFile = $tmp . DIRECTORY_SEPARATOR . md5($this->options['client_id'] . $this->options['refresh_token']) . '.gtoken';
921
+ if (is_file($aTokenFile)) {
922
+ $this->options['access_token'] = json_decode(file_get_contents($aTokenFile), true);
923
+ }
924
+ }
925
+ } else {
926
+ // make net mount key for network mount
927
+ if (is_array($this->options['access_token'])) {
928
+ $aToken = !empty($this->options['access_token']['refresh_token'])
929
+ ? $this->options['access_token']['refresh_token']
930
+ : $this->options['access_token']['access_token'];
931
+ } else {
932
+ return $this->setError('Required option "access_token" is not Array or empty.');
933
+ }
934
+ }
935
+ }
936
+
937
+ $errors = [];
938
+ if ($this->needOnline && !$this->service) {
939
+ if (($this->options['googleApiClient'] || defined('ELFINDER_GOOGLEDRIVE_GOOGLEAPICLIENT')) && !class_exists('Google_Client')) {
940
+ include_once $this->options['googleApiClient'] ? $this->options['googleApiClient'] : ELFINDER_GOOGLEDRIVE_GOOGLEAPICLIENT;
941
+ }
942
+ if (!class_exists('Google_Client')) {
943
+ return $this->setError('Class Google_Client not found.');
944
+ }
945
+
946
+ $this->client = new \Google_Client();
947
+
948
+ $client = $this->client;
949
+
950
+ if (!$serviceAccountConfig) {
951
+ if ($this->options['access_token']) {
952
+ $client->setAccessToken($this->options['access_token']);
953
+ $access_token = $this->options['access_token'];
954
+ }
955
+ if ($client->isAccessTokenExpired()) {
956
+ $client->setClientId($this->options['client_id']);
957
+ $client->setClientSecret($this->options['client_secret']);
958
+ $access_token = $client->fetchAccessTokenWithRefreshToken($this->options['refresh_token'] ?: null);
959
+ $client->setAccessToken($access_token);
960
+ if ($aTokenFile) {
961
+ file_put_contents($aTokenFile, json_encode($access_token));
962
+ } else {
963
+ $access_token['refresh_token'] = $this->options['access_token']['refresh_token'];
964
+ }
965
+ if (!empty($this->options['netkey'])) {
966
+ elFinder::$instance->updateNetVolumeOption($this->options['netkey'], 'access_token', $access_token);
967
+ }
968
+ $this->options['access_token'] = $access_token;
969
+ }
970
+ $this->expires = empty($access_token['refresh_token']) ? $access_token['created'] + $access_token['expires_in'] - 30 : 0;
971
+ } else {
972
+ $client->setAuthConfigFile($serviceAccountConfig);
973
+ $client->setScopes([Google_Service_Drive::DRIVE]);
974
+ $aToken = $client->getClientId();
975
+ }
976
+ $this->service = new \Google_Service_Drive($client);
977
+ }
978
+
979
+ if ($this->needOnline) {
980
+ $this->netMountKey = md5($aToken . '-' . $this->options['path']);
981
+ }
982
+ } catch (InvalidArgumentException $e) {
983
+ $errors[] = $e->getMessage();
984
+ } catch (Google_Service_Exception $e) {
985
+ $errors[] = $e->getMessage();
986
+ }
987
+
988
+ if ($this->needOnline && !$this->service) {
989
+ $this->session->remove($this->id . $this->netMountKey);
990
+ if ($aTokenFile) {
991
+ if (is_file($aTokenFile)) {
992
+ unlink($aTokenFile);
993
+ }
994
+ }
995
+ $errors[] = 'Google Drive Service could not be loaded.';
996
+
997
+ return $this->setError($errors);
998
+ }
999
+
1000
+ // normalize root path
1001
+ if ($this->options['path'] == 'root') {
1002
+ $this->options['path'] = '/';
1003
+ }
1004
+ $this->root = $this->options['path'] = $this->_normpath($this->options['path']);
1005
+
1006
+ if (empty($this->options['alias'])) {
1007
+ if ($this->needOnline) {
1008
+ $this->options['root'] = ($this->options['root'] === '')? $this->_gd_getNameByPath('root') : $this->options['root'];
1009
+ $this->options['alias'] = ($this->options['path'] === '/') ? $this->options['root'] : sprintf($this->options['gdAlias'], $this->_gd_getNameByPath($this->options['path']));
1010
+ if (!empty($this->options['netkey'])) {
1011
+ elFinder::$instance->updateNetVolumeOption($this->options['netkey'], 'alias', $this->options['alias']);
1012
+ }
1013
+ } else {
1014
+ $this->options['root'] = ($this->options['root'] === '')? 'GoogleDrive' : $this->options['root'];
1015
+ $this->options['alias'] = $this->options['root'];
1016
+ }
1017
+ }
1018
+
1019
+ $this->rootName = isset($this->options['alias'])? $this->options['alias'] : 'GoogleDrive';
1020
+
1021
+ if (!empty($this->options['tmpPath'])) {
1022
+ if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'])) && is_writable($this->options['tmpPath'])) {
1023
+ $this->tmp = $this->options['tmpPath'];
1024
+ }
1025
+ }
1026
+
1027
+ if (!$this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) {
1028
+ $this->tmp = $tmp;
1029
+ }
1030
+
1031
+ // This driver dose not support `syncChkAsTs`
1032
+ $this->options['syncChkAsTs'] = false;
1033
+
1034
+ // 'lsPlSleep' minmum 10 sec
1035
+ $this->options['lsPlSleep'] = max(10, $this->options['lsPlSleep']);
1036
+
1037
+ if ($this->options['useGoogleTmb']) {
1038
+ $this->options['tmbURL'] = 'https://';
1039
+ $this->options['tmbPath'] = '';
1040
+ }
1041
+
1042
+ // enable command archive
1043
+ $this->options['useRemoteArchive'] = true;
1044
+
1045
+ return true;
1046
+ }
1047
+
1048
+ /**
1049
+ * Configure after successfull mount.
1050
+ *
1051
+ * @author Dmitry (dio) Levashov
1052
+ **/
1053
+ protected function configure()
1054
+ {
1055
+ parent::configure();
1056
+
1057
+ // fallback of $this->tmp
1058
+ if (!$this->tmp && $this->tmbPathWritable) {
1059
+ $this->tmp = $this->tmbPath;
1060
+ }
1061
+
1062
+ if ($this->needOnline && $this->isMyReload()) {
1063
+ $this->_gd_getDirectoryData(false);
1064
+ }
1065
+ }
1066
+
1067
+ /*********************************************************************/
1068
+ /* FS API */
1069
+ /*********************************************************************/
1070
+
1071
+ /**
1072
+ * Close opened connection.
1073
+ *
1074
+ * @author Dmitry (dio) Levashov
1075
+ **/
1076
+ public function umount()
1077
+ {
1078
+ }
1079
+
1080
+ /**
1081
+ * Cache dir contents.
1082
+ *
1083
+ * @param string $path dir path
1084
+ *
1085
+ * @return array
1086
+ * @author Dmitry Levashov
1087
+ */
1088
+ protected function cacheDir($path)
1089
+ {
1090
+ $this->dirsCache[$path] = [];
1091
+ $hasDir = false;
1092
+
1093
+ list(, $pid) = $this->_gd_splitPath($path);
1094
+
1095
+ $opts = [
1096
+ 'fields' => self::FETCHFIELDS_LIST,
1097
+ 'q' => sprintf('trashed=false and "%s" in parents', $pid),
1098
+ ];
1099
+
1100
+ $res = $this->_gd_query($opts);
1101
+
1102
+ $mountPath = $this->_normpath($path . '/');
1103
+
1104
+ if ($res) {
1105
+ foreach ($res as $raw) {
1106
+ if ($stat = $this->_gd_parseRaw($raw)) {
1107
+ $stat = $this->updateCache($mountPath . $raw->id, $stat);
1108
+ if (empty($stat['hidden']) && $path !== $mountPath . $raw->id) {
1109
+ if (!$hasDir && $stat['mime'] === 'directory') {
1110
+ $hasDir = true;
1111
+ }
1112
+ $this->dirsCache[$path][] = $mountPath . $raw->id;
1113
+ }
1114
+ }
1115
+ }
1116
+ }
1117
+
1118
+ if (isset($this->sessionCache['subdirs'])) {
1119
+ $this->sessionCache['subdirs'][$path] = $hasDir;
1120
+ }
1121
+
1122
+ return $this->dirsCache[$path];
1123
+ }
1124
+
1125
+ /**
1126
+ * Recursive files search.
1127
+ *
1128
+ * @param string $path dir path
1129
+ * @param string $q search string
1130
+ * @param array $mimes
1131
+ *
1132
+ * @return array
1133
+ * @throws elFinderAbortException
1134
+ * @author Naoki Sawada
1135
+ */
1136
+ protected function doSearch($path, $q, $mimes)
1137
+ {
1138
+ if (!empty($this->doSearchCurrentQuery['matchMethod'])) {
1139
+ // has custom match method use elFinderVolumeDriver::doSearch()
1140
+ return parent::doSearch($path, $q, $mimes);
1141
+ }
1142
+
1143
+ list(, $itemId) = $this->_gd_splitPath($path);
1144
+
1145
+ $path = $this->_normpath($path . '/');
1146
+ $result = [];
1147
+ $query = '';
1148
+
1149
+ if ($itemId !== 'root') {
1150
+ $dirs = array_merge([$itemId], $this->_gd_getDirectories($itemId));
1151
+ $query = '(\'' . implode('\' in parents or \'', $dirs) . '\' in parents)';
1152
+ }
1153
+
1154
+ $tmp = [];
1155
+ if (!$mimes) {
1156
+ foreach (explode(' ', $q) as $_v) {
1157
+ $tmp[] = 'fullText contains \'' . str_replace('\'', '\\\'', $_v) . '\'';
1158
+ }
1159
+ $query .= ($query ? ' and ' : '') . implode(' and ', $tmp);
1160
+ } else {
1161
+ foreach ($mimes as $_v) {
1162
+ $tmp[] = 'mimeType contains \'' . str_replace('\'', '\\\'', $_v) . '\'';
1163
+ }
1164
+ $query .= ($query ? ' and ' : '') . '(' . implode(' or ', $tmp) . ')';
1165
+ }
1166
+
1167
+ $opts = [
1168
+ 'q' => sprintf('trashed=false and (%s)', $query),
1169
+ ];
1170
+
1171
+ $res = $this->_gd_query($opts);
1172
+
1173
+ $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0;
1174
+ foreach ($res as $raw) {
1175
+ if ($timeout && $timeout < time()) {
1176
+ $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->_path($path));
1177
+ break;
1178
+ }
1179
+ if ($stat = $this->_gd_parseRaw($raw)) {
1180
+ if ($parents = $raw->getParents()) {
1181
+ foreach ($parents as $parent) {
1182
+ $paths = $this->_gd_getMountPaths($parent);
1183
+ foreach ($paths as $path) {
1184
+ $path = ($path === '') ? '/' : (rtrim($path, '/') . '/');
1185
+ if (!isset($this->cache[$path . $raw->id])) {
1186
+ $stat = $this->updateCache($path . $raw->id, $stat);
1187
+ } else {
1188
+ $stat = $this->cache[$path . $raw->id];
1189
+ }
1190
+ if (empty($stat['hidden'])) {
1191
+ $stat['path'] = $this->_path($path) . $stat['name'];
1192
+ $result[] = $stat;
1193
+ }
1194
+ }
1195
+ }
1196
+ }
1197
+ }
1198
+ }
1199
+
1200
+ return $result;
1201
+ }
1202
+
1203
+ /**
1204
+ * Copy file/recursive copy dir only in current volume.
1205
+ * Return new file path or false.
1206
+ *
1207
+ * @param string $src source path
1208
+ * @param string $dst destination dir path
1209
+ * @param string $name new file name (optionaly)
1210
+ *
1211
+ * @return string|false
1212
+ * @author Dmitry (dio) Levashov
1213
+ * @author Naoki Sawada
1214
+ **/
1215
+ protected function copy($src, $dst, $name)
1216
+ {
1217
+ $this->clearcache();
1218
+ $res = $this->_gd_getFile($src);
1219
+ if ($res['mimeType'] == self::DIRMIME) {
1220
+ $newDir = $this->_mkdir($dst, $name);
1221
+ if ($newDir) {
1222
+ list(, $itemId) = $this->_gd_splitPath($newDir);
1223
+ list(, $srcId) = $this->_gd_splitPath($src);
1224
+ $path = $this->_joinPath($dst, $itemId);
1225
+ $opts = [
1226
+ 'q' => sprintf('trashed=false and "%s" in parents', $srcId),
1227
+ ];
1228
+
1229
+ $res = $this->_gd_query($opts);
1230
+ foreach ($res as $raw) {
1231
+ $raw['mimeType'] == self::DIRMIME ? $this->copy($src . '/' . $raw['id'], $path, $raw['name']) : $this->_copy($src . '/' . $raw['id'], $path, $raw['name']);
1232
+ }
1233
+
1234
+ $ret = $this->_joinPath($dst, $itemId);
1235
+ $this->added[] = $this->stat($ret);
1236
+ } else {
1237
+ $ret = $this->setError(elFinder::ERROR_COPY, $this->_path($src));
1238
+ }
1239
+ } else {
1240
+ if ($itemId = $this->_copy($src, $dst, $name)) {
1241
+ $ret = $this->_joinPath($dst, $itemId);
1242
+ $this->added[] = $this->stat($ret);
1243
+ } else {
1244
+ $ret = $this->setError(elFinder::ERROR_COPY, $this->_path($src));
1245
+ }
1246
+ }
1247
+ return $ret;
1248
+ }
1249
+
1250
+ /**
1251
+ * Remove file/ recursive remove dir.
1252
+ *
1253
+ * @param string $path file path
1254
+ * @param bool $force try to remove even if file locked
1255
+ * @param bool $recursive
1256
+ *
1257
+ * @return bool
1258
+ * @throws elFinderAbortException
1259
+ * @author Dmitry (dio) Levashov
1260
+ * @author Naoki Sawada
1261
+ */
1262
+ protected function remove($path, $force = false, $recursive = false)
1263
+ {
1264
+ $stat = $this->stat($path);
1265
+ $stat['realpath'] = $path;
1266
+ $this->rmTmb($stat);
1267
+ $this->clearcache();
1268
+
1269
+ if (empty($stat)) {
1270
+ return $this->setError(elFinder::ERROR_RM, $this->_path($path), elFinder::ERROR_FILE_NOT_FOUND);
1271
+ }
1272
+
1273
+ if (!$force && !empty($stat['locked'])) {
1274
+ return $this->setError(elFinder::ERROR_LOCKED, $this->_path($path));
1275
+ }
1276
+
1277
+ if ($stat['mime'] == 'directory') {
1278
+ if (!$recursive && !$this->_rmdir($path)) {
1279
+ return $this->setError(elFinder::ERROR_RM, $this->_path($path));
1280
+ }
1281
+ } else {
1282
+ if (!$recursive && !$this->_unlink($path)) {
1283
+ return $this->setError(elFinder::ERROR_RM, $this->_path($path));
1284
+ }
1285
+ }
1286
+
1287
+ $this->removed[] = $stat;
1288
+
1289
+ return true;
1290
+ }
1291
+
1292
+ /**
1293
+ * Create thumnbnail and return it's URL on success.
1294
+ *
1295
+ * @param string $path file path
1296
+ * @param $stat
1297
+ *
1298
+ * @return string|false
1299
+ * @throws ImagickException
1300
+ * @throws elFinderAbortException
1301
+ * @author Dmitry (dio) Levashov
1302
+ * @author Naoki Sawada
1303
+ */
1304
+ protected function createTmb($path, $stat)
1305
+ {
1306
+ if (!$stat || !$this->canCreateTmb($path, $stat)) {
1307
+ return false;
1308
+ }
1309
+
1310
+ $name = $this->tmbname($stat);
1311
+ $tmb = $this->tmbPath . DIRECTORY_SEPARATOR . $name;
1312
+
1313
+ // copy image into tmbPath so some drivers does not store files on local fs
1314
+ if (!$data = $this->_gd_getThumbnail($path)) {
1315
+ return false;
1316
+ }
1317
+ if (!file_put_contents($tmb, $data)) {
1318
+ return false;
1319
+ }
1320
+
1321
+ $result = false;
1322
+
1323
+ $tmbSize = $this->tmbSize;
1324
+
1325
+ if (($s = getimagesize($tmb)) == false) {
1326
+ return false;
1327
+ }
1328
+
1329
+ /* If image smaller or equal thumbnail size - just fitting to thumbnail square */
1330
+ if ($s[0] <= $tmbSize && $s[1] <= $tmbSize) {
1331
+ $result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png');
1332
+ } else {
1333
+ if ($this->options['tmbCrop']) {
1334
+
1335
+ /* Resize and crop if image bigger than thumbnail */
1336
+ if (!(($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize)) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) {
1337
+ $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png');
1338
+ }
1339
+
1340
+ if (($s = getimagesize($tmb)) != false) {
1341
+ $x = $s[0] > $tmbSize ? intval(($s[0] - $tmbSize) / 2) : 0;
1342
+ $y = $s[1] > $tmbSize ? intval(($s[1] - $tmbSize) / 2) : 0;
1343
+ $result = $this->imgCrop($tmb, $tmbSize, $tmbSize, $x, $y, 'png');
1344
+ }
1345
+ } else {
1346
+ $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, true, 'png');
1347
+ }
1348
+
1349
+ $result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png');
1350
+ }
1351
+
1352
+ if (!$result) {
1353
+ unlink($tmb);
1354
+
1355
+ return false;
1356
+ }
1357
+
1358
+ return $name;
1359
+ }
1360
+
1361
+ /**
1362
+ * Return thumbnail file name for required file.
1363
+ *
1364
+ * @param array $stat file stat
1365
+ *
1366
+ * @return string
1367
+ * @author Dmitry (dio) Levashov
1368
+ **/
1369
+ protected function tmbname($stat)
1370
+ {
1371
+ return $this->netMountKey . $stat['iid'] . $stat['ts'] . '.png';
1372
+ }
1373
+
1374
+ /**
1375
+ * Return content URL (for netmout volume driver)
1376
+ * If file.url == 1 requests from JavaScript client with XHR.
1377
+ *
1378
+ * @param string $hash file hash
1379
+ * @param array $options options array
1380
+ *
1381
+ * @return bool|string
1382
+ * @author Naoki Sawada
1383
+ */
1384
+ public function getContentUrl($hash, $options = [])
1385
+ {
1386
+ if (!empty($options['onetime']) && $this->options['onetimeUrl']) {
1387
+ return parent::getContentUrl($hash, $options);
1388
+ }
1389
+ if (!empty($options['temporary'])) {
1390
+ // try make temporary file
1391
+ $url = parent::getContentUrl($hash, $options);
1392
+ if ($url) {
1393
+ return $url;
1394
+ }
1395
+ }
1396
+ if (($file = $this->file($hash)) == false || !$file['url'] || $file['url'] == 1) {
1397
+ $path = $this->decode($hash);
1398
+
1399
+ if ($this->_gd_publish($path)) {
1400
+ if ($raw = $this->_gd_getFile($path)) {
1401
+ return $this->_gd_getLink($raw);
1402
+ }
1403
+ }
1404
+ }
1405
+
1406
+ return false;
1407
+ }
1408
+
1409
+ /**
1410
+ * Return debug info for client.
1411
+ *
1412
+ * @return array
1413
+ **/
1414
+ public function debug()
1415
+ {
1416
+ $res = parent::debug();
1417
+ if (!empty($this->options['netkey']) && empty($this->options['refresh_token']) && $this->options['access_token'] && isset($this->options['access_token']['refresh_token'])) {
1418
+ $res['refresh_token'] = $this->options['access_token']['refresh_token'];
1419
+ }
1420
+
1421
+ return $res;
1422
+ }
1423
+
1424
+ /*********************** paths/urls *************************/
1425
+
1426
+ /**
1427
+ * Return parent directory path.
1428
+ *
1429
+ * @param string $path file path
1430
+ *
1431
+ * @return string
1432
+ * @author Dmitry (dio) Levashov
1433
+ **/
1434
+ protected function _dirname($path)
1435
+ {
1436
+ list(, , $parent) = $this->_gd_splitPath($path);
1437
+
1438
+ return $this->_normpath($parent);
1439
+ }
1440
+
1441
+ /**
1442
+ * Return file name.
1443
+ *
1444
+ * @param string $path file path
1445
+ *
1446
+ * @return string
1447
+ * @author Dmitry (dio) Levashov
1448
+ **/
1449
+ protected function _basename($path)
1450
+ {
1451
+ list(, $basename) = $this->_gd_splitPath($path);
1452
+
1453
+ return $basename;
1454
+ }
1455
+
1456
+ /**
1457
+ * Join dir name and file name and retur full path.
1458
+ *
1459
+ * @param string $dir
1460
+ * @param string $name
1461
+ *
1462
+ * @return string
1463
+ * @author Dmitry (dio) Levashov
1464
+ **/
1465
+ protected function _joinPath($dir, $name)
1466
+ {
1467
+ return $this->_normpath($dir . '/' . str_replace('/', '\\/', $name));
1468
+ }
1469
+
1470
+ /**
1471
+ * Return normalized path, this works the same as os.path.normpath() in Python.
1472
+ *
1473
+ * @param string $path path
1474
+ *
1475
+ * @return string
1476
+ * @author Troex Nevelin
1477
+ **/
1478
+ protected function _normpath($path)
1479
+ {
1480
+ if (DIRECTORY_SEPARATOR !== '/') {
1481
+ $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
1482
+ }
1483
+ $path = '/' . ltrim($path, '/');
1484
+
1485
+ return $path;
1486
+ }
1487
+
1488
+ /**
1489
+ * Return file path related to root dir.
1490
+ *
1491
+ * @param string $path file path
1492
+ *
1493
+ * @return string
1494
+ * @author Dmitry (dio) Levashov
1495
+ **/
1496
+ protected function _relpath($path)
1497
+ {
1498
+ return $path;
1499
+ }
1500
+
1501
+ /**
1502
+ * Convert path related to root dir into real path.
1503
+ *
1504
+ * @param string $path file path
1505
+ *
1506
+ * @return string
1507
+ * @author Dmitry (dio) Levashov
1508
+ **/
1509
+ protected function _abspath($path)
1510
+ {
1511
+ return $path;
1512
+ }
1513
+
1514
+ /**
1515
+ * Return fake path started from root dir.
1516
+ *
1517
+ * @param string $path file path
1518
+ *
1519
+ * @return string
1520
+ * @author Dmitry (dio) Levashov
1521
+ **/
1522
+ protected function _path($path)
1523
+ {
1524
+ if (!$this->names) {
1525
+ $this->_gd_getDirectoryData();
1526
+ }
1527
+ $path = $this->_normpath(substr($path, strlen($this->root)));
1528
+ $names = [];
1529
+ $paths = explode('/', $path);
1530
+ foreach ($paths as $_p) {
1531
+ $names[] = isset($this->names[$_p]) ? $this->names[$_p] : $_p;
1532
+ }
1533
+
1534
+ return $this->rootName . implode('/', $names);
1535
+ }
1536
+
1537
+ /**
1538
+ * Return true if $path is children of $parent.
1539
+ *
1540
+ * @param string $path path to check
1541
+ * @param string $parent parent path
1542
+ *
1543
+ * @return bool
1544
+ * @author Dmitry (dio) Levashov
1545
+ **/
1546
+ protected function _inpath($path, $parent)
1547
+ {
1548
+ return $path == $parent || strpos($path, $parent . '/') === 0;
1549
+ }
1550
+
1551
+ /***************** file stat ********************/
1552
+ /**
1553
+ * Return stat for given path.
1554
+ * Stat contains following fields:
1555
+ * - (int) size file size in b. required
1556
+ * - (int) ts file modification time in unix time. required
1557
+ * - (string) mime mimetype. required for folders, others - optionally
1558
+ * - (bool) read read permissions. required
1559
+ * - (bool) write write permissions. required
1560
+ * - (bool) locked is object locked. optionally
1561
+ * - (bool) hidden is object hidden. optionally
1562
+ * - (string) alias for symlinks - link target path relative to root path. optionally
1563
+ * - (string) target for symlinks - link target path. optionally.
1564
+ * If file does not exists - returns empty array or false.
1565
+ *
1566
+ * @param string $path file path
1567
+ *
1568
+ * @return array|false
1569
+ * @author Dmitry (dio) Levashov
1570
+ **/
1571
+ protected function _stat($path)
1572
+ {
1573
+ if ($raw = $this->_gd_getFile($path)) {
1574
+ $stat = $this->_gd_parseRaw($raw);
1575
+ if ($path === $this->root) {
1576
+ $stat['expires'] = $this->expires;
1577
+ }
1578
+ return $stat;
1579
+ }
1580
+
1581
+ return false;
1582
+ }
1583
+
1584
+ /**
1585
+ * Return true if path is dir and has at least one childs directory.
1586
+ *
1587
+ * @param string $path dir path
1588
+ *
1589
+ * @return bool
1590
+ * @author Dmitry (dio) Levashov
1591
+ **/
1592
+ protected function _subdirs($path)
1593
+ {
1594
+ if ($this->directories === null) {
1595
+ $this->_gd_getDirectoryData();
1596
+ }
1597
+ list(, $itemId) = $this->_gd_splitPath($path);
1598
+
1599
+ return isset($this->directories[$itemId]);
1600
+ }
1601
+
1602
+ /**
1603
+ * Return object width and height
1604
+ * Ususaly used for images, but can be realize for video etc...
1605
+ *
1606
+ * @param string $path file path
1607
+ * @param string $mime file mime type
1608
+ *
1609
+ * @return string
1610
+ * @throws ImagickException
1611
+ * @throws elFinderAbortException
1612
+ * @author Dmitry (dio) Levashov
1613
+ */
1614
+ protected function _dimensions($path, $mime)
1615
+ {
1616
+ if (strpos($mime, 'image') !== 0) {
1617
+ return '';
1618
+ }
1619
+ $ret = '';
1620
+
1621
+ if ($file = $this->_gd_getFile($path)) {
1622
+ if (isset($file['imageMediaMetadata'])) {
1623
+ $ret = array('dim' => $file['imageMediaMetadata']['width'] . 'x' . $file['imageMediaMetadata']['height']);
1624
+ if (func_num_args() > 2) {
1625
+ $args = func_get_arg(2);
1626
+ } else {
1627
+ $args = array();
1628
+ }
1629
+ if (!empty($args['substitute'])) {
1630
+ $tmbSize = intval($args['substitute']);
1631
+ $srcSize = explode('x', $ret['dim']);
1632
+ if ($srcSize[0] && $srcSize[1]) {
1633
+ if (min(($tmbSize / $srcSize[0]), ($tmbSize / $srcSize[1])) < 1) {
1634
+ if ($this->_gd_isPublished($file)) {
1635
+ $tmbSize = strval($tmbSize);
1636
+ $ret['url'] = 'https://drive.google.com/thumbnail?authuser=0&sz=s' . $tmbSize . '&id=' . $file['id'];
1637
+ } elseif ($subImgLink = $this->getSubstituteImgLink(elFinder::$currentArgs['target'], $srcSize)) {
1638
+ $ret['url'] = $subImgLink;
1639
+ }
1640
+ }
1641
+ }
1642
+ }
1643
+ }
1644
+ }
1645
+
1646
+ return $ret;
1647
+ }
1648
+
1649
+ /******************** file/dir content *********************/
1650
+
1651
+ /**
1652
+ * Return files list in directory.
1653
+ *
1654
+ * @param string $path dir path
1655
+ *
1656
+ * @return array
1657
+ * @author Dmitry (dio) Levashov
1658
+ * @author Cem (DiscoFever)
1659
+ **/
1660
+ protected function _scandir($path)
1661
+ {
1662
+ return isset($this->dirsCache[$path])
1663
+ ? $this->dirsCache[$path]
1664
+ : $this->cacheDir($path);
1665
+ }
1666
+
1667
+ /**
1668
+ * Open file and return file pointer.
1669
+ *
1670
+ * @param string $path file path
1671
+ * @param bool $write open file for writing
1672
+ *
1673
+ * @return resource|false
1674
+ * @author Dmitry (dio) Levashov
1675
+ **/
1676
+ protected function _fopen($path, $mode = 'rb')
1677
+ {
1678
+ if ($mode === 'rb' || $mode === 'r') {
1679
+ if ($file = $this->_gd_getFile($path)) {
1680
+ if ($dlurl = $this->_gd_getDownloadUrl($file)) {
1681
+ $token = $this->client->getAccessToken();
1682
+ if (!$token && $this->client->isUsingApplicationDefaultCredentials()) {
1683
+ $this->client->fetchAccessTokenWithAssertion();
1684
+ $token = $this->client->getAccessToken();
1685
+ }
1686
+ $access_token = '';
1687
+ if (is_array($token)) {
1688
+ $access_token = $token['access_token'];
1689
+ } else {
1690
+ if ($token = json_decode($this->client->getAccessToken())) {
1691
+ $access_token = $token->access_token;
1692
+ }
1693
+ }
1694
+ if ($access_token) {
1695
+ $data = array(
1696
+ 'target' => $dlurl,
1697
+ 'headers' => array('Authorization: Bearer ' . $access_token),
1698
+ );
1699
+
1700
+ // to support range request
1701
+ if (func_num_args() > 2) {
1702
+ $opts = func_get_arg(2);
1703
+ } else {
1704
+ $opts = array();
1705
+ }
1706
+ if (!empty($opts['httpheaders'])) {
1707
+ $data['headers'] = array_merge($opts['httpheaders'], $data['headers']);
1708
+ }
1709
+
1710
+ return elFinder::getStreamByUrl($data);
1711
+ }
1712
+ }
1713
+ }
1714
+ }
1715
+
1716
+ return false;
1717
+ }
1718
+
1719
+ /**
1720
+ * Close opened file.
1721
+ *
1722
+ * @param resource $fp file pointer
1723
+ *
1724
+ * @return bool
1725
+ * @author Dmitry (dio) Levashov
1726
+ **/
1727
+ protected function _fclose($fp, $path = '')
1728
+ {
1729
+ is_resource($fp) && fclose($fp);
1730
+ if ($path) {
1731
+ unlink($this->getTempFile($path));
1732
+ }
1733
+ }
1734
+
1735
+ /******************** file/dir manipulations *************************/
1736
+
1737
+ /**
1738
+ * Create dir and return created dir path or false on failed.
1739
+ *
1740
+ * @param string $path parent dir path
1741
+ * @param string $name new directory name
1742
+ *
1743
+ * @return string|bool
1744
+ * @author Dmitry (dio) Levashov
1745
+ **/
1746
+ protected function _mkdir($path, $name)
1747
+ {
1748
+ $path = $this->_joinPath($path, $name);
1749
+ list($parentId, , $parent) = $this->_gd_splitPath($path);
1750
+
1751
+ try {
1752
+ $file = new \Google_Service_Drive_DriveFile();
1753
+
1754
+ $file->setName($name);
1755
+ $file->setMimeType(self::DIRMIME);
1756
+ $file->setParents([$parentId]);
1757
+
1758
+ //create the Folder in the Parent
1759
+ $obj = $this->service->files->create($file);
1760
+
1761
+ if ($obj instanceof Google_Service_Drive_DriveFile) {
1762
+ $path = $this->_joinPath($parent, $obj['id']);
1763
+ $this->_gd_getDirectoryData(false);
1764
+
1765
+ return $path;
1766
+ } else {
1767
+ return false;
1768
+ }
1769
+ } catch (Exception $e) {
1770
+ return $this->setError('GoogleDrive error: ' . $e->getMessage());
1771
+ }
1772
+ }
1773
+
1774
+ /**
1775
+ * Create file and return it's path or false on failed.
1776
+ *
1777
+ * @param string $path parent dir path
1778
+ * @param string $name new file name
1779
+ *
1780
+ * @return string|bool
1781
+ * @author Dmitry (dio) Levashov
1782
+ **/
1783
+ protected function _mkfile($path, $name)
1784
+ {
1785
+ return $this->_save($this->tmpfile(), $path, $name, []);
1786
+ }
1787
+
1788
+ /**
1789
+ * Create symlink. FTP driver does not support symlinks.
1790
+ *
1791
+ * @param string $target link target
1792
+ * @param string $path symlink path
1793
+ *
1794
+ * @return bool
1795
+ * @author Dmitry (dio) Levashov
1796
+ **/
1797
+ protected function _symlink($target, $path, $name)
1798
+ {
1799
+ return false;
1800
+ }
1801
+
1802
+ /**
1803
+ * Copy file into another file.
1804
+ *
1805
+ * @param string $source source file path
1806
+ * @param string $targetDir target directory path
1807
+ * @param string $name new file name
1808
+ *
1809
+ * @return bool
1810
+ * @author Dmitry (dio) Levashov
1811
+ **/
1812
+ protected function _copy($source, $targetDir, $name)
1813
+ {
1814
+ $source = $this->_normpath($source);
1815
+ $targetDir = $this->_normpath($targetDir);
1816
+
1817
+ try {
1818
+ $file = new \Google_Service_Drive_DriveFile();
1819
+ $file->setName($name);
1820
+
1821
+ //Set the Parent id
1822
+ list(, $parentId) = $this->_gd_splitPath($targetDir);
1823
+ $file->setParents([$parentId]);
1824
+
1825
+ list(, $srcId) = $this->_gd_splitPath($source);
1826
+ $file = $this->service->files->copy($srcId, $file, ['fields' => self::FETCHFIELDS_GET]);
1827
+ $itemId = $file->id;
1828
+
1829
+ return $itemId;
1830
+ } catch (Exception $e) {
1831
+ return $this->setError('GoogleDrive error: ' . $e->getMessage());
1832
+ }
1833
+
1834
+ return true;
1835
+ }
1836
+
1837
+ /**
1838
+ * Move file into another parent dir.
1839
+ * Return new file path or false.
1840
+ *
1841
+ * @param string $source source file path
1842
+ * @param string $target target dir path
1843
+ * @param string $name file name
1844
+ *
1845
+ * @return string|bool
1846
+ * @author Dmitry (dio) Levashov
1847
+ **/
1848
+ protected function _move($source, $targetDir, $name)
1849
+ {
1850
+ list($removeParents, $itemId) = $this->_gd_splitPath($source);
1851
+ $target = $this->_normpath($targetDir . '/' . $itemId);
1852
+ try {
1853
+ //moving and renaming a file or directory
1854
+ $files = new \Google_Service_Drive_DriveFile();
1855
+ $files->setName($name);
1856
+
1857
+ //Set new Parent and remove old parent
1858
+ list(, $addParents) = $this->_gd_splitPath($targetDir);
1859
+ $opts = ['addParents' => $addParents, 'removeParents' => $removeParents];
1860
+
1861
+ $file = $this->service->files->update($itemId, $files, $opts);
1862
+
1863
+ if ($file->getMimeType() === self::DIRMIME) {
1864
+ $this->_gd_getDirectoryData(false);
1865
+ }
1866
+ } catch (Exception $e) {
1867
+ return $this->setError('GoogleDrive error: ' . $e->getMessage());
1868
+ }
1869
+
1870
+ return $target;
1871
+ }
1872
+
1873
+ /**
1874
+ * Remove file.
1875
+ *
1876
+ * @param string $path file path
1877
+ *
1878
+ * @return bool
1879
+ * @author Dmitry (dio) Levashov
1880
+ **/
1881
+ protected function _unlink($path)
1882
+ {
1883
+ try {
1884
+ $files = new \Google_Service_Drive_DriveFile();
1885
+ $files->setTrashed(true);
1886
+
1887
+ list($pid, $itemId) = $this->_gd_splitPath($path);
1888
+ $opts = ['removeParents' => $pid];
1889
+ $this->service->files->update($itemId, $files, $opts);
1890
+ } catch (Exception $e) {
1891
+ return $this->setError('GoogleDrive error: ' . $e->getMessage());
1892
+ }
1893
+
1894
+ return true;
1895
+ }
1896
+
1897
+ /**
1898
+ * Remove dir.
1899
+ *
1900
+ * @param string $path dir path
1901
+ *
1902
+ * @return bool
1903
+ * @author Dmitry (dio) Levashov
1904
+ **/
1905
+ protected function _rmdir($path)
1906
+ {
1907
+ $res = $this->_unlink($path);
1908
+ $res && $this->_gd_getDirectoryData(false);
1909
+
1910
+ return $res;
1911
+ }
1912
+
1913
+ /**
1914
+ * Create new file and write into it from file pointer.
1915
+ * Return new file path or false on error.
1916
+ *
1917
+ * @param resource $fp file pointer
1918
+ * @param $path
1919
+ * @param string $name file name
1920
+ * @param array $stat file stat (required by some virtual fs)
1921
+ *
1922
+ * @return bool|string
1923
+ * @author Dmitry (dio) Levashov
1924
+ */
1925
+ protected function _save($fp, $path, $name, $stat)
1926
+ {
1927
+ if ($name !== '') {
1928
+ $path .= '/' . str_replace('/', '\\/', $name);
1929
+ }
1930
+ list($parentId, $itemId, $parent) = $this->_gd_splitPath($path);
1931
+ if ($name === '') {
1932
+ $stat['iid'] = $itemId;
1933
+ }
1934
+
1935
+ if (!$stat || empty($stat['iid'])) {
1936
+ $opts = [
1937
+ 'q' => sprintf('trashed=false and "%s" in parents and name="%s"', $parentId, $name),
1938
+ 'fields' => self::FETCHFIELDS_LIST,
1939
+ ];
1940
+ $srcFile = $this->_gd_query($opts);
1941
+ $srcFile = empty($srcFile) ? null : $srcFile[0];
1942
+ } else {
1943
+ $srcFile = $this->_gd_getFile($path);
1944
+ }
1945
+
1946
+ try {
1947
+ $mode = 'update';
1948
+ $mime = isset($stat['mime']) ? $stat['mime'] : '';
1949
+
1950
+ $file = new Google_Service_Drive_DriveFile();
1951
+ if ($srcFile) {
1952
+ $mime = $srcFile->getMimeType();
1953
+ } else {
1954
+ $mode = 'insert';
1955
+ $file->setName($name);
1956
+ $file->setParents([
1957
+ $parentId,
1958
+ ]);
1959
+ }
1960
+
1961
+ if (!$mime) {
1962
+ $mime = self::mimetypeInternalDetect($name);
1963
+ }
1964
+ if ($mime === 'unknown') {
1965
+ $mime = 'application/octet-stream';
1966
+ }
1967
+ $file->setMimeType($mime);
1968
+
1969
+ $size = 0;
1970
+ if (isset($stat['size'])) {
1971
+ $size = $stat['size'];
1972
+ } else {
1973
+ $fstat = fstat($fp);
1974
+ if (!empty($fstat['size'])) {
1975
+ $size = $fstat['size'];
1976
+ }
1977
+ }
1978
+
1979
+ // set chunk size (max: 100MB)
1980
+ $chunkSizeBytes = 100 * 1024 * 1024;
1981
+ if ($size > 0) {
1982
+ $memory = elFinder::getIniBytes('memory_limit');
1983
+ if ($memory > 0) {
1984
+ $chunkSizeBytes = max(262144, min([$chunkSizeBytes, (intval($memory / 4 / 256) * 256)]));
1985
+ }
1986
+ }
1987
+
1988
+ if ($size > $chunkSizeBytes) {
1989
+ $client = $this->client;
1990
+ // Call the API with the media upload, defer so it doesn't immediately return.
1991
+ $client->setDefer(true);
1992
+ if ($mode === 'insert') {
1993
+ $request = $this->service->files->create($file, [
1994
+ 'fields' => self::FETCHFIELDS_GET,
1995
+ ]);
1996
+ } else {
1997
+ $request = $this->service->files->update($srcFile->getId(), $file, [
1998
+ 'fields' => self::FETCHFIELDS_GET,
1999
+ ]);
2000
+ }
2001
+
2002
+ // Create a media file upload to represent our upload process.
2003
+ $media = new Google_Http_MediaFileUpload($client, $request, $mime, null, true, $chunkSizeBytes);
2004
+ $media->setFileSize($size);
2005
+ // Upload the various chunks. $status will be false until the process is
2006
+ // complete.
2007
+ $status = false;
2008
+ while (!$status && !feof($fp)) {
2009
+ elFinder::checkAborted();
2010
+ // read until you get $chunkSizeBytes from TESTFILE
2011
+ // fread will never return more than 8192 bytes if the stream is read buffered and it does not represent a plain file
2012
+ // An example of a read buffered file is when reading from a URL
2013
+ $chunk = $this->_gd_readFileChunk($fp, $chunkSizeBytes);
2014
+ $status = $media->nextChunk($chunk);
2015
+ }
2016
+ // The final value of $status will be the data from the API for the object
2017
+ // that has been uploaded.
2018
+ if ($status !== false) {
2019
+ $obj = $status;
2020
+ }
2021
+
2022
+ $client->setDefer(false);
2023
+ } else {
2024
+ $params = [
2025
+ 'data' => stream_get_contents($fp),
2026
+ 'uploadType' => 'media',
2027
+ 'fields' => self::FETCHFIELDS_GET,
2028
+ ];
2029
+ if ($mode === 'insert') {
2030
+ $obj = $this->service->files->create($file, $params);
2031
+ } else {
2032
+ $obj = $this->service->files->update($srcFile->getId(), $file, $params);
2033
+ }
2034
+ }
2035
+ if ($obj instanceof Google_Service_Drive_DriveFile) {
2036
+ return $this->_joinPath($parent, $obj->getId());
2037
+ } else {
2038
+ return false;
2039
+ }
2040
+ } catch (Exception $e) {
2041
+ return $this->setError('GoogleDrive error: ' . $e->getMessage());
2042
+ }
2043
+ }
2044
+
2045
+ /**
2046
+ * Get file contents.
2047
+ *
2048
+ * @param string $path file path
2049
+ *
2050
+ * @return string|false
2051
+ * @author Dmitry (dio) Levashov
2052
+ **/
2053
+ protected function _getContents($path)
2054
+ {
2055
+ $contents = '';
2056
+
2057
+ try {
2058
+ list(, $itemId) = $this->_gd_splitPath($path);
2059
+
2060
+ $contents = $this->service->files->get($itemId, [
2061
+ 'alt' => 'media',
2062
+ ]);
2063
+ $contents = (string)$contents->getBody();
2064
+ } catch (Exception $e) {
2065
+ return $this->setError('GoogleDrive error: ' . $e->getMessage());
2066
+ }
2067
+
2068
+ return $contents;
2069
+ }
2070
+
2071
+ /**
2072
+ * Write a string to a file.
2073
+ *
2074
+ * @param string $path file path
2075
+ * @param string $content new file content
2076
+ *
2077
+ * @return bool
2078
+ * @author Dmitry (dio) Levashov
2079
+ **/
2080
+ protected function _filePutContents($path, $content)
2081
+ {
2082
+ $res = false;
2083
+
2084
+ if ($local = $this->getTempFile($path)) {
2085
+ if (file_put_contents($local, $content, LOCK_EX) !== false
2086
+ && ($fp = fopen($local, 'rb'))) {
2087
+ clearstatcache();
2088
+ $res = $this->_save($fp, $path, '', []);
2089
+ fclose($fp);
2090
+ }
2091
+ file_exists($local) && unlink($local);
2092
+ }
2093
+
2094
+ return $res;
2095
+ }
2096
+
2097
+ /**
2098
+ * Detect available archivers.
2099
+ **/
2100
+ protected function _checkArchivers()
2101
+ {
2102
+ // die('Not yet implemented. (_checkArchivers)');
2103
+ return [];
2104
+ }
2105
+
2106
+ /**
2107
+ * chmod implementation.
2108
+ *
2109
+ * @return bool
2110
+ **/
2111
+ protected function _chmod($path, $mode)
2112
+ {
2113
+ return false;
2114
+ }
2115
+
2116
+ /**
2117
+ * Unpack archive.
2118
+ *
2119
+ * @param string $path archive path
2120
+ * @param array $arc archiver command and arguments (same as in $this->archivers)
2121
+ *
2122
+ * @return void
2123
+ * @author Dmitry (dio) Levashov
2124
+ * @author Alexey Sukhotin
2125
+ */
2126
+ protected function _unpack($path, $arc)
2127
+ {
2128
+ die('Not yet implemented. (_unpack)');
2129
+ //return false;
2130
+ }
2131
+
2132
+ /**
2133
+ * Extract files from archive.
2134
+ *
2135
+ * @param string $path archive path
2136
+ * @param array $arc archiver command and arguments (same as in $this->archivers)
2137
+ *
2138
+ * @return void
2139
+ * @author Dmitry (dio) Levashov,
2140
+ * @author Alexey Sukhotin
2141
+ */
2142
+ protected function _extract($path, $arc)
2143
+ {
2144
+ die('Not yet implemented. (_extract)');
2145
+ }
2146
+
2147
+ /**
2148
+ * Create archive and return its path.
2149
+ *
2150
+ * @param string $dir target dir
2151
+ * @param array $files files names list
2152
+ * @param string $name archive name
2153
+ * @param array $arc archiver options
2154
+ *
2155
+ * @return string|bool
2156
+ * @author Dmitry (dio) Levashov,
2157
+ * @author Alexey Sukhotin
2158
+ **/
2159
+ protected function _archive($dir, $files, $name, $arc)
2160
+ {
2161
+ die('Not yet implemented. (_archive)');
2162
+ }
2163
+ } // END class