drg_elfinder 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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