Replace deprecated $a->page with DI::page()
[friendica-addons.git/.git] / js_upload / js_upload.php
1 <?php
2 /**
3  * Name: JS Uploader
4  * Description: JavaScript photo/image uploader. Uses Valum 'qq' Uploader.
5  * Version: 1.1
6  * Author: Chris Case <http://friendika.openmindspace.org/profile/chris_case>
7  * Maintainer: Hypolite Petovan <https://friendica.mrpetovan.com/profile/hypolite>
8  */
9
10 use Friendica\App;
11 use Friendica\Core\Config;
12 use Friendica\Core\Hook;
13 use Friendica\Core\L10n;
14 use Friendica\Core\Logger;
15 use Friendica\Core\Renderer;
16 use Friendica\DI;
17
18 function js_upload_install()
19 {
20         Hook::register('photo_upload_form', __FILE__, 'js_upload_form');
21         Hook::register('photo_post_init', __FILE__, 'js_upload_post_init');
22         Hook::register('photo_post_file', __FILE__, 'js_upload_post_file');
23         Hook::register('photo_post_end', __FILE__, 'js_upload_post_end');
24 }
25
26 function js_upload_form(App $a, array &$b)
27 {
28         $b['default_upload'] = false;
29
30         DI::page()->registerStylesheet('addon/js_upload/file-uploader/client/fileuploader.css');
31         DI::page()->registerFooterScript('addon/js_upload/file-uploader/client/fileuploader.js');
32
33         $tpl = Renderer::getMarkupTemplate('js_upload.tpl', 'addon/js_upload');
34         $b['addon_text'] .= Renderer::replaceMacros($tpl, [
35                 '$upload_msg' => L10n::t('Select files for upload'),
36                 '$drop_msg' => L10n::t('Drop files here to upload'),
37                 '$cancel' => L10n::t('Cancel'),
38                 '$failed' => L10n::t('Failed'),
39                 '$post_url' => $b['post_url'],
40                 '$maximagesize' => intval(Config::get('system', 'maximagesize')),
41         ]);
42 }
43
44 function js_upload_post_init(App $a, &$b)
45 {
46         // list of valid extensions
47         $allowedExtensions = ['jpeg', 'gif', 'png', 'jpg'];
48
49         // max file size in bytes
50         $sizeLimit = Config::get('system', 'maximagesize');
51
52         $uploader = new qqFileUploader($allowedExtensions, $sizeLimit);
53
54         $result = $uploader->handleUpload();
55
56         // to pass data through iframe you will need to encode all html tags
57         $a->data['upload_jsonresponse'] = htmlspecialchars(json_encode($result), ENT_NOQUOTES);
58
59         if (isset($result['error'])) {
60                 Logger::log('mod/photos.php: photos_post(): error uploading photo: ' . $result['error'], Logger::DEBUG);
61                 echo json_encode($result);
62                 exit();
63         }
64
65         $a->data['upload_result'] = $result;
66 }
67
68 function js_upload_post_file(App $a, &$b)
69 {
70         $result = $a->data['upload_result'];
71
72         $b['src'] = $result['path'];
73         $b['filename'] = $result['filename'];
74         $b['filesize'] = filesize($b['src']);
75
76 }
77
78 function js_upload_post_end(App $a, &$b)
79 {
80         Logger::log('upload_post_end');
81         if (!empty($a->data['upload_jsonresponse'])) {
82                 echo $a->data['upload_jsonresponse'];
83                 exit();
84         }
85 }
86
87 /**
88  * Handle file uploads via XMLHttpRequest
89  */
90 class qqUploadedFileXhr
91 {
92         private $pathnm = '';
93
94         /**
95          * Save the file in the temp dir.
96          *
97          * @return boolean TRUE on success
98          */
99         function save()
100         {
101                 $input = fopen('php://input', 'r');
102
103                 $upload_dir = Config::get('system', 'tempdir');
104                 if (!$upload_dir)
105                         $upload_dir = sys_get_temp_dir();
106
107                 $this->pathnm = tempnam($upload_dir, 'frn');
108
109                 $temp = fopen($this->pathnm, 'w');
110                 $realSize = stream_copy_to_stream($input, $temp);
111
112                 fclose($input);
113                 fclose($temp);
114
115                 if ($realSize != $this->getSize()) {
116                         return false;
117                 }
118                 return true;
119         }
120
121         function getPath()
122         {
123                 return $this->pathnm;
124         }
125
126         function getName()
127         {
128                 return $_GET['qqfile'];
129         }
130
131         function getSize()
132         {
133                 if (isset($_SERVER['CONTENT_LENGTH'])) {
134                         return (int)$_SERVER['CONTENT_LENGTH'];
135                 } else {
136                         throw new Exception('Getting content length is not supported.');
137                 }
138         }
139 }
140
141 /**
142  * Handle file uploads via regular form post (uses the $_FILES array)
143  */
144 class qqUploadedFileForm
145 {
146         /**
147          * Save the file to the specified path
148          *
149          * @return boolean TRUE on success
150          */
151         function save()
152         {
153                 return true;
154         }
155
156         function getPath()
157         {
158                 return $_FILES['qqfile']['tmp_name'];
159         }
160
161         function getName()
162         {
163                 return $_FILES['qqfile']['name'];
164         }
165
166         function getSize()
167         {
168                 return $_FILES['qqfile']['size'];
169         }
170 }
171
172 class qqFileUploader
173 {
174         private $allowedExtensions = [];
175         private $sizeLimit = 10485760;
176         private $file;
177
178         function __construct(array $allowedExtensions = [], $sizeLimit = 10485760)
179         {
180                 $allowedExtensions = array_map('strtolower', $allowedExtensions);
181
182                 $this->allowedExtensions = $allowedExtensions;
183                 $this->sizeLimit = $sizeLimit;
184
185                 if (isset($_GET['qqfile'])) {
186                         $this->file = new qqUploadedFileXhr();
187                 } elseif (isset($_FILES['qqfile'])) {
188                         $this->file = new qqUploadedFileForm();
189                 } else {
190                         $this->file = false;
191                 }
192
193         }
194
195         private function toBytes($str)
196         {
197                 $val = trim($str);
198                 $last = strtolower($str[strlen($str) - 1]);
199                 switch ($last) {
200                         case 'g':
201                                 $val *= 1024;
202                         case 'm':
203                                 $val *= 1024;
204                         case 'k':
205                                 $val *= 1024;
206                 }
207                 return $val;
208         }
209
210         /**
211          * Returns array('success'=>true) or array('error'=>'error message')
212          */
213         function handleUpload()
214         {
215                 if (!$this->file) {
216                         return ['error' => L10n::t('No files were uploaded.')];
217                 }
218
219                 $size = $this->file->getSize();
220
221                 if ($size == 0) {
222                         return ['error' => L10n::t('Uploaded file is empty')];
223                 }
224
225 //              if ($size > $this->sizeLimit) {
226
227 //                      return array('error' => L10n::t('Uploaded file is too large'));
228 //              }
229
230
231                 $maximagesize = Config::get('system', 'maximagesize');
232
233                 if (($maximagesize) && ($size > $maximagesize)) {
234                         return ['error' => L10n::t('Image exceeds size limit of ') . $maximagesize];
235
236                 }
237
238                 $pathinfo = pathinfo($this->file->getName());
239                 $filename = $pathinfo['filename'];
240
241                 if (!isset($pathinfo['extension'])) {
242                         Logger::warning('extension isn\'t set.', ['filename' => $filename]);
243                 }
244                 $ext = $pathinfo['extension'] ?? '';
245
246                 if ($this->allowedExtensions && !in_array(strtolower($ext), $this->allowedExtensions)) {
247                         $these = implode(', ', $this->allowedExtensions);
248                         return ['error' => L10n::t('File has an invalid extension, it should be one of ') . $these . '.'];
249                 }
250
251                 if ($this->file->save()) {
252                         return [
253                                 'success' => true,
254                                 'path' => $this->file->getPath(),
255                                 'filename' => $filename . '.' . $ext
256                         ];
257                 } else {
258                         return [
259                                 'error' => L10n::t('Upload was cancelled, or server error encountered'),
260                                 'path' => $this->file->getPath(),
261                                 'filename' => $filename . '.' . $ext
262                         ];
263                 }
264         }
265 }