source: trunk/server/www/app/controllers/requests_controller.php @ 385

Last change on this file since 385 was 385, checked in by sander, 11 years ago

Fixed a bug with selecting the latest version of every application

File size: 13.5 KB
Line 
1<?php
2/**
3 * Officeshots.org - Test your office documents in different applications
4 * Copyright (C) 2009 Stichting Lone Wolves
5 * Written by Sander Marechal <s.marechal@jejik.com>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21/**
22 * The Requests controller
23 */
24class RequestsController extends AppController
25{
26        /** @var array The components this controller uses */
27        public $components = array('AuthCert', 'RequestHandler');
28       
29        /** @var array The helpers that will be available on the view */
30        public $helpers = array('Html', 'Form', 'Javascript', 'RequestModel', 'ValidatorModel');
31
32        /** @var array Add Request and Worker model */
33        public $uses = array('Request', 'Worker', 'Mimetype', 'User', 'Format');
34
35        /** @var array Set default sort order for paginate */
36        public $paginate = array(
37                'order' => array('Request.created' => 'desc')
38        );
39
40        /**
41         * Set the auth permissions for this controller
42         * @return void
43         */
44        public function beforeFilter()
45        {
46                parent::beforeFilter();
47                $this->AuthCert->allow('add', 'view', 'download');
48        }
49
50        /**
51         * List all your requests
52         * @return void
53         */
54        public function index()
55        {
56                $this->set('requests', $this->paginate('Request', array('Request.user_id' => $this->AuthCert->user('id'))));
57        }
58
59        /**
60         * Get a Request by the request ID and check access control
61         *
62         * @param string $id The request ID
63         * @param $type the access type, either 'read' or 'write'
64         * @return array An array containing the request
65         */
66        private function _getRequest($id, $type = 'read')
67        {
68                if (!$id) {
69                        $this->Session->setFlash(__('Invalid Request.', true));
70                        $this->redirect(array('action'=>'add'));
71                }
72               
73                $this->Request->contain(array(
74                        'User',
75                        'Mimetype',
76                        'Mimetype.Doctype',
77                        'Job',
78                        'Job.Format',
79                        'Job.Platform',
80                        'Job.Application',
81                        'Job.Result',
82                        'Job.Result.Mimetype',
83                        'Job.Result.Format',
84                        'Job.Result.Validator' => array('order' => 'Validator.name ASC'),
85                        'Gallery',
86                        'Validator' => array('order' => 'Validator.name ASC'),
87                ));
88
89                $request = $this->Request->read(null, $id);
90                if (empty($request) || !$this->Request->checkAccess($this->AuthCert->user('id'), $type, $id)) {
91                        $this->Session->setFlash(__('Invalid Request.', true));
92                        $this->redirect(array('action'=>'add'));
93                }
94
95                usort($request['Job'], array($this, '_cmpJobs'));
96                return $request;
97        }
98
99        /**
100         * usort() callback to properly sort Jobs
101         */
102        private function _cmpJobs($a, $b)
103        {
104                if (0 !== ($cmp = strcmp($a['Application']['name'], $b['Application']['name']))) {
105                        return $cmp;
106                }
107
108                if (0 !== ($cmp = strcmp($a['version'], $b['version']))) {
109                        return $cmp;
110                }
111
112                if (0 !== ($cmp = strcmp($a['Platform']['name'], $b['Platform']['name']))) {
113                        return $cmp;
114                }
115
116                $format_a = ($a['Result'] ? $a['Result']['Format']['name'] : ($a['Format'] ? $a['Format']['name'] : ''));
117                $format_b = ($b['Result'] ? $b['Result']['Format']['name'] : ($b['Format'] ? $b['Format']['name'] : ''));
118
119                if (0 !== ($cmp = strcmp($format_a, $format_b))) {
120                        return $cmp;
121                }
122
123                return strcmp($a['created'], $b['created']);
124        }
125
126        /**
127         * View a single request
128         *
129         * @param string $id The request ID
130         * @return void
131         */
132        public function view($id = null)
133        {
134                // Used to display the jobs associated with the Request
135                $this->helpers[] = 'JobModel';
136
137                $request = $this->_getRequest($id, 'read');
138                $writeAccess = $this->Request->checkAccess($this->AuthCert->user('id'), 'write', $id);
139
140                $this->set(compact('request', 'writeAccess'));
141        }
142
143        /**
144         * Edit a request description
145         *
146         * @param string $id The request ID
147         * @return void
148         */
149        public function edit($id = null)
150        {
151                $request = $this->_getRequest($id, 'write');
152
153                if (!empty($this->data)) {
154                        $this->data['Request']['id'] = $id;
155                        if (!$this->Request->save($this->data)) {
156                                $this->Session->setFlash(__('Unable to save the description', true));
157                        } else {
158                                $this->redirect(array('action' => 'view', $id));
159                        }
160                }
161
162                $this->set(array(
163                        'request' => $request,
164                ));
165        }
166
167        /**
168         * Add a new request
169         * @return void
170         */
171        public function add()
172        {
173                if (!empty($this->data)) {
174                        // Access control
175                        if (!Configure::read('Auth.allowAnonymous') && !$this->AuthCert->user()) {
176                                $this->Session->setFlash(__('You are not allowed to submit requests anonymously.', true));
177                                $this->redirect(array('action'=>'add'));
178                        }
179
180                        // Check the daily request limits
181                        if ($user = $this->AuthCert->user()) {
182                                $requestLimit = Configure::read('Request.limitRegistered');
183
184                                $user = $this->User->find(array('User.id' => $user['User']['id']));
185                                $limits = Set::extract('/Group/request_limit', $user);
186                                $limits[] = $requestLimit;
187                                $requestLimit = max($limits);
188
189                                $numRequests = $this->Request->find('count', array(
190                                        'conditions' => array(
191                                                'Request.user_id' => $user['User']['id'],
192                                                'Request.created >' => date('Y-m-d H:i:s', strtotime('-1 day'))
193                                        )
194                                ));
195
196                                if ($numRequests > $requestLimit) {
197                                        $this->Session->setFlash(sprintf(__('You have exceeded your quota of %d daily requests.', true), $requestLimit));
198                                        $this->redirect(array('action'=>'add'));
199                                }
200                        } else {
201                                $requestLimit = Configure::read('Request.limitAnonymous');
202                                $numRequests = $this->Request->find('count', array(
203                                        'conditions' => array(
204                                                'Request.ip_address' => inet_ptod($this->RequestHandler->getClientIP()),
205                                                'Request.created >' => date('Y-m-d H:i:s', strtotime('-1 day'))
206                                        )
207                                ));
208
209                                if ($numRequests > $requestLimit) {
210                                        $this->Session->setFlash(sprintf(__('You have exceeded your quota of %d daily requests.', true), $requestLimit));
211                                        $this->redirect(array('action'=>'add'));
212                                }
213                        }
214
215                        // Set some extra data
216                        $this->data['Request']['user_id']    = (string) $this->AuthCert->user('id');
217                        $this->data['Request']['ip_address'] = inet_ptod($this->RequestHandler->getClientIP());
218                        $this->data['Request']['expire']     = date('Y-m-d H:i:s', time() + Configure::read('Request.expire'));
219                        $this->data['Request']['state']      = Request::STATE_PREPROCESSOR_QUEUED;
220
221
222                        // Create the request
223                        $this->Request->create();
224                        if (!$this->Request->save($this->data)) {
225                                $this->Session->setFlash(__('The request could not be saved', true));
226                                $this->redirect(array('action'=>'add'));
227                        }
228
229                        // Set root and path based on the ID
230                        $this->Request->read();
231                        $this->Request->set(array(
232                                'root' => 'requests' . DS . $this->Request->id,
233                                'path' => 'requests' . DS . $this->Request->id . DS . 'source',
234                        ));
235                       
236                        // Add the file upload
237                        $errors = array();
238                        if (!$this->Request->addUpload($this->data, $errors)) {
239                                $this->Request->delete();
240                                $this->Session->setFlash(implode("<br />\n", $errors));
241                                $this->redirect(array('action'=>'add'));
242                        }
243
244                        // Add the jobs to the request
245                        $this->Request->read();
246                        if (isset($this->data['Request']['App'])) {
247                                $jobs = array();
248                                $format_id = $this->data['Request']['format_id'];
249                                foreach ($this->data['Request']['App'] as $app) {
250                                        list($platform_id, $doctype_code, $application_id, $version) = explode('_', $app);
251                                        $jobs[] = compact('platform_id', 'application_id', 'version', 'format_id');
252                                }
253
254                                $this->Request->addJobs($jobs);
255                        }
256
257                        // Add ODF Validators
258                        $this->Request->addValidators();
259
260                        // Queue the preprocessor
261                        if (!$this->Request->defer('run', 'Preprocessor')) {
262                                $this->Request->delete();
263                                $this->Session->setFlash(__('Failed to queue the request for the virus scanner. Please contact system administration.', true));
264                                $this->redirect(array('action'=>'add'));
265                        }
266
267                        // All done!
268                        $this->redirect(array('action' => 'view', 'id' => $this->Request->id));
269                }
270
271                $this->set('can_have_factories', $this->__permitted('factories', 'edit'));
272                $this->set('can_submit_requests', ($this->__permitted('factories', 'edit') || $this->AuthCert->user()));
273               
274                // TODO: The result of all the below actions should really be cached for better performance
275                $workers = $this->Worker->getActive();
276
277                // Give all the active workers a unique, short ID.
278                // We need this for checking and unchecking sets of applications
279                $count = 0;
280                foreach ($workers as &$worker) {
281                        $worker['short_id'] = 'c'.$count++;
282                }
283                unset($worker); // Don't leave it assigned or bugs will occur below
284
285                // Get all the other information we need to build the front page
286                $platforms = $this->Worker->Factory->Operatingsystem->Platform->find('all');
287                $doctypes = $this->Worker->Application->Doctype->find('all');
288                $formats = $this->Format->find('list');
289                $mimetypes = $this->Mimetype->find('all');
290
291                // Build a format map which consists of sets of active applications
292                $sets = array(
293                        'platform'    => array(),
294                        'extension'   => array(),
295                        'format'      => array(),
296                        'latest'      => array(),
297                        'development' => Set::extract('/Worker[development=1]/../short_id', $workers),
298                        'all'         => Set::extract('/short_id', $workers)
299                );
300
301                foreach ($platforms as $platform) {
302                        $sets['platform'][$platform['Platform']['id']] = Set::extract('/Platform[id=' . $platform['Platform']['id'] . ']/../short_id', $workers);
303                }
304                foreach ($mimetypes as $mimetype) {
305                        if ($mimetype['Mimetype']['doctype_id']) {
306                                $sets['extension'][$mimetype['Mimetype']['extension']] = Set::extract('/Doctype[id=' . $mimetype['Mimetype']['doctype_id'] . ']/../short_id', $workers);
307                        }
308                }
309                foreach ($formats as $format_id => $format) {
310                        $sets['format'][$format_id] = Set::extract('/Format[id=' . $format_id . ']/../short_id', $workers);
311                }
312
313                // This is the tricky bit. Set the highest version for every application/platform combo as default
314                // This requires the workers array to be sorted by platform, app, version
315                $short_id = false;
316                $platform_id = '';
317                $application_id = '';
318                $doctype_id = '';
319                foreach ($workers as $worker) {
320                        if ($worker['Worker']['development']) {
321                                continue;
322                        }
323
324                        if ($worker['Application']['id'] != $application_id || $worker['Platform']['id'] != $platform_id || $worker['Doctype']['id'] != $doctype_id) {
325                                if ($short_id) {
326                                        $sets['latest'][] = $short_id;
327                                }
328
329                                $platform_id = $worker['Platform']['id'];
330                                $application_id = $worker['Application']['id'];
331                                $doctype_id = $worker['Doctype']['id'];
332                        }
333
334                        $short_id = $worker['short_id'];
335                }
336                $sets['latest'][] = $short_id; // don't forget to add the last one.
337
338                $this->set(compact('workers', 'platforms', 'doctypes', 'formats', 'sets', 'mimetypes'));
339        }
340
341        /**
342         * Download a request
343         *
344         * @param string $id The request ID
345         */
346        public function download($id = null)
347        {
348                $request = $this->_getRequest($id, 'read');
349                extract($this->Request->createZip());
350               
351                $this->view = 'Media';
352                $this->set(array(
353                        'id' => $id,
354                        'name' => $filename,
355                        'download' => true,
356                        'extension' => 'zip',
357                        'path' => $directory . DS,
358                ));
359        }
360
361        /**
362         * Extend the deadline of a request
363         *
364         * @param string $id The request ID
365         */
366        public function extend($id = null)
367        {
368                $request = $this->_getRequest($id, 'write');
369
370                if (strtotime($request['Request']['expire']) < time()) {
371                        $this->Session->setFlash(__('You cannot extend a request that has expired.', true));
372                        $this->redirect(array('action' => 'view', $id));
373                }
374
375                $this->Request->saveField('expire', date('Y-m-d H:i:s', time() + Configure::read('Request.expire')));
376                $this->redirect(array('action' => 'view', $id));
377        }
378
379        /**
380         * Cancel a job by setting it's expiration time
381         *
382         * @param string $id The request ID
383         */
384        public function cancel($id = null)
385        {
386                $request = $this->_getRequest($id, 'write');
387                $this->Request->cancel();
388                $this->redirect(array('action' => 'view', $id));
389        }
390
391        /**
392         * List all requests
393         * @return void
394         */
395        public function admin_index()
396        {
397                $this->Request->recursive = 0;
398                $this->paginate = array('order' => 'Request.created');
399                $this->set('requests', $this->paginate());
400        }
401
402        /**
403         * View a single request
404         *
405         * @param string $id The request ID
406         * @return void
407         */
408        public function admin_view($id = null)
409        {
410                if (!$id) {
411                        $this->Session->setFlash(__('Invalid Request.', true));
412                        $this->redirect(array('action'=>'index'));
413                }
414
415                // Used to display the jobs associated with the Request
416                $this->helpers[] = 'JobModel';
417
418                $request = $this->_getRequest($id, 'admin');
419                $this->set(compact('request'));
420        }
421
422        /**
423         * Edit a request
424         *
425         * @param string $id The request ID
426         * @return void
427         */
428        public function admin_edit($id = null)
429        {
430                $request = $this->_getRequest($id, 'admin');
431
432                if (!empty($this->data)) {
433                        $this->data['Request']['id'] = $id;
434                        if (!$this->Request->save($this->data)) {
435                                $this->Session->setFlash(__('Unable to save the description', true));
436                        } else {
437                                $this->redirect(array('action' => 'view', $id));
438                        }
439                }
440
441                $this->set(array(
442                        'request' => $request,
443                ));
444                $this->render('edit');
445        }
446
447        /**
448         * Delete a request
449         *
450         * @param string $id The request ID
451         * @return void
452         */
453        public function admin_delete($id = null)
454        {
455                if (!$id) {
456                        $this->Session->setFlash(__('Invalid id for Request', true));
457                        $this->redirect(array('action'=>'index'));
458                }
459                if ($this->Request->del($id)) {
460                        $this->Session->setFlash(__('Request deleted', true));
461                        $this->redirect(array('action'=>'index'));
462                }
463        }
464}
465
466?>
Note: See TracBrowser for help on using the repository browser.