1<?php
2/*********************************************************************
3    class.attachment.php
4
5    Attachment Handler - mainly used for lookup...doesn't save!
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**********************************************************************/
16require_once(INCLUDE_DIR.'class.ticket.php');
17require_once(INCLUDE_DIR.'class.file.php');
18
19class Attachment extends VerySimpleModel {
20    static $meta = array(
21        'table' => ATTACHMENT_TABLE,
22        'pk' => array('id'),
23        'select_related' => array('file'),
24        'joins' => array(
25            'draft' => array(
26                'constraint' => array(
27                    'type' => "'D'",
28                    'object_id' => 'Draft.id',
29                ),
30            ),
31            'file' => array(
32                'constraint' => array(
33                    'file_id' => 'AttachmentFile.id',
34                ),
35            ),
36            'thread_entry' => array(
37                'constraint' => array(
38                    'type' => "'H'",
39                    'object_id' => 'ThreadEntry.id',
40                ),
41            ),
42        ),
43    );
44
45    var $object;
46
47    function getId() {
48        return $this->id;
49    }
50
51    function getFileId() {
52        return $this->file_id;
53    }
54
55    function getFile() {
56        return $this->file;
57    }
58
59    function getFilename() {
60        return $this->name ?: $this->file->name;
61    }
62
63    function getName() {
64        return $this->getFilename();
65    }
66
67    function getHashtable() {
68        return $this->ht;
69    }
70
71    function getInfo() {
72        return $this->getHashtable();
73    }
74
75    function getObject() {
76
77        if (!isset($this->object))
78            $this->object = ObjectModel::lookup(
79                    $this->ht['object_id'], $this->ht['type']);
80
81        return $this->object;
82    }
83
84    static function lookupByFileHash($hash, $objectId=0) {
85        $file = static::objects()
86            ->filter(array('file__key' => $hash));
87
88        if ($objectId)
89            $file->filter(array('object_id' => $objectId));
90
91        return $file->first();
92    }
93
94    static function lookup($var, $objectId=0) {
95        return (is_string($var))
96            ? static::lookupByFileHash($var, $objectId)
97            : parent::lookup($var);
98    }
99}
100
101class GenericAttachments
102extends InstrumentedList {
103
104    var $lang;
105
106    function getId() { return $this->key['object_id']; }
107    function getType() { return $this->key['type']; }
108    function getMimeType() { return $this->getType(); }
109    /**
110     * Drop attachments whose file_id values are not in the included list,
111     * additionally, add new files whose IDs are in the list provided.
112     */
113    function keepOnlyFileIds($ids, $inline=false, $lang=false) {
114        if (!$ids) $ids = array();
115        foreach ($this as $A) {
116            if (!isset($ids[$A->file_id]) && $A->lang == $lang && $A->inline == $inline)
117                // Not in the $ids list, delete
118                $this->remove($A);
119            unset($ids[$A->file_id]);
120        }
121        $attachments = array();
122        // Format $new for upload() with new name
123        foreach ($ids as $id=>$name) {
124            $attachments[] = array(
125                    'id' => $id,
126                    'name' => $name
127                );
128        }
129        // Everything remaining in $attachments is truly new
130        $this->upload($attachments, $inline, $lang);
131    }
132
133    function upload($files, $inline=false, $lang=false) {
134        $i=array();
135        if (!is_array($files))
136            $files = array($files);
137        foreach ($files as $file) {
138            if (is_numeric($file))
139                $fileId = $file;
140            elseif (is_array($file) && isset($file['id']) && $file['id'])
141                $fileId = $file['id'];
142            elseif (isset($file['tmp_name']) && ($F = AttachmentFile::upload($file)))
143                $fileId = $F->getId();
144            elseif ($F = AttachmentFile::create($file))
145                $fileId = $F->getId();
146            else
147                continue;
148
149            $_inline = isset($file['inline']) ? $file['inline'] : $inline;
150
151            // Check if Attachment exists
152            if ($F && $this->key)
153                $existing = Attachment::objects()->filter(array(
154                    'file__key' => $F->key,
155                    'object_id' => $this->key['object_id'],
156                    'type' => $this->key['type']
157                ))->first();
158
159            $att = $this->add(isset($existing) ? $existing : new Attachment(array(
160                'file_id' => $fileId,
161                'inline' => $_inline ? 1 : 0,
162            )));
163
164            // Record varying file names in the attachment record
165            if (is_array($file) && isset($file['name'])) {
166                $filename = $file['name'];
167            }
168            if ($filename) {
169                // This should be a noop since the ORM caches on PK
170                $file = $F ?: AttachmentFile::lookup($fileId);
171                // XXX: This is not Unicode safe
172                if ($file && 0 !== strcasecmp($file->name, $filename))
173                    $att->name = $filename;
174            }
175            if ($lang)
176                $att->lang = $lang;
177
178            // File may already be associated with the draft (in the
179            // event it was deleted and re-added)
180            $att->save();
181            $i[] = $fileId;
182        }
183        return $i;
184    }
185
186    function save($file, $inline=true) {
187        $ids = $this->upload($file, $inline);
188        return $ids[0];
189    }
190
191    function getInlines($lang=false) { return $this->_getList(false, true, $lang); }
192    function getSeparates($lang=false) { return $this->_getList(true, false, $lang); }
193    function getAll($lang=false) { return $this->_getList(true, true, $lang); }
194    function count($lang=false) { return count($this->getSeparates($lang)); }
195
196    function _getList($separates=false, $inlines=false, $lang=false) {
197        $base = $this;
198
199        if ($separates && !$inlines)
200            $base = $base->filter(array('inline' => 0));
201        elseif (!$separates && $inlines)
202            $base = $base->filter(array('inline' => 1));
203
204        if ($lang)
205            $base = $base->filter(array('lang' => $lang));
206
207        return $base;
208    }
209
210    function delete($file_id) {
211        return $this->objects()->filter(array('file_id'=>$file_id))->delete();
212    }
213
214    function deleteAll($inline_only=false){
215        if ($inline_only)
216            return $this->objects()->filter(array('inline' => 1))->delete();
217
218        return parent::expunge();
219    }
220
221    function deleteInlines() {
222        return $this->deleteAll(true);
223    }
224
225    static function forIdAndType($id, $type) {
226        return new static(array(
227            'Attachment',
228            array('object_id' => $id, 'type' => $type)
229        ));
230    }
231}
232?>
233