3 * FusionForge document manager
5 * Copyright 2000, Quentin Cregan/Sourceforge
6 * Copyright 2002, Tim Perdue/GForge, LLC
7 * Copyright 2009, Roland Mas
8 * Copyright 2010, Franck Villaume - Capgemini
9 * Copyright (C) 2011 Alain Peyrat - Alcatel-Lucent
10 * http://fusionforge.org
12 * This file is part of FusionForge. FusionForge is free software;
13 * you can redistribute it and/or modify it under the terms of the
14 * GNU General Public License as published by the Free Software
15 * Foundation; either version 2 of the Licence, or (at your option)
18 * FusionForge is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License along
24 * with FusionForge; if not, write to the Free Software Foundation, Inc.,
25 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 require_once $gfcommon.'include/Error.class.php';
30 class DocumentGroup extends Error {
42 * @var array $data_array.
47 * DocumentGroup - constructor.
49 * Use this constructor if you are modifying an existing doc_group.
51 * @param object Group object.
52 * @param array (all fields from doc_groups) OR doc_group id from database.
53 * @return boolean True on success.
56 function DocumentGroup(&$Group, $data = false) {
60 if (!$Group || !is_object($Group)) {
61 $this->setError(_('Document Directory: No Valid Project'));
64 //did Group have an error?
65 if ($Group->isError()) {
66 $this->setError(_('Document Directory:').' '.$Group->getErrorMessage());
69 $this->Group =& $Group;
72 if (is_array($data)) {
73 $this->data_array =& $data;
74 if ($this->data_array['group_id'] != $this->Group->getID()) {
75 $this->setError('DocumentGroup:: '. _('Group_id in db result does not match Group Object'));
76 $this->data_array = null;
81 if (!$this->fetchData($data)) {
92 * create - create a new item in the database.
94 * @param string Item name.
95 * @return boolean on success / false on failure.
98 function create($name, $parent_doc_group = 0) {
103 $this->setError(_('Name is required'));
107 if ($parent_doc_group) {
108 // check if parent group exists
109 $res = db_query_params('SELECT * FROM doc_groups WHERE doc_group=$1 AND group_id=$2',
110 array($parent_doc_group, $this->Group->getID()));
111 if (!$res || db_numrows($res) < 1) {
112 $this->setError(_('Invalid Documents folder parent ID'));
116 $parent_doc_group = 0;
119 if ($parent_doc_group || $name != 'Uncategorized Submissions') {
120 $perm =& $this->Group->getPermission();
121 if (!$perm || !$perm->isDocEditor()) {
122 $this->setPermissionDeniedError();
127 $res = db_query_params('SELECT * FROM doc_groups WHERE groupname=$1 AND parent_doc_group=$2 AND group_id=$3',
130 $this->Group->getID())
132 if ($res && db_numrows($res) > 0) {
133 $this->setError(_('Folder name already exists'));
137 $result = db_query_params('INSERT INTO doc_groups (group_id,groupname,parent_doc_group,stateid) VALUES ($1, $2, $3, $4)',
138 array ($this->Group->getID(),
139 htmlspecialchars($name),
143 if ($result && db_affected_rows($result) > 0) {
146 $this->setError(_('Error Adding Folder:').' '.db_error());
150 $doc_group = db_insertid($result, 'doc_groups', 'doc_group');
152 // Now set up our internal data structures
153 if (!$this->fetchData($doc_group)) {
161 * delete - delete a DocumentGroup.
162 * WARNING delete is recursive and permanent
163 * @param integer Document Group Id, integer Project Group Id
164 * @return boolean success
167 function delete($doc_groupid, $project_group_id) {
168 $perm =& $this->Group->getPermission();
169 if (!$perm || !$perm->isDocEditor()) {
170 $this->setPermissionDeniedError();
174 /* delete documents in directory */
175 $result = db_query_params('DELETE FROM doc_data where doc_group = $1 and group_id = $2',
176 array($doc_groupid, $project_group_id));
178 /* delete directory */
179 $result = db_query_params('DELETE FROM doc_groups where doc_group = $1 and group_id = $2',
180 array($doc_groupid, $project_group_id));
184 /* is there any subdir ? */
185 $subdir = db_query_params('select doc_group from doc_groups where parent_doc_group = $1 and group_id = $2',
186 array($doc_groupid, $project_group_id));
187 /* make a recursive call */
188 while ($arr = db_fetch_array($subdir)) {
189 $this->delete($arr['doc_group'], $project_group_id);
199 * injectArchive - extract the attachment and create the directory tree if needed
201 * @param array uploaded data
202 * @return boolean success or not
205 function injectArchive($uploaded_data) {
206 if (!is_uploaded_file($uploaded_data['tmp_name'])) {
207 $this->setError(_('Invalid file name.'));
210 if (function_exists('finfo_open')) {
211 $finfo = finfo_open(FILEINFO_MIME_TYPE);
212 $uploaded_data_type = finfo_file($finfo, $uploaded_data['tmp_name']);
214 $uploaded_data_type = $uploaded_data['type'];
217 switch ($uploaded_data_type) {
218 case "application/zip": {
219 $returned = $this->__injectZip($uploaded_data);
222 case "application/x-rar-compressed": {
223 $returned = $this->__injectRar($uploaded_data);
227 $this->setError( _('Unsupported injected file:') . ' ' .$uploaded_data_type);
235 * fetchData - re-fetch the data for this DocumentGroup from the database.
237 * @param integer ID of the doc_group.
238 * @return boolean success
241 function fetchData($id) {
242 $res = db_query_params('SELECT * FROM doc_groups WHERE doc_group = $1 and group_id = $2',
243 array($id, $this->Group->getID()));
244 if (!$res || db_numrows($res) < 1) {
245 $this->setError(_('Invalid Document Folder ID'));
248 $this->data_array = db_fetch_array($res);
249 db_free_result($res);
254 * getGroup - get the Group Object this DocumentGroup is associated with.
256 * @return Object Group.
259 function &getGroup() {
264 * getID - get this DocumentGroup's ID.
266 * @return integer The id #.
270 return $this->data_array['doc_group'];
274 * getID - get parent DocumentGroup's id.
276 * @return integer The id #.
279 function getParentID() {
280 return $this->data_array['parent_doc_group'];
284 * getName - get the name.
286 * @return string The name.
290 return $this->data_array['groupname'];
294 * getState - get the state id.
296 * @return integer The state id.
299 function getState() {
300 return $this->data_array['stateid'];
304 * update - update a DocumentGroup.
306 * @param string Name of the category.
307 * @param integer the doc_group id of the parent. default = 0
308 * @return boolean success or not
311 function update($name, $parent_doc_group = 0) {
312 $perm =& $this->Group->getPermission();
313 if (!$perm || !$perm->isDocEditor()) {
314 $this->setPermissionDeniedError();
318 $this->setMissingParamsError();
322 if ($parent_doc_group) {
323 // check if parent group exists
324 $res = db_query_params('SELECT * FROM doc_groups WHERE doc_group=$1 AND group_id=$2',
325 array($parent_doc_group,
326 $this->Group->getID())
328 if (!$res || db_numrows($res) < 1) {
329 $this->setError(_('Invalid Documents Folder parent ID'));
334 $res=db_query_params('SELECT * FROM doc_groups WHERE groupname=$1 AND parent_doc_group=$2 AND group_id=$3',
337 $this->Group->getID())
339 if ($res && db_numrows($res) > 0) {
340 $this->setError(_('Documents Folder name already exists'));
344 $result = db_query_params('UPDATE doc_groups SET groupname=$1, parent_doc_group=$2 WHERE doc_group=$3 AND group_id=$4',
345 array(htmlspecialchars($name),
348 $this->Group->getID())
350 if ($result && db_affected_rows($result) > 0) {
351 $this->fetchData($this->getID()) ;
354 $this->setOnUpdateError(_('DocumentGroup:').' '.db_error());
360 * hasDocuments - Recursive function that checks if this group or any of it childs has documents associated to it
362 * A group has associated documents if and only if there are documents associated to this
363 * group or to any of its childs
365 * @param array Array of nested groups information, fetched from DocumentGroupFactory class
366 * @param object The DocumentFactory object
367 * @param int (optional) State of the documents
368 * @return boolean success
371 function hasDocuments(&$nested_groups, &$document_factory, $stateid = 0) {
372 $doc_group_id = $this->getID();
373 static $result = array(); // this function will probably be called several times so we better store results in order to speed things up
374 if (!array_key_exists($stateid, $result) || !is_array($result[$stateid]))
375 $result[$stateid] = array();
377 if (array_key_exists($doc_group_id, $result[$stateid]))
378 return $result[$stateid][$doc_group_id];
380 // check if it has documents
382 $document_factory->setStateID($stateid);
384 $document_factory->setDocGroupID($doc_group_id);
385 $docs = $document_factory->getDocuments();
386 if (is_array($docs) && count($docs) > 0) { // this group has documents
387 $result[$stateid][$doc_group_id] = true;
391 // this group doesn't have documents... check recursively on the childs
392 if (array_key_exists($doc_group_id, $nested_groups) && is_array($nested_groups[$doc_group_id])) {
393 $count = count($nested_groups[$doc_group_id]);
394 for ($i=0; $i < $count; $i++) {
395 if ($nested_groups[$doc_group_id][$i]->hasDocuments($nested_groups, $document_factory, $stateid)) {
396 // child has documents
397 $result[$stateid][$doc_group_id] = true;
401 // no child has documents, then this group doesn't have associated documents
402 $result[$stateid][$doc_group_id] = false;
404 } else { // this group doesn't have childs
405 $result[$stateid][$doc_group_id] = false;
410 function getNumberOfDocuments($stateId = 1) {
411 $res = db_query_params('select count(*) from docdata_vw where doc_group = $1 and group_id = $2 and stateid = $3',
412 array($this->getID(), $this->Group->getID(), $stateId));
416 $arr = db_fetch_array($res);
421 * hasSubgroup - Checks if this group has a specified subgroup associated to it
423 * @param array Array of nested groups information, fetched from DocumentGroupFactory class
424 * @param int ID of the subgroup
425 * @return boolean success
428 function hasSubgroup(&$nested_groups, $doc_subgroup_id) {
429 $doc_group_id = $this->getID();
431 if (is_array(@$nested_groups[$doc_group_id])) {
432 $count = count($nested_groups[$doc_group_id]);
433 for ($i=0; $i < $count; $i++) {
435 if ($nested_groups[$doc_group_id][$i]->getID() == $doc_subgroup_id) {
438 // recursively check if this child has this subgroup
439 if ($nested_groups[$doc_group_id][$i]->hasSubgroup($nested_groups, $doc_subgroup_id)) {
448 function getSubgroup($docGroupId, $stateId = 1) {
449 $returnArr = array();
450 $res = db_query_params('SELECT doc_group from doc_groups where parent_doc_group = $1 and stateid = $2 and group_id = $3 order by groupname',
451 array($docGroupId, $stateId, $this->Group->getID()));
456 while ($row = db_fetch_array($res)) {
457 $returnArr[] = $row['doc_group'];
464 * getPath - return the complete_path
466 * @param boolean does path is url clickable (default is false)
467 * @param boolean does path include this document group name ? (default is true)
468 * @return string the complete_path
471 function getPath($url = false, $includename = true) {
473 if ($this->getParentID()) {
474 $parentDg = new DocumentGroup($this->Group, $this->getParentID());
475 $returnPath = $parentDg->getPath($url);
479 $returnPath .= '/'.util_make_link('/docman/?group_id='.$this->Group->getID().'&view=listfile&dirid='.$this->getID(),$this->getName(), array('title' => _('Browse this folder'), 'class' => 'tabtitle'));
481 $returnPath .= '/'.$this->getName();
484 if (!sizeof($returnPath))
491 * setStateID - set the state id of this document group.
493 * @param int State ID.
494 * @return boolean success or not.
497 function setStateID($stateid) {
498 return $this->__setValueinDB('stateid', $stateid);
502 * setParentDocGroupId - set the parent doc_group id of this document group.
504 * @param int Parent Doc_group Id.
505 * @return boolean success or not.
508 function setParentDocGroupId($parentDocGroupId) {
509 return $this->__setValueinDB('parent_doc_group', $parentDocGroupId);
513 * __injectZip - private method to inject a zip archive tree and files
515 * @param array uploaded zip
516 * @return boolean success or not
519 private function __injectZip($uploadedZip) {
520 $zip = new ZipArchive();
521 if ($zip->open($uploadedZip['tmp_name'])) {
522 $extractDir = forge_get_config('data_path').'/'.uniqid();
523 if ($zip->extractTo($extractDir)) {
525 if ($this->__injectContent($extractDir)) {
529 $this->setError(_('Unable inject zipfile.'));
533 $this->setError(_('Unable to extract zipfile.'));
538 $this->setError(_('Unable to open zipfile.'));
543 * __injectRar - private method to inject a rar archive tree and files
545 * @param array uploaded rar
546 * @return boolean success or not
549 private function __injectRar($uploadedRar) {
554 * __injectContent - private method to inject a directory tree and files
556 * @param string the directory to inject
557 * @return boolean success or not
560 private function __injectContent($directory) {
561 if (is_dir($directory)) {
562 $dir_arr = scandir($directory);
563 for ($i = 0; $i < count($dir_arr); $i++) {
564 if ($dir_arr[$i] != '.' && $dir_arr[$i] != '..') {
565 if (is_dir($directory.'/'.$dir_arr[$i])) {
566 if ($this->create($dir_arr[$i], $this->getID())) {
567 if (!$this->__injectContent($directory.'/'.$dir_arr[$i])) {
568 $this->setError(_('Unable to open directory for inject into tree'));
571 rmdir($directory.'/'.$dir_arr[$i]);
575 $d = new Document($this->getGroup());
576 if (function_exists('finfo_open')) {
577 $finfo = finfo_open(FILEINFO_MIME_TYPE);
578 $dir_arr_type = finfo_file($finfo, $directory.'/'.$dir_arr[$i]);
580 $dir_arr_type = 'application/binary';
582 $data = fread(fopen($directory.'/'.$dir_arr[$i], 'r'), filesize($directory.'/'.$dir_arr[$i]));
583 if (!$d->create($dir_arr[$i], $dir_arr_type, $data, $this->getID(), 'no title', 'no description')) {
584 $this->setError(_('Unable to add document from zip injection.'));
587 unlink($directory.'/'.$dir_arr[$i]);
594 $this->setError(_('Unable to open directory for inject into tree'));
599 * __setValueinDB - private function to update columns in db
601 * @param string the column to update
602 * @param int the value to store
603 * @return boolean success or not
606 private function __setValueinDB($column, $value) {
609 case "parent_doc_group": {
610 $qpa = db_construct_qpa();
611 $qpa = db_construct_qpa($qpa, 'UPDATE doc_groups SET ');
612 $qpa = db_construct_qpa($qpa, $column);
613 $qpa = db_construct_qpa($qpa, '=$1
617 $this->Group->getID(),
619 $res = db_query_qpa($qpa);
620 if (!$res || db_affected_rows($res) < 1) {
621 $this->setOnUpdateError(db_error().print_r($res));
627 $this->setOnUpdateError(_('wrong column name'));
636 // c-file-style: "bsd"