GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Docman_ItemFactory   F
last analyzed

Complexity

Total Complexity 238

Size/Duplication

Total Lines 1389
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 25
Metric Value
wmc 238
lcom 1
cbo 25
dl 0
loc 1389
rs 0.5217

67 Methods

Rating   Name   Duplication   Size   Complexity  
A setInstance() 0 3 1
A clearInstance() 0 3 1
A setGroupId() 0 3 1
A getGroupId() 0 3 1
A getWikiPageReferencers() 0 16 4
A getChildrenFromParent() 0 19 4
A getItemTree() 0 3 1
A _getVersionFactory() 0 3 1
A _getUserManager() 0 3 1
A _getEventManager() 0 3 1
A massUpdate() 0 4 1
A isMoveable() 0 10 4
A isRoot() 0 4 1
A rawCreate() 0 4 1
A setCutPreference() 0 4 1
A setCopyPreference() 0 4 1
A delCutPreference() 0 3 1
A delCopyPreference() 0 3 1
A delCutPreferenceForAllUsers() 0 4 1
A delCopyPreferenceForAllUsers() 0 4 1
A listPendingItems() 0 4 1
A purgeDeletedItems() 0 12 4
A purgeDeletedItem() 0 4 1
A Docman_ItemFactory() 0 10 1
A instance() 0 6 2
C getItemFromRow() 0 60 11
C getItemTypeAsText() 0 24 7
C getItemTypeForItem() 0 26 7
A getIdInWikiOfWikiPageItem() 0 9 2
A deleteWikiPage() 0 5 1
A getWikiPage() 0 19 2
A getItemFromDb() 0 12 3
B getAllChildrenFromParent() 0 23 5
A _getExpandedUserPrefs() 0 17 3
A preloadItemPerms() 0 14 2
A isInSubTree() 0 12 3
A getParents() 0 10 2
C getItemSubTree() 0 56 13
F getItemSubTreeAsList() 0 130 27
A getItemList() 0 7 2
B getDocumentsIterator() 0 22 4
A searchPaginatedWithVersionByGroupId() 0 12 2
B findByTitle() 0 25 4
A findFuturObsoleteItems() 0 18 2
A _getItemDao() 0 6 2
A update() 0 12 3
A updateLink() 0 18 2
A create() 0 9 2
B isItemTheOnlyChildOfRoot() 0 17 6
A setNewParent() 0 5 3
B breathFirst() 0 18 6
C getItemTreeFromLeaves() 0 60 14
C connectOrphansToParents() 0 29 8
A getItemMapping() 0 5 1
A getRoot() 0 8 2
A createRoot() 0 7 1
B cloneItems() 0 31 4
A preferrencesExist() 0 5 1
B getCutPreference() 0 14 5
A getCopyPreference() 0 6 2
A getCurrentWikiVersion() 0 11 3
A getFolderStats() 0 8 3
D getFolderTreeStats() 0 38 10
A delete() 0 20 2
B deleteProjectTree() 0 20 5
B deleteSubTree() 0 20 6
C restore() 0 28 7

How to fix   Complexity   

Complex Class

Complex classes like Docman_ItemFactory often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Docman_ItemFactory, and based on these observations, apply Extract Interface, too.

1
<?php
2
/*
3
 * Copyright (c) Enalean, 2015. All Rights Reserved.
4
 * Copyright (c) STMicroelectronics, 2006. All Rights Reserved.
5
 *
6
 * Originally written by Manuel Vacelet, 2006
7
 * 
8
 * This file is a part of Tuleap.
9
 *
10
 * Tuleap is free software; you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation; either version 2 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * Tuleap is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with Tuleap. If not, see <http://www.gnu.org/licenses/>.
22
 */
23
require_once('common/dao/CodendiDataAccess.class.php');
24
require_once('common/reference/ReferenceManager.class.php');
25
26
require_once('DocmanConstants.class.php');
27
require_once('Docman_Item.class.php');
28
require_once('Docman_ItemDao.class.php');
29
require_once('Docman_Folder.class.php');
30
require_once('Docman_File.class.php');
31
require_once('Docman_Link.class.php');
32
require_once('Docman_EmbeddedFile.class.php');
33
require_once('Docman_Wiki.class.php');
34
require_once('Docman_Empty.class.php');
35
require_once('Docman_Version.class.php');
36
require_once('Docman_CloneItemsVisitor.class.php');
37
require_once('Docman_SubItemsRemovalVisitor.class.php');
38
require_once('Docman_PermissionsManager.class.php');
39
require_once('Docman_BuildItemMappingVisitor.class.php');
40
require_once('Docman_ActionsDeleteVisitor.class.php');
41
42
/**
43
 * 
44
 */
45
class Docman_ItemFactory {
46
    var $rootItems;
47
    var $onlyOneChildForRoot;
48
    var $copiedItem;
49
    var $groupId;
50
51
    private static $instance;
52
    
53
    function Docman_ItemFactory($groupId=null) {
54
        // Cache highly used info
55
        $this->rootItems[] = array();
56
        $this->onlyOneChildForRoot[] = array();
57
        $this->copiedItem = array();
58
        $this->cutItem = array();
59
60
        // Parameter
61
        $this->groupId = $groupId;
62
    }
63
64
    /**
65
     * Return a single instance of Docman_ItemFactory per group.
66
     * 
67
     * This is useful when you need to cache information across method calls
68
     * 
69
     * @param Integer $group_id Project id
70
     * 
71
     * @return Docman_ItemFactory
72
     */
73
    public static function instance($group_id) {
74
        if(!isset(self::$instance[$group_id])) {
75
            self::$instance[$group_id] = new Docman_ItemFactory($group_id);
76
        }
77
        return self::$instance[$group_id];
78
    }
79
80
    /**
81
     * Return a single instance of Docman_ItemFactory per group.
82
     * 
83
     * This is useful when you need to cache information across method calls
84
     * 
85
     * @param Integer $group_id Project id
86
     * 
87
     * @return Docman_ItemFactory
88
     */
89
    public static function setInstance($group_id, $instance) {
90
        self::$instance[$group_id] = $instance;
91
    }
92
93
    /**
94
     * Return a single instance of Docman_ItemFactory per group.
95
     * 
96
     * This is useful when you need to cache information across method calls
97
     * 
98
     * @param Integer $group_id Project id
99
     * 
100
     * @return Docman_ItemFactory
101
     */
102
    public static function clearInstance($group_id) {
103
        self::$instance[$group_id] = null;
104
    }
105
106
    function setGroupId($id) {
107
        $this->groupId = $id;
108
    }
109
    function getGroupId() {
110
        return $this->groupId;
111
    }
112
113
    function &getItemFromRow(&$row) {
114
        $item = null;
115
        switch($row['item_type']) {
116
        case PLUGIN_DOCMAN_ITEM_TYPE_FOLDER:
117
            $item = new Docman_Folder($row);
118
            break;
119
        case PLUGIN_DOCMAN_ITEM_TYPE_FILE:
120
            $item = new Docman_File($row);
121
            break;
122
        case PLUGIN_DOCMAN_ITEM_TYPE_LINK:
123
            $item = new Docman_Link($row);
124
            if (isset($row['link_version_id'])) {
125
                $item->setCurrentVersion(
126
                    new Docman_LinkVersion(
127
                        array(
128
                            'id'        => $row['link_version_id'],
129
                            'user_id'   => $row['link_version_user_id'],
130
                            'item_id'   => $item->getId(),
131
                            'number'    => $row['link_version_number'],
132
                            'label'     => $row['link_version_label'],
133
                            'changelog' => $row['link_version_changelog'],
134
                            'date'      => $row['link_version_date'],
135
                            'link_url'      => $row['link_version_link_url']
136
                        )
137
                    )
138
                );
139
            }
140
            break;
141
        case PLUGIN_DOCMAN_ITEM_TYPE_EMBEDDEDFILE:
142
            $item = new Docman_EmbeddedFile($row);
143
            break;
144
        case PLUGIN_DOCMAN_ITEM_TYPE_WIKI:
145
            $item = new Docman_Wiki($row);
146
            break;
147
        case PLUGIN_DOCMAN_ITEM_TYPE_EMPTY:
148
            $item = new Docman_Empty($row);
149
            break;
150
        default:
151
            return;
152
        }
153
        if ($row['item_type'] == PLUGIN_DOCMAN_ITEM_TYPE_FILE || $row['item_type'] == PLUGIN_DOCMAN_ITEM_TYPE_EMBEDDEDFILE) {
154
            if (isset($row['version_id'])) {
155
                $version = array(
156
                    'id'        => $row['version_id'],
157
                    'user_id'   => $row['version_user_id'],
158
                    'item_id'   => $item->getId(),
159
                    'number'    => $row['version_number'],
160
                    'label'     => $row['version_label'],
161
                    'changelog' => $row['version_changelog'],
162
                    'date'      => $row['version_date'],
163
                    'filename'  => $row['version_filename'],
164
                    'filesize'  => $row['version_filesize'],
165
                    'filetype'  => $row['version_filetype'],
166
                    'path'      => $row['version_path']
167
                );
168
                $item->setCurrentVersion(new Docman_Version($version));
0 ignored issues
show
Bug introduced by
The method setCurrentVersion does only exist in Docman_File and Docman_Link, but not in Docman_Empty and Docman_Folder and Docman_Wiki.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
169
            }
170
        }
171
        return $item;
172
    }
173
174
    function getItemTypeAsText($itemTypeId) {
175
        switch($itemTypeId) {
176
        case PLUGIN_DOCMAN_ITEM_TYPE_FOLDER:
177
            return $GLOBALS['Language']->getText('plugin_docman','filters_item_type_folder');
178
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
179
        case PLUGIN_DOCMAN_ITEM_TYPE_FILE:
180
            return $GLOBALS['Language']->getText('plugin_docman','filters_item_type_file');
181
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
182
        case PLUGIN_DOCMAN_ITEM_TYPE_LINK:
183
            return $GLOBALS['Language']->getText('plugin_docman','filters_item_type_link');
184
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
185
        case PLUGIN_DOCMAN_ITEM_TYPE_EMBEDDEDFILE:
186
            return $GLOBALS['Language']->getText('plugin_docman','filters_item_type_embeddedfile');
187
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
188
        case PLUGIN_DOCMAN_ITEM_TYPE_WIKI:
189
            return $GLOBALS['Language']->getText('plugin_docman','filters_item_type_wiki');
190
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
191
        case PLUGIN_DOCMAN_ITEM_TYPE_EMPTY:
192
            return $GLOBALS['Language']->getText('plugin_docman','filters_item_type_empty');
193
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
194
        default:
195
            return $GLOBALS['Language']->getText('include_html','unknown_value');
196
        }
197
    }
198
199
    function getItemTypeForItem(&$item) {
200
        $type = false;
201
        switch(strtolower(get_class($item))) {
202
            case 'docman_folder':
203
                $type = PLUGIN_DOCMAN_ITEM_TYPE_FOLDER;
204
                break;
205
            case 'docman_link':
206
                $type = PLUGIN_DOCMAN_ITEM_TYPE_LINK;
207
                break;
208
            case 'docman_wiki':
209
               $type = PLUGIN_DOCMAN_ITEM_TYPE_WIKI;
210
                break;
211
            case 'docman_file':
212
                $type = PLUGIN_DOCMAN_ITEM_TYPE_FILE;
213
                break;
214
            case 'docman_embeddedfile':
215
                $type = PLUGIN_DOCMAN_ITEM_TYPE_EMBEDDEDFILE;
216
                break;
217
            case 'docman_empty':
218
                $type = PLUGIN_DOCMAN_ITEM_TYPE_EMPTY;
219
                break;
220
            default:
221
                break;
222
        }
223
        return $type;
224
    }
225
	
226
    /**   
227
    * @return wiki page id or null if the page is not yet created in wiki.
228
    */   
229
    function getIdInWikiOfWikiPageItem($pagename, $group_id) {
230
        $wiki_page = $this->getWikiPage($group_id, $pagename);
231
232
        if ($wiki_page->exist()) {
233
            return $wiki_page->getId();
234
        } else {
235
            return null;
236
        }
237
    }
238
239
    /**
240
    * This looks for possible references of a wiki page from docman
241
    *
242
    * @param string $wiki_page
243
    * @param string $group_id
244
    *
245
    * @return Array items that reference the same given wiki page.
246
    */
247
    function getWikiPageReferencers($wiki_page, $group_id) {
248
        $items = array();
249
        $item_dao =& $this->_getItemDao();
250
        if($item_dao->isWikiPageReferenced($wiki_page, $group_id)) {
251
            $items_ids = $item_dao->getItemIdByWikiPageAndGroupId($wiki_page, $group_id);
252
            if(is_array($items_ids)){
253
                foreach($items_ids as $key => $id) {
254
                    $items[] = $this->getItemFromDb($id);
255
                }
256
            }
257
            else {
258
                $items[] =& $this->getItemFromDb($items_ids);
259
            }
260
        }
261
        return $items;
262
    }
263
264
    /**
265
    * This deletes an existant wiki page and all its stored data and infos from codendi db.
266
    *
267
    * @param string $wiki_page_name name of the wiki page
268
    * @param int $group_id project id.
269
    *
270
    * @return true if there was no error.
271
    */
272
    function deleteWikiPage($wiki_page_name, $group_id) {
273
        $wiki_page = $this->getWikiPage($group_id, $wiki_page_name);
274
275
        return $wiki_page->delete();
276
    }
277
278
    private function getWikiPage($project_id, $pagename) {
279
        $wiki_page = null;
280
281
        $event_manager = EventManager::instance();
282
        $event_manager->processEvent(
283
            PLUGIN_DOCMAN_EVENT_GET_PHPWIKI_PAGE,
284
            array(
285
                'phpwiki_page_name' => $pagename,
286
                'project_id'        => $project_id,
287
                'phpwiki_page'      => $wiki_page
288
            )
289
        );
290
291
        if ($wiki_page === null) {
292
            $wiki_page = new WikiPage($project_id, $pagename);
293
        }
294
295
        return $wiki_page;
296
    }
297
298
    /**
299
     * @return Docman_Item
300
     */
301
    function &getItemFromDb($id, $params = array()) {
302
        $_id = (int) $id;
303
        $dao =& $this->_getItemDao();
304
        $dar = $dao->searchById($id, $params);
305
306
        $item = null;
307
        if(!$dar->isError() && $dar->valid()) {
308
            $row =& $dar->current();
309
            $item =& Docman_ItemFactory::getItemFromRow($row);
310
        }
311
        return $item;
312
    }
313
314
    function &getChildrenFromParent($item) {
315
        $dao =& $this->_getItemDao();
316
        
317
        $itemArray = array();
318
319
        $dar = $dao->searchByParentsId(array($item->getId()));
320
        if ($dar && !$dar->isError()) {
321
            while($dar->valid()) {
322
                $row = $dar->current();
323
324
                $itemArray[] = Docman_ItemFactory::getItemFromRow($row);
325
326
                $dar->next();
327
            }
328
        }
329
330
        $iIter = new ArrayIterator($itemArray);
331
        return $iIter;
332
    }
333
334
    public function getAllChildrenFromParent(Docman_Item $item) {
335
        $children      = array();
336
        $item_iterator = $this->getChildrenFromParent($item);
337
338
        foreach($item_iterator as $child) {
339
            $item_type = $this->getItemTypeForItem($child);
340
341
            if ($item_type === PLUGIN_DOCMAN_ITEM_TYPE_FILE || $item_type === PLUGIN_DOCMAN_ITEM_TYPE_EMBEDDEDFILE) {
342
                $docman_version_factory = new Docman_VersionFactory();
343
                $item_current_version   = $docman_version_factory->getCurrentVersionForItem($child);
344
345
                $child->setCurrentVersion($item_current_version);
346
            }
347
348
            $children[] = $child;
349
350
            if ($item_type === PLUGIN_DOCMAN_ITEM_TYPE_FOLDER) {
351
                $children = array_merge($children, $this->getAllChildrenFromParent($child));
352
            }
353
        }
354
355
        return $children;
356
    }
357
    
358
    /**
359
     * Retreive list of collapsed items for given user
360
     *
361
     * This function retreive collapsed folders from user preferences 
362
     *
363
     * @param $parentId Id of the "current" root node (cannot be excluded).
364
     * @param $userId Id of current user.
365
     * @return Array List of items to exclude for a search
366
     **/
367
    function &_getExpandedUserPrefs($parentId, $userId) {           
368
        $collapsedItems = array();     
369
        // Retreive the list of collapsed folders in prefs
370
        $dao =& $this->_getItemDao();
371
        $dar = $dao->searchExpandedUserPrefs($this->groupId, 
372
                                                   $userId);
373
        while($dar->valid()) {
374
            $row =& $dar->current();
375
            $tmp = explode('_', $row['preference_name']);
376
            if ($tmp[4] != $parentId) {
377
                $collapsedItems[] = (int) $tmp[4];
378
            }
379
            $dar->next();
380
        }               
381
        
382
        return $collapsedItems;
383
    }
384
    
385
    /**
386
     * Preload item perms from a item result set
387
     */
388
    function preloadItemPerms($dar, $user, $groupId) {
389
        // Preload perms
390
        $objectsIds = array();
391
        $dar->rewind();
392
        while($dar->valid()) {
393
            $row = $dar->current();
394
            $objectsIds[] = $row['item_id'];
395
            $dar->next();
396
        }
397
        $dar->rewind();
398
399
        $dPm = Docman_PermissionsManager::instance($groupId);
400
        $dPm->retreiveReadPermissionsForItems($objectsIds, $user);
401
    }
402
403
    /**
404
     * Check if a given item is into the subtree of another given item or not.
405
     *
406
     * @param Integer $childId  Id of the potential child.
407
     * @param Integer $parentId Id of the potential parent.
408
     *
409
     * @return Boolean
410
     */
411
    function isInSubTree($childId, $parentId) {
412
        $child = $this->getItemFromDb($childId);
413
        if ($this->isRoot($child)) {
414
            return false;
415
        }
416
        $directParentId = $child->getParentId();
417
        if ($parentId == $directParentId) {
418
            return true;
419
        } else {
420
            return $this->isInSubTree($directParentId, $parentId);
421
        }
422
    }
423
424
    /**
425
     * Give the list of parents of an item
426
     *
427
     * @param Integer $childId  Id of the child.
428
     *
429
     * @return Array
430
     */
431
    function getParents($childId) {
432
        $child = $this->getItemFromDb($childId);
433
        if ($this->isRoot($child)) {
434
            return array();
435
        }
436
        $directParentId = $child->getParentId();
437
        $parents = $this->getParents($directParentId);
438
        $parents[$directParentId] = true;
439
        return $parents;
440
    }
441
442
    /**
443
     * Build a subtree from the given item id.
444
     *
445
     * Build the list in depth, level per level, from root to leaves.
446
     * 
447
     * @param Docman_Item $rootItem
448
     * @param PFUser $user
449
     * @param boolean $ignorePerms
450
     * @param boolean $expandAll
451
     * @param boolean $ignoreObsolete
452
     * @return Docman_Item
453
     */
454
    function &getItemSubTree(&$rootItem, &$user, $ignorePerms=false, $expandAll=false, $ignoreObsolete=true) {
455
        // {{1}} Exclude collapsed items
456
        $expandedFolders = array();
457
        if(!$expandAll) {
458
            $fld = $this->_getExpandedUserPrefs($rootItem->getId(), user_getid());
459
            foreach($fld as $v) {
460
                $expandedFolders[$v] = true;
461
            }
462
        }
463
464
        $searchItemsParams = array('ignore_obsolete' => $ignoreObsolete);
465
466
        //
467
        // Treatment
468
        //
469
        $dao =& $this->_getItemDao();
470
        $dPm = Docman_PermissionsManager::instance($rootItem->getGroupId());
471
        
472
        $itemList = array($rootItem->getId() => &$rootItem);
473
        $parentIds = array($rootItem->getId());
474
        do {
475
            // Fetch all children for the given level.
476
            $dar = $dao->searchChildren($parentIds, $searchItemsParams);
477
            $parentIds = array();
478
            $itemIds = array();
479
            $itemRows = array();
480
            if($dar && !$dar->isError()) {
481
                $dar->rewind();
482
                while($dar->valid()) {
483
                    $row = $dar->current();
484
                    $itemRows[$row['item_id']] = $row;
485
                    $itemIds[] = $row['item_id'];
486
                    if($row['item_type'] == PLUGIN_DOCMAN_ITEM_TYPE_FOLDER
487
                        && ($expandAll || isset($expandedFolders[$row['item_id']]))) {
488
                        $parentIds[$row['item_id']] = $row['item_id'];
489
                    }
490
                    $dar->next();
491
                }
492
                
493
                // Fetch all the permissions at the same time
494
                $dPm->retreiveReadPermissionsForItems($itemIds, $user);
495
496
                // Build hierarchy: only keep displayable items
497
                foreach($itemIds as $id) {
498
                    if($ignorePerms || $dPm->userCanRead($user, $id)) {
499
                        $itemList[$id] =& $this->getItemFromRow($itemRows[$id]);
500
                        $itemList[$itemList[$id]->getParentId()]->addItem($itemList[$id]);
501
                    } else {
502
                       unset($parentIds[$id]);
503
                    }
504
                }
505
            }
506
        } while(count($parentIds) > 0);
507
508
        return $itemList[$rootItem->getId()];
509
    }
510
    
511
    /**
512
     * This function return an iterator on a list of documents (no folders).
513
     *
514
     * How it works:
515
     * 1. Get the list of all documents that match the criteria (if
516
     *    any!). (permissions apply).
517
     *    Note: the final list of documents is a subset of this result.
518
     * 2. Get the list of folders behind $parentId (permissions apply).
519
     * 3. Check that each document in list 1. is in a folder of list 2.
520
     * 5. Apply limits ($start, $offset) is only a subset of the list is required.
521
     * 6. If needed, add the metadata to the items. 
522
     */
523
    function &getItemSubTreeAsList($parentId, &$nbItemsFound, $params = null) {
524
        $_parentId = (int) $parentId;
525
         
526
        $user =& $params['user'];
527
528
        // Prepare filters if any
529
        $filter = null;
530
        if(isset($params['filter'])) {
531
            $filter =& $params['filter'];
532
        }
533
534
        // Obsolescence
535
        $searchItemsParams = array();
536
        if(isset($params['ignore_obsolete'])) {
537
            $searchItemsParams['ignore_obsolete'] = $params['ignore_obsolete'];
538
        }
539
540
        // Range of documents to return
541
        $start = 0;
542
        if(isset($params['start'])) {
543
            $start = $params['start'];
544
        }
545
        $end = 25;
546
        if(isset($params['offset'])) {
547
            $end = $start + $params['offset'];
548
        }
549
550
        $dao =& $this->_getItemDao();
551
        
552
        //
553
        // Build Folder List
554
        //
555
        $parentItem = $this->getItemFromDb($parentId);
556
        $dPm = Docman_PermissionsManager::instance($parentItem->getGroupId());
557
        $folderList = array($parentId => &$parentItem);
558
        $pathIdArray = array($parentId => array());
559
        $pathTitleArray = array($parentId => array());
560
        $parentIds = array($parentId);
561
        $folderIds = array($parentId);
562
        $i = 0;
563
        do {
564
            $i++;
565
            $dar = $dao->searchSubFolders($parentIds);
566
            $parentIds = array();
567
            $itemIds   = array();
568
            $itemRows = array();
569
            if($dar && !$dar->isError()) {
570
                $dar->rewind();
571
                while($dar->valid()) {
572
                    $row = $dar->current();
573
                    $itemRows[$row['item_id']] = $row;
574
                    $itemIds[] = $row['item_id'];
575
                    $parentIds[$row['item_id']] = $row['item_id'];
576
                    $dar->next();
577
                }
578
579
                // Fetch all the permissions at the same time
580
                $dPm->retreiveReadPermissionsForItems($itemIds, $user);
581
582
                // Build hierarchy: only keep displayable items
583
                foreach($itemIds as $id) {
584
                    if($dPm->userCanRead($user, $id)) {
585
                        $folderList[$id] =& $this->getItemFromRow($itemRows[$id]);
586
                        // Update path
587
                        $pathIdArray[$id] = array_merge($pathIdArray[$folderList[$id]->getParentId()], array($id));
588
                        $pathTitleArray[$id] = array_merge($pathTitleArray[$folderList[$id]->getParentId()], array($folderList[$id]->getTitle()));
589
                    } else {
590
                        unset($parentIds[$id]);
591
                    }
592
                }
593
            }
594
        } while(count($parentIds) > 0);
595
596
        //
597
        // Keep only documents in allowed subfolders
598
        //
599
        $mdFactory  = new Docman_MetadataFactory($this->groupId);
600
        $ci = null;
601
        if($filter !== null) {
602
            $ci = $filter->getColumnIterator();
603
        }
604
605
        //
606
        // Build Document list
607
        //
608
        $itemArray = array();
609
        if(isset($params['obsolete_only']) && $params['obsolete_only']) {
610
            $dar = $dao->searchObsoleteByGroupId($this->groupId);
611
        } else {
612
            $dar = $dao->searchByGroupId($this->groupId, $filter, $searchItemsParams);
613
        }
614
615
        $nbItemsFound = 0;
616
        if($dar && !$dar->isError()) {
617
            $this->preloadItemPerms($dar, $user, $this->groupId);
618
            $dar->rewind();
619
            while($dar->valid()) {
620
                $row = $dar->current();
621
                // The document is not is one of the allowed subfolder so we
622
                // can delete it. As a side effect decrease the number of
623
                // document found.
624
                if($dPm->userCanRead($user, $row['item_id']) && isset($folderList[$row['parent_id']])) {
625
                    if($nbItemsFound >= $start && $nbItemsFound < $end || (isset($params['getall']) && $params['getall'])) {
626
                        $itemArray[$row['item_id']] =& $this->getItemFromRow($row);
627
628
                        // Append Path
629
                        $itemArray[$row['item_id']]->setPathTitle($pathTitleArray[$row['parent_id']]);
630
                        $itemArray[$row['item_id']]->setPathId($pathIdArray[$row['parent_id']]);
631
632
                        // Append metadata
633
                        if($ci !== null) {
634
                            $ci->rewind();
635
                            while($ci->valid()) {
636
                                $c = $ci->current();
637
                                if($c->md !== null && $mdFactory->isRealMetadata($c->md->getLabel())) {
638
                                    $mdFactory->addMetadataValueToItem($itemArray[$row['item_id']], $c->md);
639
                                }
640
                                $ci->next();
641
                            }
642
                        }
643
                    }
644
                    $nbItemsFound++;
645
                }
646
                $dar->next();
647
            }
648
        }
649
650
        $docIter =& new ArrayIterator($itemArray);
651
        return $docIter;
652
    }
653
654
    /**
655
     * Build a tree from with the list of items
656
     *
657
     * @return Docman_Item
658
     */
659
    function &getItemTree(&$rootItem, &$user, $ignorePerms=false, $expandAll=false, $ignoreObsolete=true) {
660
        return $this->getItemSubTree($rootItem, $user, $ignorePerms, $expandAll, $ignoreObsolete);
661
    }
662
663
    /**
664
     * Build a list of items
665
     *
666
     * @return ItemNode
667
     */
668
    function &getItemList($id = 0, &$nbItemsFound, $params = null) {
669
        if (!$id) {
670
            $dao =& $this->_getItemDao();
671
            $id = $dao->searchRootIdForGroupId($this->groupId);
672
        }
673
        return $this->getItemSubTreeAsList($id, $nbItemsFound, $params);
674
    } 
675
676
    /**
677
     *
678
     */
679
    function &getDocumentsIterator() {
680
        $dao =& $this->_getItemDao();
681
        $filters = null;
682
        $dar = $dao->searchByGroupId($this->groupId, $filters, array());
683
        $itemList = array();
684
        while($dar->valid()) {
685
            $row = $dar->current();
686
687
            $item =& $this->getItemFromRow($row);
688
            $type = $this->getItemTypeForItem($item);
689
            if($type != PLUGIN_DOCMAN_ITEM_TYPE_FOLDER) {
690
                if(!isset($itemList[$item->getId()])) {
691
                    $itemList[$item->getId()] =& $item;
692
                }
693
            }
694
695
            $dar->next();
696
        }
697
698
        $i = new ArrayIterator($itemList);
699
        return $i;
700
    }
701
702
    /**
703
     * @param int $limit
704
     * @param int $offset
705
     * @return Docman_File[]
706
     */
707
    public function searchPaginatedWithVersionByGroupId($limit, $offset) {
708
        $result = $this->_getItemDao()->searchPaginatedWithVersionByGroupId($this->groupId, $limit, $offset);
709
710
        $items = array();
711
        foreach ($result as $row) {
712
            $items[] = $this->getItemFromRow($row);
713
        }
714
715
        $result->freeMemory();
716
717
        return $items;
718
    }
719
720
    /**
721
     *
722
     */
723
    function findByTitle($user, $title, $groupId) {
724
        $ia = array();
725
726
        $dao =& $this->_getItemDao();
727
        $dPm = Docman_PermissionsManager::instance($groupId);
728
        $dar = $dao->searchByTitle($title);
729
        $dar->rewind();
730
        while($dar->valid()) {
731
            $row = $dar->current();
732
733
            $item = $this->getItemFromRow($row);
734
            if($dPm->userCanRead($user, $item->getId())) {
735
                $parentItem = $this->getItemFromDb($item->getParentId());
736
                if($dPm->userCanRead($user, $parentItem->getId())) {
737
                    $ia[] = $item;
738
                }
739
            }
740
741
            $dar->next();
742
        }
743
744
        $ii = new ArrayIterator($ia);
745
746
        return $ii;
747
    }
748
749
    /*
750
     * Give the list of documents obsolete that have an obsolescence date in
751
     * one month.
752
     * It means that the obso date of the document is between 00:00:00 and
753
     * 23:59:59 in on month from today.
754
     */
755
    function findFuturObsoleteItems() {
756
        // Compute the timescale for the day in one month
757
        $today = getdate();
758
        $tsStart = mktime(0,0,0, $today['mon']+1, $today['mday'], $today['year']);
759
        $tsEnd   = mktime(23,59,59, $today['mon']+1, $today['mday'], $today['year']);
760
761
        $ia = array();
762
        $dao =& $this->_getItemDao();
763
        $dar = $dao->searchObsoleteAcrossProjects($tsStart, $tsEnd);
764
        while($dar->valid()) {
765
            $row = $dar->current();
766
            $ia[] = $this->getItemFromRow($row);
767
            $dar->next();
768
        }
769
770
        $ii = new ArrayIterator($ia);
771
        return $ii;
772
    }
773
774
    var $dao;
775
    /**
776
     * @return Docman_ItemDao
777
     */
778
    function _getItemDao() {
779
        if (! $this->dao) {
780
            $this->dao = new Docman_ItemDao(CodendiDataAccess::instance());
781
        }
782
        return $this->dao;
783
    }
784
785
    protected function _getVersionFactory() {
786
        return new Docman_VersionFactory();
787
    }
788
789
    protected function _getUserManager() {
790
        return UserManager::instance();
791
    }
792
793
    protected function _getEventManager() {
794
        return EventManager::instance();
795
    }
796
797
    function update($row) {
798
        // extract cross references
799
        $reference_manager = ReferenceManager::instance();
800
        if (isset($row['title'])) {
801
            $reference_manager->extractCrossRef($row['title'], $row['id'], ReferenceManager::REFERENCE_NATURE_DOCUMENT, $this->groupId);
802
        }
803
        if (isset($row['description'])) {
804
            $reference_manager->extractCrossRef($row['description'], $row['id'], ReferenceManager::REFERENCE_NATURE_DOCUMENT, $this->groupId);
805
        }
806
        $dao = $this->_getItemDao();
807
        return $dao->updateFromRow($row);
808
    }
809
810
    public function updateLink(Docman_Link $link, array $version_data) {
811
        $update = $this->update(
812
            array(
813
                'id'        => $link->getId(),
814
                'group_id'  => $link->getGroupId(),
815
                'title'     => $link->getTitle(),
816
                'user_id'   => $link->getOwnerId(),
817
                'item_type' => PLUGIN_DOCMAN_ITEM_TYPE_LINK,
818
                'link_url'  => $link->getUrl(),
819
            )
820
        );
821
822
        $link_version_factory = new Docman_LinkVersionFactory();
823
824
        $create = $link_version_factory->create($link, $version_data['label'], $version_data['changelog'], $_SERVER['REQUEST_TIME']);
825
826
        return ($update && $create);
827
    }
828
829
    function massUpdate($srcItemId, $mdLabel, $itemIdArray) {
830
        $dao =& $this->_getItemDao();
831
        $dao->massUpdate($srcItemId, $mdLabel, $itemIdArray);
832
    }
833
834
    public function create($row, $ordering) {
835
        $dao =& $this->_getItemDao();
836
        $id = $dao->createFromRow($row);
837
        if ($id) {
838
            $this->setNewParent($id, $row['parent_id'], $ordering);
839
        }
840
841
        return $id;
842
    }
843
844
    /**
845
     * Find root unique child if exists.
846
     *
847
     * @param $groupId Project id of the docman.
848
     * @return int/boolean false if there is more than one children for root.
0 ignored issues
show
Documentation introduced by
The doc-type int/boolean could not be parsed: Unknown type name "int/boolean" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
849
     *                     true if there is no child for root.
850
     *                     item_id of the unique child of root if any.
851
     */
852
    function isItemTheOnlyChildOfRoot($groupId) {
853
        if(!isset($this->onlyOneChildForRoot[$groupId])) {
854
            $dao = $this->_getItemDao();
855
            $dar = $dao->hasRootOnlyOneChild($groupId);
856
            if($dar && !$dar->isError()) {
857
                if($dar->rowCount() > 1) {
858
                    $this->onlyOneChildForRoot[$groupId] = false;
859
                } elseif($dar->rowCount() == 0) {
860
                    $this->onlyOneChildForRoot[$groupId] = true;
861
                } else {
862
                    $row = $dar->getRow();
863
                    $this->onlyOneChildForRoot[$groupId] = (int) $row['item_id'];
864
                }
865
            }
866
        }
867
        return $this->onlyOneChildForRoot[$groupId];
868
    }
869
870
    /**
871
     * Check if given item is movable or not.
872
     *
873
     * An item is movable if:
874
     * - it's not root of the project.
875
     * - there is more than one children for root.
876
     * - or if the item is not the unique children of root.
877
     */
878
    function isMoveable($item) {
879
        $movable = false;
880
        if($item->getParentId() != 0) {
881
            $onlyOneChild = $this->isItemTheOnlyChildOfRoot($item->getGroupId());
882
            if($onlyOneChild === false || $onlyOneChild !== $item->getId()) {
883
                $movable = true;
884
            }
885
        }
886
        return $movable;
887
    }
888
 
889
   function setNewParent($item_id, $new_parent_id, $ordering) {
890
        $item =& $this->getItemFromDb($item_id);
891
        $dao =& $this->_getItemDao();
892
        return $item && $this->isMoveable($item) && $dao->setNewParent($item_id, $new_parent_id, $ordering);
893
    }
894
895
    /**
896
    * Walk through a item hierarchy and for each subitem apply callback method
897
    * in parameter.
898
    *
899
    * The callback method (or function) will be applied for each sub-item of
900
    * $item_id with following paramters:
901
    * - A plugin_docman_item table row that correspond to the child node.
902
    * - $params
903
    *
904
    * @see call_user_func_array for details on $callback forms.
905
    *
906
    * @param int   $item_id  Id of the parent item.
907
    * @param mixed $callback Callback function or method.
908
    * @param array $params   Parameters for the callback function
909
    * @return void
910
    */
911
    function breathFirst($item_id, $callback, $params) {
912
        $dao =& $this->_getItemDao();
913
        $parents = array($item_id);
914
        do {
915
            $dar = $dao->searchByParentsId($parents);
916
            if ($dar && !$dar->isError()) {
917
                $parents = array();
918
                while ($dar->valid()) {
919
                    $row = $dar->current();
920
                    call_user_func_array($callback, array($row, $params));
921
                    if (PLUGIN_DOCMAN_ITEM_TYPE_FOLDER == $row['item_type']) {
922
                        $parents[] = $row['item_id'];
923
                    }
924
                    $dar->next();
925
                }
926
            }
927
        } while (count($parents) > 0);
928
    }
929
930
    /**
931
     * Returns an item tree build from leaves to root ("bottom -> top").
932
     *
933
     * @param  Array of items.
934
     * @return Item_Folder A sub tree or null if root node was not found.
935
     */
936
    function &getItemTreeFromLeaves($itemArray, $user) {
937
        $null = null;
938
        if(is_array($itemArray)) {
939
            foreach($itemArray as $item) {
940
                $itemList[$item->getId()] = $item;
941
                $orphans[$item->getId()] = $item->getId();
942
                $itemIds[] = $item->getId();
943
            }
944
        } else {
945
            return $null;
946
        }
947
948
        // Check permissions on submitted item array
949
        $dpm =& Docman_PermissionsManager::instance($this->groupId);
950
        $dpm->retreiveReadPermissionsForItems($itemIds, $user);
951
        foreach($itemArray as $item) {
952
             if(!$dpm->userCanRead($user, $item->getId())) {
953
                 unset($itemList[$item->getId()]);
954
                 unset($orphans[$item->getId()]);
955
             }
956
        }
957
958
        // Now, here we go
959
        $paths = array();
960
        $dao =& $this->_getItemDao();
961
        $rootId = false;
962
        do {
963
            // Try to build the connections between childrens and parents in itemList
964
            $wantedItems = array();
965
            $rootInfo = $this->connectOrphansToParents($itemList, $orphans, $wantedItems);
966
            if($rootInfo !== false) {
967
                $rootId = $rootInfo;
968
            }
969
970
            // If some items are missing, look for them in the DB.
971
            if(is_array($wantedItems) && count($wantedItems) > 0) {
972
                $dar = $dao->searchByIdList($wantedItems);
973
                if($dar && !$dar->isError()) {
974
                    $this->preloadItemPerms($dar, $user, $this->groupId);
975
                    while ($dar->valid()) {
976
                        $row = $dar->current();
977
                        $item = $this->getItemFromRow($row);
978
                        if($dpm->userCanRead($user, $item->getId())) {
979
                            $itemList[$item->getId()] = $item;
980
                            $orphans[$item->getId()] = $item->getId();
981
                        } else {
982
                            $itemList[$item->getId()] = false;
983
                        }
984
                        $dar->next();
985
                    }
986
                }
987
            }
988
        } while (count($wantedItems) > 0);
989
990
        if($rootId !== false) {
991
            return $itemList[$rootId];
992
        } else {
993
            return $null;
994
        }
995
    }
996
997
    /**
998
     * Build the connexions between the different nodes in item list and
999
     * identify the missing nodes.
1000
     *
1001
     * This method iterates on $orphans list that indicates the item in
1002
     * $itemList that are not yet connected to their father node.
1003
     * The function returns the nodes in $itemList that are still orphans and
1004
     * the list of item Ids needed to continue to build the tree
1005
     * ($wantedItems).
1006
     *
1007
     * See UnitTests
1008
     * @param $itemList    Array of Docma_Item.
1009
     * @param $orphan      Hashmap of item ids. Items (in ItemList) without
1010
     *                     parent node
1011
     * @param $wantedItems Items needed to continue to build the tree.
1012
     * @return Integer Id of root item if found, false otherwise.
1013
     */
1014
    function connectOrphansToParents(&$itemList, &$orphans, &$wantedItems) {
1015
        $rootId = false;
1016
        foreach($orphans as $itemId) {
1017
            // Check if orphan belong to the item list and is available.
1018
            // As orphans should always be parts of $itemList, it means that
1019
            // this orphan is not readable by user.
1020
            if(isset($itemList[$itemId]) && $itemList[$itemId] !== false) {
1021
                // Check if current item parents is in the list
1022
                $pid = $itemList[$itemId]->getParentId();
1023
                if($pid != 0) {
1024
                    if(isset($itemList[$pid])) {
1025
                        if($itemList[$pid] !== false) {
1026
                            $itemList[$pid]->addItem($itemList[$itemId]);
1027
                            unset($orphans[$itemId]);
1028
                        }
1029
                    } else {
1030
                        if(!isset($orphans[$itemId])) {
1031
                            $orphans[$itemId] = $itemId;
1032
                        }
1033
                        $wantedItems[] = $pid;
1034
                    }
1035
                } else {
1036
                    $rootId = $itemId;
1037
                    unset($orphans[$itemId]);
1038
                }
1039
            }
1040
        }
1041
        return $rootId;
1042
    }
1043
1044
    /**
1045
     * Returns a hashmap with the mapping between items in $item tree and items
1046
     * that belongs to this group.
1047
     */
1048
    function getItemMapping($item) {
1049
        $v =& new Docman_BuildItemMappingVisitor($this->groupId);
1050
        $item->accept($v);
1051
        return $v->getItemMapping();
1052
    }
1053
1054
1055
    function &getRoot($group_id) {
1056
        if(!isset($this->rootItems[$group_id])) {
1057
            $dao =& $this->_getItemDao();
1058
            $id = $dao->searchRootIdForGroupId($group_id);
1059
            $this->rootItems[$group_id] = $this->getItemFromDb($id);
1060
        }
1061
        return $this->rootItems[$group_id];
1062
    }
1063
    function isRoot(&$item) {
1064
        $root = $this->getRoot($item->getGroupId());
1065
        return $item->getId() == $root->getId();
1066
    }
1067
    function &createRoot($group_id, $title) {
1068
        $dao =& $this->_getItemDao();
1069
        $root =& new Docman_Folder();
1070
        $root->setGroupId($group_id);
1071
        $root->setTitle($title);
1072
        return $dao->createFromRow($root->toRow());
1073
    }
1074
1075
    function rawCreate($item) {
1076
        $dao = $this->_getItemDao();
1077
        return $dao->createFromRow($item->toRow());
1078
    }
1079
1080
    /**
1081
     * Copy a subtree.
1082
     */
1083
    function cloneItems($srcGroupId, $dstGroupId, $user, $metadataMapping, $ugroupsMapping, $dataRoot, $srcItemId = 0, $dstItemId = 0, $ordering = null) {
1084
        $itemMapping = array();
1085
1086
        $itemFactory = new Docman_ItemFactory($srcGroupId);
1087
        if($srcItemId == 0) {
1088
            $srcItem = $this->getRoot($srcGroupId);
1089
        } else {
1090
            $srcItem = $this->getItemFromDb($srcItemId);
1091
        }
1092
        $itemTree = $itemFactory->getItemTree($srcItem, $user, false, true);
1093
        
1094
        if ($itemTree) {
1095
            $rank = null;
1096
            if($ordering !== null) {
1097
                $dao  =& $this->_getItemDao();
1098
                $rank = $dao->_changeSiblingRanking($dstItemId, $ordering);
1099
            }
1100
1101
            $cloneItemsVisitor = new Docman_CloneItemsVisitor($dstGroupId);
1102
            $visitorParams = array('parentId' => $dstItemId,
1103
                               'user' => $user,
1104
                               'metadataMapping' => $metadataMapping,
1105
                               'ugroupsMapping'  => $ugroupsMapping,
1106
                               'data_root' => $dataRoot,
1107
                               'newRank' => $rank,
1108
                               'srcRootId' => $srcItemId);
1109
            $itemTree->accept($cloneItemsVisitor, $visitorParams);
1110
            $itemMapping = $cloneItemsVisitor->getItemMapping();
1111
        }
1112
        return $itemMapping;
1113
    }
1114
    
1115
    function preferrencesExist($group_id, $user_id) {
1116
        $dao =& $this->_getItemDao();
1117
        $dar = $dao->searchExpandedUserPrefs($group_id, $user_id);
1118
        return $dar->valid();
1119
    }
1120
1121
    function setCutPreference($item) {
1122
        user_set_preference(PLUGIN_DOCMAN_PREF.'_item_cut',
1123
                            $item->getId());
1124
    }
1125
1126
    function setCopyPreference($item) {
1127
        user_set_preference(PLUGIN_DOCMAN_PREF.'_item_copy',
1128
                            $item->getId());
1129
    }
1130
1131
    /**
1132
     * Get the item_id that was cut by the user.
1133
     * 
1134
     * If groupId is given, only items that belongs to this groupId will be
1135
     * returned.
1136
     * If no item match, returns false.
1137
     * 
1138
     * @param PFUser    $user
1139
     * @param Integer $groupId
1140
     * 
1141
     * @return Integer or false.
1142
     */
1143
    function getCutPreference($user, $groupId=null) {
1144
        if(!isset($this->cutItem[$user->getId()])) {
1145
            $cutId = false;
1146
            $id = user_get_preference(PLUGIN_DOCMAN_PREF.'_item_cut');
1147
            if ($groupId !== null && $id !== false) {
1148
                $item = $this->getItemFromDb($id);
1149
                if ($item->getGroupId() == $groupId) {
1150
                    $cutId = $id;
1151
                }
1152
            }
1153
            $this->cutItem[$user->getId()] = $cutId;
1154
        }
1155
        return $this->cutItem[$user->getId()];
1156
    }
1157
1158
    function getCopyPreference($user) {
1159
        if(!isset($this->copiedItem[$user->getId()])) {
1160
            $this->copiedItem[$user->getId()] = user_get_preference(PLUGIN_DOCMAN_PREF.'_item_copy');
1161
        }
1162
        return $this->copiedItem[$user->getId()];
1163
    }
1164
1165
    function delCutPreference() {
1166
        user_del_preference(PLUGIN_DOCMAN_PREF.'_item_cut');
1167
    }
1168
1169
    function delCopyPreference() {
1170
        user_del_preference(PLUGIN_DOCMAN_PREF.'_item_copy');
1171
    }
1172
1173
    /**
1174
    * This order deletion of cut preferences of all users set on item identified by $item_id.
1175
    *
1176
    * @param int $item_id identifier of docman item that has been marked as deleted.
1177
    * @return void.
0 ignored issues
show
Documentation introduced by
The doc-type void. could not be parsed: Unknown type name "void." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
1178
    *
1179
    */
1180
    function delCutPreferenceForAllUsers($item_id) {
1181
        $dao =& $this->_getItemDao();
1182
        $dao->deleteCutPreferenceForAllUsers($item_id);
1183
    }
1184
1185
    /**
1186
    * This order deletion of copy preferences of all users set on item identified by $item_id.
1187
    *
1188
    * @param int $item_id identifier of docman item that has been marked as deleted.
1189
    * @return void.
0 ignored issues
show
Documentation introduced by
The doc-type void. could not be parsed: Unknown type name "void." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
1190
    *
1191
    */
1192
    function delCopyPreferenceForAllUsers($item_id) {
1193
        $dao =& $this->_getItemDao();
1194
        $dao->deleteCopyPreferenceForAllUsers($item_id);
1195
    }
1196
1197
    function getCurrentWikiVersion($item) {
1198
        $version = null;
1199
        if($this->getItemTypeForItem($item) == PLUGIN_DOCMAN_ITEM_TYPE_WIKI) {
1200
            $wiki_page = $this->getWikiPage($item->getGroupId(), $item->getPagename());
1201
1202
            if ($wiki_page->exist()) {
1203
                $version = $wiki_page->getCurrentVersion();
1204
            }
1205
        }
1206
        return $version;
1207
    }
1208
    
1209
    /**
1210
     * Returns the folder stats (count + size)
1211
     */
1212
    public function getFolderStats($folder, $user) {
1213
        if(is_a($folder, 'Docman_Folder') && $folder->getId() !== null) {
1214
            $folderSubTree = $this->getItemSubTree($folder, $user, false, true);
1215
            return $this->getFolderTreeStats($folderSubTree);
1216
        } else {
1217
            return null;
1218
        }
1219
    }
1220
    
1221
    /**
1222
     * Recursive method that takes a subtree and
1223
     * returns the corresponding stats (count + size)
1224
     */
1225
    private function getFolderTreeStats($folder) {
1226
        $stats['count'] = 0;
1227
        $stats['size'] = 0;
1228
        $stats['types'] = array();
1229
        
1230
        if(is_a($folder, 'Docman_Folder')) {
1231
            $items = $folder->getAllItems();
1232
            foreach ($items->iterator() as $item) {
1233
                $class = get_class($item);
1234
                $type = strtolower(substr(strrchr($class, '_'), 1));
1235
                
1236
                if (!isset($stats['types'][$type])) {
1237
                    $stats['types'][$type] = 0;
1238
                }
1239
                
1240
                $stats['types'][$type]++;
1241
                $stats['count']++;
1242
                if ($type == 'file' || $type == 'embeddedfile') {
1243
                    $currentVersion = $item->getCurrentVersion();
1244
                    if ($currentVersion !== null) {
1245
                        $stats['size'] += $currentVersion->getFilesize();
1246
                    }
1247
                } else if ($type == 'folder') {
1248
                    $childStats = $this->getFolderTreeStats($item);
1249
                    foreach ($childStats['types'] as $k => $v) {
1250
                        if (!isset($stats['types'][$k])) {
1251
                            $stats['types'][$k] = 0;
1252
                        }
1253
                        $stats['types'][$k] += $v;
1254
                    }
1255
                    $stats['count'] += $childStats['count'];
1256
                    $stats['size'] += $childStats['size'];
1257
                }
1258
            }
1259
        }
1260
        
1261
        return $stats;
1262
    }
1263
1264
    /**
1265
     * Mark item as deleted
1266
     *
1267
     * @param Docman_Item $item 
1268
     *
1269
     * @return void
1270
     */
1271
    function delete($item) {
1272
        // The event must be processed before the item is deleted
1273
        $um         = UserManager::instance();
1274
        $user       = $um->getCurrentUser();
1275
        $itemParent = $this->getItemFromDb($item->getParentId());
1276
        $item->fireEvent('plugin_docman_event_del', $user, $itemParent);
1277
        
1278
        // Delete Lock if any
1279
        $lF = new Docman_LockFactory();
1280
        if($lF->itemIsLocked($item)) {
1281
            $lF->unlock($item);
1282
        }
1283
1284
        $item->setDeleteDate(time());
1285
        $this->delCutPreferenceForAllUsers($item->getId());
1286
        $this->delCopyPreferenceForAllUsers($item->getId());
1287
        $dao = $this->_getItemDao();
1288
        $dao->updateFromRow($item->toRow());
1289
        $dao->storeDeletedItem($item->getId());
1290
    }
1291
1292
    /**
1293
     * Delete Docman hierarchy for a given project
1294
     *
1295
     * @param Integer $groupId The project id
1296
     *
1297
     * @return Boolean success
1298
     */
1299
    public function deleteProjectTree($groupId) {
1300
        $deleteStatus = true;
1301
        $root = $this->getRoot($groupId);
1302
        if ($root) {
1303
            $dPm = Docman_PermissionsManager::instance($groupId);
1304
            $subItemsWritable = $dPm->currentUserCanWriteSubItems($root->getId());
1305
            if($subItemsWritable) {
1306
                $rootChildren = $this->getChildrenFromParent($root);
1307
                $user = $this->_getUserManager()->getCurrentUser();
1308
                foreach ($rootChildren as $children) {
1309
                    if (!$this->deleteSubTree($children, $user, true)) {
1310
                        $deleteStatus = false;
1311
                    }
1312
                }
1313
            } else {
1314
                $deleteStatus = false;
1315
            }
1316
        }
1317
        return $deleteStatus;
1318
    }
1319
1320
    /**
1321
     * Manage deletion of a entire item hierarchy.
1322
     * 
1323
     * It's the recommended and official way to delete a file in the docman
1324
     *
1325
     * @param Docman_Item $item        Item to delete
1326
     * @param PFUser        $user        User who performs the delete
1327
     * @param Boolean     $cascadeWiki If there are wiki documents, do we delete corresponding in wiki page too ?
1328
     * 
1329
     * @return Boolean success
1330
     */
1331
    public function deleteSubTree(Docman_Item $item, PFUser $user, $cascadeWiki) {
1332
        if($item && !$this->isRoot($item)) {
1333
            // Cannot delete one folder if at least on of the document inside
1334
            // cannot be deleted
1335
            $dPm = Docman_PermissionsManager::instance($item->getGroupId());
1336
            $subItemsWritable = $dPm->currentUserCanWriteSubItems($item->getId());
1337
            if($subItemsWritable) {
1338
                $itemSubTree = $this->getItemSubTree($item, $user, false, true);
1339
                if ($itemSubTree) {
1340
                    $deletor = new Docman_ActionsDeleteVisitor();
1341
                    if ($itemSubTree->accept($deletor, array('user'  => $user, 'cascadeWikiPageDeletion' => $cascadeWiki))) {
1342
                        return true;
1343
                    }
1344
                }
1345
            } else {
1346
                throw new RuntimeException($GLOBALS['Language']->getText('plugin_docman', 'error_item_not_deleted_no_w'));
1347
            }
1348
        }
1349
        return false;
1350
    }
1351
    
1352
    /**
1353
     * List pending items
1354
     *
1355
     * @param Integer $groupId
1356
     * @param Integer $offset
1357
     * @param Integer $limit
1358
     *
1359
     * @return Array
1360
     */
1361
    function listPendingItems($groupId, $offset, $limit) {
1362
        $dao = $this->_getItemDao();
1363
        return $dao->listPendingItems($groupId, $offset, $limit);
1364
    }
1365
1366
    /**
1367
     * Purge deleted items with delete date lower than the given time
1368
     *
1369
     * @param Integer $time
1370
     *
1371
     * @return Boolean
1372
     */
1373
    function purgeDeletedItems($time) {
1374
        $dao = $this->_getItemDao();
1375
        $dar = $dao->listItemsToPurge($time);
1376
        if ($dar && !$dar->isError()) {
1377
            foreach ($dar as $row) {
1378
                $item = new Docman_Item($row);
1379
                $this->purgeDeletedItem($item);
1380
            }
1381
            return true;
1382
        }
1383
        return false;
1384
    }
1385
1386
    /**
1387
     * Mark the deleted item as purged
1388
     *
1389
     * @param Docman_Item $item
1390
     *
1391
     * @return Boolean
1392
     */
1393
    public function purgeDeletedItem($item) {
1394
        $dao = $this->_getItemDao();
1395
        return $dao->setPurgeDate($item->getId(), time());
1396
    }
1397
1398
    /**
1399
     * Restore on item
1400
     * 
1401
     * @param Docman_Item $item
1402
     * 
1403
     * @return Boolean
1404
     */
1405
    public function restore($item) {
1406
        $dao         = $this->_getItemDao();
1407
        $type        = $this->getItemTypeForItem($item);
1408
        $oneRestored = false;
1409
        $isFile      = false;
1410
        if ($type == PLUGIN_DOCMAN_ITEM_TYPE_FILE || $type == PLUGIN_DOCMAN_ITEM_TYPE_EMBEDDEDFILE) {
1411
            $isFile      = true;
1412
            $vf          = $this->_getVersionFactory();
1413
            $versions    = $vf->listVersionsToPurgeForItem($item);
1414
            if ($versions) {
1415
                foreach ($versions as $version) {
1416
                    $oneRestored |= $vf->restore($version);
1417
                }
1418
            }
1419
        }
1420
1421
        if (!$isFile || $oneRestored) {
1422
            // Log the event
1423
            $user = $this->_getUserManager()->getCurrentUser();
1424
            $this->_getEventManager()->processEvent('plugin_docman_event_restore', array(
1425
                    'group_id'   => $item->getGroupId(),
1426
                    'item'       => $item,
1427
                    'user'       => $user)
1428
            );
1429
            return $dao->restore($item->getId());
1430
        }
1431
        return false;
1432
    }
1433
}
1434
1435
?>
1436