b90ef97fcb2f0ce05d8bc6913698041d4f9965e9
[tine20] / tine20 / Tinebase / FileSystem / RecordAttachments.php
1 <?php
2 /**
3  * Tine 2.0
4  *
5  * @package     Tinebase
6  * @subpackage  Filesystem
7  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
8  * @author      Philipp Schüle <p.schuele@metaways.de>
9  * @copyright   Copyright (c) 2013 Metaways Infosystems GmbH (http://www.metaways.de)
10  * 
11  */
12
13 /**
14  * filesystem attachments for records
15  *
16  * @package     Tinebase
17  * @subpackage  Filesystem
18  */
19 class Tinebase_FileSystem_RecordAttachments
20 {
21     /**
22      * filesystem controller
23      * 
24      * @var Tinebase_FileSystem
25      */
26     protected $_fsController = NULL;
27     
28     /**
29      * holds the instance of the singleton
30      *
31      * @var Tinebase_FileSystem_RecordAttachments
32      */
33     private static $_instance = NULL;
34     
35     /**
36      * the constructor
37      */
38     public function __construct() 
39     {
40         $this->_fsController  = Tinebase_FileSystem::getInstance();
41     }
42     
43     /**
44      * the singleton pattern
45      *
46      * @return Tinebase_FileSystem_RecordAttachments
47      */
48     public static function getInstance() 
49     {
50         if (self::$_instance === NULL) {
51             self::$_instance = new Tinebase_FileSystem_RecordAttachments();
52         }
53         
54         return self::$_instance;
55     }
56     
57     /**
58      * fetch all file attachments of a record
59      * 
60      * @param Tinebase_Record_Abstract $record
61      * @return Tinebase_Record_RecordSet of Tinebase_Model_Tree_Node
62      */
63     public function getRecordAttachments(Tinebase_Record_Abstract $record)
64     {
65         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ .
66             ' Fetching attachments of ' . get_class($record) . ' record with id ' . $record->getId() . ' ...');
67         
68         $parentPath = $this->getRecordAttachmentPath($record);
69         
70         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ .
71             ' Looking in path ' . $parentPath);
72         
73         try {
74             $record->attachments = $this->_fsController->scanDir($parentPath);
75         } catch (Tinebase_Exception_NotFound $tenf) {
76             $record->attachments = new Tinebase_Record_RecordSet('Tinebase_Model_Tree_Node');
77         }
78         
79         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG) && count($record->attachments) > 0) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ .
80             ' Found ' . count($record->attachments) . ' attachment(s).');
81         
82         return $record->attachments;
83     }
84     
85     /**
86      * fetches attachments for multiple records at once
87      * 
88      * @param Tinebase_Record_RecordSet $records
89      * 
90      * @todo maybe this should be improved
91      */
92     public function getMultipleAttachmentsOfRecords($records)
93     {
94         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ .
95             ' Fetching attachments for ' . count($records) . ' record(s)');
96         
97         $parentNodes = new Tinebase_Record_RecordSet('Tinebase_Model_Tree_Node');
98         $recordNodeMapping = array();
99         foreach ($records as $record) {
100             $parentPath = $this->getRecordAttachmentPath($record);
101             try {
102                 $node = $this->_fsController->stat($parentPath);
103                 $parentNodes->addRecord($node);
104                 $recordNodeMapping[$node->getId()] = $record->getId();
105             } catch (Tinebase_Exception_NotFound $tenf) {
106                 $record->attachments = new Tinebase_Record_RecordSet('Tinebase_Model_Tree_Node');
107             }
108         }
109         
110         $children = $this->_fsController->getTreeNodeChildren($parentNodes);
111         
112         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ .
113             ' Found ' . count($children) . ' attachment(s).');
114         
115         foreach ($children as $node) {
116             $record = $records->getById($recordNodeMapping[$node->parent_id]);
117             if (! isset($record->attachments)) {
118                 $record->attachments = new Tinebase_Record_RecordSet('Tinebase_Model_Tree_Node');
119             }
120             $record->attachments->addRecord($node);
121         }
122     }
123     
124     /**
125      * set file attachments of a record
126      * 
127      * @param Tinebase_Record_Abstract $record
128      */
129     public function setRecordAttachments(Tinebase_Record_Abstract $record)
130     {
131         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ .
132             ' Record: ' . print_r($record->toArray(), TRUE));
133
134         $currentAttachments = ($record->getId()) ? $this->getRecordAttachments(clone $record) : new Tinebase_Record_RecordSet('Tinebase_Model_Tree_Node');
135         $attachmentsToSet = ($record->attachments instanceof Tinebase_Record_RecordSet) 
136             ? $record->attachments
137             : new Tinebase_Record_RecordSet('Tinebase_Model_Tree_Node', (array)$record->attachments, TRUE);
138         
139         $attachmentDiff = $currentAttachments->diff($attachmentsToSet);
140         
141         foreach ($attachmentDiff->added as $added) {
142             try {
143                 $this->addRecordAttachment($record, $added->name, $added);
144             } catch (Tinebase_Exception_NotFound $tenf) {
145                 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ .
146                         ' Record: ' . print_r($record->toArray(), TRUE));
147                 if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ .
148                         ' Could not add new attachment to record: ' . $tenf);
149             }
150         }
151         
152         foreach ($attachmentDiff->removed as $removed) {
153             $this->_fsController->deleteFileNode($removed);
154         }
155
156         foreach ($attachmentDiff->modified as $modified) {
157             $this->_fsController->update($attachmentsToSet->getById($modified->getId()));
158         }
159     }
160     
161     /**
162      * add attachement to record
163      * 
164      * @param  Tinebase_Record_Abstract $record
165      * @param  string $name
166      * @param  mixed $attachment
167          @see Tinebase_FileSystem::copyTempfile
168      * @return Tinebase_Model_Tree_Node
169      */
170     public function addRecordAttachment(Tinebase_Record_Abstract $record, $name, $attachment)
171     {
172         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ .
173                 ' Creating new record attachment');
174         
175         // only occurs via unittests
176         if (!$name && isset($attachment->tempFile)) {
177             $attachment = Tinebase_TempFile::getInstance()->getTempFile($attachment->tempFile);
178             $name = $attachment->name;
179         }
180         
181         $attachmentsDir = $this->getRecordAttachmentPath($record, TRUE);
182         $attachmentPath = $attachmentsDir . '/' . $name;
183         if ($this->_fsController->fileExists($attachmentPath)) {
184             throw new Tinebase_Exception_InvalidArgument('file already exists');
185         }
186         
187         $this->_fsController->copyTempfile($attachment, $attachmentPath);
188         
189         $node = $this->_fsController->stat($attachmentPath);
190         return $node;
191     }
192     
193     
194     /**
195      * delete attachments of record
196      * 
197      * @param Tinebase_Record_Abstract $record
198      */
199     public function deleteRecordAttachments($record)
200     {
201         $attachments = ($record->attachments instanceof Tinebase_Record_RecordSet) ? $record->attachments : $this->getRecordAttachments($record);
202         foreach ($attachments as $node) {
203             $this->_fsController->deleteFileNode($node);
204         }
205     }
206     
207     /**
208      * get path for record attachments
209      * 
210      * @param Tinebase_Record_Abstract $record
211      * @param boolean $createDirIfNotExists
212      * @throws Tinebase_Exception_InvalidArgument
213      * @return string
214      */
215     public function getRecordAttachmentPath(Tinebase_Record_Abstract $record, $createDirIfNotExists = FALSE)
216     {
217         if (! $record->getId()) {
218             throw new Tinebase_Exception_InvalidArgument('record needs an identifier');
219         }
220         
221         $parentPath = $this->_fsController->getApplicationBasePath($record->getApplication(), Tinebase_FileSystem::FOLDER_TYPE_RECORDS);
222         $recordPath = $parentPath . '/' . get_class($record) . '/' . $record->getId();
223         if ($createDirIfNotExists && ! $this->_fsController->fileExists($recordPath)) {
224             $this->_fsController->mkdir($recordPath);
225         }
226         
227         return $recordPath;
228     }
229 }