1<?php
2/*********************************************************************
3    class.canned.php
4
5    Canned Responses AKA Premade replies
6
7    Peter Rotich <peter@osticket.com>
8    Copyright (c)  2006-2013 osTicket
9    http://www.osticket.com
10
11    Released under the GNU General Public License WITHOUT ANY WARRANTY.
12    See LICENSE.TXT for details.
13
14    vim: expandtab sw=4 ts=4 sts=4:
15**********************************************************************/
16include_once(INCLUDE_DIR.'class.file.php');
17
18class Canned
19extends VerySimpleModel {
20    static $meta = array(
21        'table' => CANNED_TABLE,
22        'pk' => array('canned_id'),
23        'joins' => array(
24            'dept' => array(
25                'constraint' => array('dept_id' => 'Dept.id'),
26                'null' => true,
27            ),
28            'attachments' => array(
29                'constraint' => array(
30                    "'C'" => 'Attachment.type',
31                    'canned_id' => 'Attachment.object_id',
32                ),
33                'list' => true,
34                'null' => true,
35                'broker' => 'GenericAttachments',
36            ),
37        ),
38    );
39
40    const PERM_MANAGE = 'canned.manage';
41
42    static protected $perms = array(
43            self::PERM_MANAGE => array(
44                'title' =>
45                /* @trans */ 'Premade',
46                'desc'  =>
47                /* @trans */ 'Ability to add/update/disable/delete canned responses')
48    );
49
50    static function getPermissions() {
51        return self::$perms;
52    }
53
54    function getId(){
55        return $this->canned_id;
56    }
57
58    function isEnabled() {
59         return $this->isenabled;
60    }
61
62    function isActive(){
63        return $this->isEnabled();
64    }
65
66    function getFilters() {
67
68        if (!isset($this->_filters)) {
69            $this->_filters = array();
70            $cid = sprintf('"canned_id":%d', $this->getId());
71            $sql='SELECT filter.id, filter.name '
72                .' FROM '.FILTER_TABLE.' filter'
73                .' INNER JOIN '.FILTER_ACTION_TABLE.' action'
74                .'  ON (filter.id=action.filter_id)'
75                .' WHERE action.type="canned"'
76                ."  AND action.configuration LIKE '%$cid%'";
77
78            if (($res=db_query($sql)) && db_num_rows($res))
79                while (list($id, $name) = db_fetch_row($res))
80                    $this->_filters[$id] = $name;
81        }
82
83        return $this->_filters;
84    }
85
86    function getAttachedFiles($inlines=false) {
87        return AttachmentFile::objects()
88            ->filter(array(
89                'attachments__type'=>'C',
90                'attachments__object_id'=>$this->getId(),
91                'attachments__inline' => $inlines,
92            ));
93    }
94
95    function getNumFilters() {
96        return count($this->getFilters());
97    }
98
99    function getTitle() {
100        return $this->title;
101    }
102
103    function getResponse() {
104        return $this->response;
105    }
106    function getResponseWithImages() {
107        return Format::viewableImages($this->getResponse());
108    }
109
110    function getReply() {
111        return $this->getResponse();
112    }
113
114    function getHtml() {
115        return $this->getFormattedResponse('html');
116    }
117
118    function getPlainText() {
119        return $this->getFormattedResponse('text.plain');
120    }
121
122    function getFormattedResponse($format='text', $cb=null) {
123
124        $resp = array();
125        $html = true;
126        switch($format) {
127            case 'json.plain':
128                $html = false;
129                // fall-through
130            case 'json':
131                $resp['id'] = $this->getId();
132                $resp['title'] = $this->getTitle();
133                $resp['response'] = $this->getResponseWithImages();
134
135                // Callback to strip or replace variables!
136                if ($cb && is_callable($cb))
137                    $resp = $cb($resp);
138
139                $resp['files'] = array();
140                foreach ($this->getAttachedFiles(!$html) as $file) {
141                    $_SESSION[':cannedFiles'][$file->id] = $file->name;
142                    $resp['files'][] = array(
143                        'id' => $file->id,
144                        'name' => $file->name,
145                        'size' => $file->size,
146                        'download_url' => $file->getDownloadUrl(),
147                    );
148                }
149                // strip html
150                if (!$html) {
151                    $resp['response'] = Format::html2text($resp['response'], 90);
152                }
153
154                return Format::json_encode($resp);
155                break;
156            case 'html':
157            case 'text.html':
158                $response = $this->getResponseWithImages();
159                break;
160            case 'text.plain':
161                $html = false;
162            case 'text':
163            default:
164                $response = $this->getResponse();
165                if (!$html)
166                    $response = Format::html2text($response, 90);
167                break;
168        }
169
170        // Callback to strip or replace variables!
171        if ($response && $cb && is_callable($cb))
172            $response = $cb($response);
173
174        return $response;
175    }
176
177    function getNotes() {
178        return $this->ht['notes'];
179    }
180
181    function getDeptId(){
182        return $this->ht['dept_id'];
183    }
184
185    function getHashtable() {
186        $base = $this->ht;
187        unset($base['attachments']);
188        return $base;
189    }
190
191    function getInfo() {
192        return $this->getHashtable();
193    }
194
195    function getNumAttachments() {
196        return $this->attachments->count();
197    }
198
199    function delete(){
200        if ($this->getNumFilters() > 0)
201            return false;
202
203        if (!parent::delete())
204            return false;
205
206        $type = array('type' => 'deleted');
207        Signal::send('object.deleted', $this, $type);
208
209        $this->attachments->deleteAll();
210
211        return true;
212    }
213
214    /*** Static functions ***/
215
216    static function create($vars=false) {
217        $faq = new static($vars);
218        $faq->created = SqlFunction::NOW();
219        return $faq;
220    }
221
222    static function getIdByTitle($title) {
223        $row = static::objects()
224            ->filter(array('title' => $title))
225            ->values_flat('canned_id')
226            ->first();
227
228        return $row ? $row[0] : null;
229    }
230
231    static function getCannedResponses($deptId=0, $explicit=false) {
232        global $thisstaff;
233
234        $canned = static::objects()
235            ->filter(array('isenabled' => true))
236            ->order_by('title')
237            ->values_flat('canned_id', 'title');
238
239        if ($thisstaff) {
240            $staffDepts = array();
241
242            $staffDepts = $thisstaff->getDepts();
243            $staffDepts[] = 0;
244
245            $canned->filter(array('dept_id__in' => $staffDepts));
246        }
247
248        if ($deptId) {
249            $depts = array($deptId);
250            if (!$explicit)
251                $depts[] = 0;
252            $canned->filter(array('dept_id__in' => $depts));
253        }
254
255        $responses = array();
256        foreach ($canned as $row) {
257            list($id, $title) = $row;
258            $responses[$id] = $title;
259        }
260
261        return $responses;
262    }
263
264    function responsesByDeptId($deptId, $explicit=false) {
265        return self::getCannedResponses($deptId, $explicit);
266    }
267
268    function save($refetch=false) {
269        if ($this->dirty || $refetch)
270            $this->updated = SqlFunction::NOW();
271        return parent::save($refetch || $this->dirty);
272    }
273
274    function update($vars,&$errors) {
275        global $cfg;
276
277        $vars['title'] = Format::striptags(trim($vars['title']));
278
279        $id = isset($this->canned_id) ? $this->canned_id : null;
280        if ($id && $id != $vars['id'])
281            $errors['err']=sprintf('%s - %s', __('Internal error occurred'), __('Please try again!'));
282
283        if (!$vars['title'])
284            $errors['title'] = __('Title required');
285        elseif (strlen($vars['title']) < 3)
286            $errors['title'] = __('Title is too short. 3 chars minimum');
287        elseif (($cid=self::getIdByTitle($vars['title'])) && $cid!=$id)
288            $errors['title'] = __('Title already exists');
289
290        if (!$vars['response'])
291            $errors['response'] = __('Response text is required');
292
293        if ($errors)
294            return false;
295
296        $this->dept_id = $vars['dept_id'] ?: 0;
297        $this->isenabled = $vars['isenabled'];
298        $this->title = $vars['title'];
299        $this->response = Format::sanitize($vars['response']);
300        $this->notes = Format::sanitize($vars['notes']);
301
302        $isnew = !isset($id);
303        if ($this->save())
304            return true;
305
306        if ($isnew)
307            $errors['err'] = sprintf(__('Unable to update %s.'), __('this canned response'));
308        else
309            $errors['err']=sprintf(__('Unable to create %s.'), __('this canned response'))
310               .' '.__('Internal error occurred');
311
312        return true;
313    }
314}
315RolePermission::register( /* @trans */ 'Knowledgebase', Canned::getPermissions());
316
317?>
318