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 * http://fusionforge.org
11 * This file is part of FusionForge. FusionForge is free software;
12 * you can redistribute it and/or modify it under the terms of the
13 * GNU General Public License as published by the Free Software
14 * Foundation; either version 2 of the Licence, or (at your option)
17 * FusionForge is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License along
23 * with FusionForge; if not, write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 require_once $gfcommon.'include/Error.class.php';
29 class DocumentGroup extends Error {
41 * @var array $data_array.
46 * DocumentGroup - constructor.
48 * Use this constructor if you are modifying an existing doc_group.
50 * @param object Group object.
51 * @param array (all fields from doc_groups) OR doc_group id from database.
52 * @return boolean success.
55 function DocumentGroup(&$Group, $data = false) {
59 if (!$Group || !is_object($Group)) {
60 $this->setError(_('Document Directory: No Valid Project'));
63 //did Group have an error?
64 if ($Group->isError()) {
65 $this->setError(_('Document Directory:').' '.$Group->getErrorMessage());
68 $this->Group =& $Group;
71 if (is_array($data)) {
72 $this->data_array =& $data;
74 // should verify group_id
78 if (!$this->fetchData($data)) {
89 * create - create a new item in the database.
91 * @param string Item name.
92 * @return boolean on success / false on failure.
95 function create($name, $parent_doc_group = 0) {
100 $this->setError(_('DocumentGroup: Name is required'));
104 if ($parent_doc_group) {
105 // check if parent group exists
106 $res = db_query_params('SELECT * FROM doc_groups WHERE doc_group=$1 AND group_id=$2',
107 array($parent_doc_group, $this->Group->getID()));
108 if (!$res || db_numrows($res) < 1) {
109 $this->setError(_('DocumentGroup: Invalid Document Directory parent ID'));
113 $parent_doc_group = 0;
116 $perm =& $this->Group->getPermission();
117 if (!$perm || !$perm->isDocEditor()) {
118 $this->setPermissionDeniedError();
122 $res=db_query_params('SELECT * FROM doc_groups WHERE groupname=$1 AND parent_doc_group=$2 AND group_id=$3',
125 $this->Group->getID())
127 if ($res && db_numrows($res) > 0) {
128 $this->setError(_('Directory name already exists'));
132 $result = db_query_params('INSERT INTO doc_groups (group_id,groupname,parent_doc_group,stateid) VALUES ($1, $2, $3, $4)',
133 array ($this->Group->getID(),
134 htmlspecialchars($name),
138 if ($result && db_affected_rows($result) > 0) {
141 $this->setError(_('DocumentGroup::create() Error Adding Directory:').' '.db_error());
145 $doc_group = db_insertid($result, 'doc_groups', 'doc_group');
147 // Now set up our internal data structures
148 if (!$this->fetchData($doc_group)) {
156 * delete - delete a DocumentGroup.
157 * WARNING delete is recursive and permanent
158 * @param integer Document Group Id, integer Project Group Id
159 * @return boolean success
162 function delete($doc_groupid, $project_group_id) {
163 $perm =& $this->Group->getPermission();
164 if (!$perm || !$perm->isDocEditor()) {
165 $this->setPermissionDeniedError();
169 /* delete documents in directory */
170 $result = db_query_params('DELETE FROM doc_data where doc_group = $1 and group_id = $2',
171 array($doc_groupid, $project_group_id));
173 /* delete directory */
174 $result = db_query_params('DELETE FROM doc_groups where doc_group = $1 and group_id = $2',
175 array($doc_groupid, $project_group_id));
179 /* is there any subdir ? */
180 $subdir = db_query_params('select doc_group from doc_groups where parent_doc_group = $1 and group_id = $2',
181 array($doc_groupid, $project_group_id));
182 /* make a recursive call */
183 while ($arr = db_fetch_array($subdir)) {
184 $this->delete($arr['doc_group'], $project_group_id);
194 * injectArchive - extract the attachment and create the directory tree if needed
196 * @param array uploaded data
197 * @return boolean success or not
200 function injectArchive($uploaded_data) {
201 if (!is_uploaded_file($uploaded_data['tmp_name'])) {
202 $this->setError(_('Invalid file name.'));
205 if (function_exists('finfo_open')) {
206 $finfo = finfo_open(FILEINFO_MIME_TYPE);
207 $uploaded_data_type = finfo_file($finfo, $uploaded_data['tmp_name']);
209 $uploaded_data_type = $uploaded_data['type'];
212 switch ($uploaded_data_type) {
213 case "application/zip": {
214 $returned = $this->__injectZip($uploaded_data);
217 case "application/x-rar-compressed": {
218 $returned = $this->__injectRar($uploaded_data);
222 $this->setError( _('Unsupported injected file:') . ' ' .$uploaded_data_type);
230 * fetchData - re-fetch the data for this DocumentGroup from the database.
232 * @param integer ID of the doc_group.
233 * @return boolean success
236 function fetchData($id) {
237 $res = db_query_params('SELECT * FROM doc_groups WHERE doc_group=$1',
239 if (!$res || db_numrows($res) < 1) {
240 $this->setError(_('DocumentGroup: Invalid Document Directory ID'));
243 $this->data_array = db_fetch_array($res);
244 db_free_result($res);
249 * getGroup - get the Group Object this DocumentGroup is associated with.
251 * @return Object Group.
254 function &getGroup() {
259 * getID - get this DocumentGroup's ID.
261 * @return integer The id #.
265 return $this->data_array['doc_group'];
269 * getID - get parent DocumentGroup's id.
271 * @return integer The id #.
274 function getParentID() {
275 return $this->data_array['parent_doc_group'];
279 * getName - get the name.
281 * @return string The name.
285 return $this->data_array['groupname'];
289 * getState - get the state id.
291 * @return integer The state id.
294 function getState() {
295 return $this->data_array['stateid'];
299 * update - update a DocumentGroup.
301 * @param string Name of the category.
302 * @param integer the doc_group id of the parent. default = 0
303 * @return boolean success or not
306 function update($name, $parent_doc_group = 0) {
307 $perm =& $this->Group->getPermission();
308 if (!$perm || !$perm->isDocEditor()) {
309 $this->setPermissionDeniedError();
313 $this->setMissingParamsError();
317 if ($parent_doc_group) {
318 // check if parent group exists
319 $res = db_query_params('SELECT * FROM doc_groups WHERE doc_group=$1 AND group_id=$2',
320 array($parent_doc_group,
321 $this->Group->getID())
323 if (!$res || db_numrows($res) < 1) {
324 $this->setError(_('DocumentGroup: Invalid Document Directory parent ID'));
329 $res=db_query_params('SELECT * FROM doc_groups WHERE groupname=$1 AND parent_doc_group=$2 AND group_id=$3',
332 $this->Group->getID())
334 if ($res && db_numrows($res) > 0) {
335 $this->setError(_('Directory name already exists'));
339 $result = db_query_params('UPDATE doc_groups SET groupname=$1, parent_doc_group=$2 WHERE doc_group=$3 AND group_id=$4',
340 array(htmlspecialchars($name),
343 $this->Group->getID())
345 if ($result && db_affected_rows($result) > 0) {
346 $this->fetchData($this->getID()) ;
349 $this->setOnUpdateError(_('DocumentGroup:').' '.db_error());
355 * hasDocuments - Recursive function that checks if this group or any of it childs has documents associated to it
357 * A group has associated documents if and only if there are documents associated to this
358 * group or to any of its childs
360 * @param array Array of nested groups information, fetched from DocumentGroupFactory class
361 * @param object The DocumentFactory object
362 * @param int (optional) State of the documents
363 * @return boolean success
366 function hasDocuments(&$nested_groups, &$document_factory, $stateid = 0) {
367 $doc_group_id = $this->getID();
368 static $result = array(); // this function will probably be called several times so we better store results in order to speed things up
369 if (!array_key_exists($stateid, $result) || !is_array($result[$stateid]))
370 $result[$stateid] = array();
372 if (array_key_exists($doc_group_id, $result[$stateid]))
373 return $result[$stateid][$doc_group_id];
375 // check if it has documents
377 $document_factory->setStateID($stateid);
379 $document_factory->setDocGroupID($doc_group_id);
380 $docs = $document_factory->getDocuments();
381 if (is_array($docs) && count($docs) > 0) { // this group has documents
382 $result[$stateid][$doc_group_id] = true;
386 // this group doesn't have documents... check recursively on the childs
387 if (array_key_exists($doc_group_id, $nested_groups) && is_array($nested_groups[$doc_group_id])) {
388 $count = count($nested_groups[$doc_group_id]);
389 for ($i=0; $i < $count; $i++) {
390 if ($nested_groups[$doc_group_id][$i]->hasDocuments($nested_groups, $document_factory, $stateid)) {
391 // child has documents
392 $result[$stateid][$doc_group_id] = true;
396 // no child has documents, then this group doesn't have associated documents
397 $result[$stateid][$doc_group_id] = false;
399 } else { // this group doesn't have childs
400 $result[$stateid][$doc_group_id] = false;
406 * hasSubgroup - Checks if this group has a specified subgroup associated to it
408 * @param array Array of nested groups information, fetched from DocumentGroupFactory class
409 * @param int ID of the subgroup
410 * @return boolean success
413 function hasSubgroup(&$nested_groups, $doc_subgroup_id) {
414 $doc_group_id = $this->getID();
416 if (is_array(@$nested_groups[$doc_group_id])) {
417 $count = count($nested_groups[$doc_group_id]);
418 for ($i=0; $i < $count; $i++) {
420 if ($nested_groups[$doc_group_id][$i]->getID() == $doc_subgroup_id) {
423 // recursively check if this child has this subgroup
424 if ($nested_groups[$doc_group_id][$i]->hasSubgroup($nested_groups, $doc_subgroup_id)) {
435 * getPath - return the complete_path
437 * @return string the complete_path
442 if ($this->getParentID()) {
443 $parentDg = new DocumentGroup($this->Group,$this->getParentID());
444 $returnPath = $parentDg->getPath();
446 return $returnPath.'/'.$this->getName();
450 * setStateID - set the state id of this document group.
452 * @param int State ID.
453 * @return boolean success or not.
456 function setStateID($stateid) {
457 return $this->__setValueinDB('stateid',$stateid);
461 * setParentDocGroupId - set the parent doc_group id of this document group.
463 * @param int Parent Doc_group Id.
464 * @return boolean success or not.
467 function setParentDocGroupId($parentDocGroupId) {
468 return $this->__setValueinDB('parent_doc_group',$parentDocGroupId);
472 * __injectZip - private method to inject a zip archive tree and files
474 * @param array uploaded zip
475 * @return boolean success or not
478 private function __injectZip($uploadedZip) {
479 $zip = new ZipArchive();
480 if ($zip->open($uploadedZip['tmp_name'])) {
481 $extractDir = forge_get_config('data_path').'/'.uniqid();
482 if ($zip->extractTo($extractDir)) {
484 if ($this->__injectContent($extractDir)) {
488 $this->setError(_('Unable inject zipfile.'));
492 $this->setError(_('Unable to extract zipfile.'));
497 $this->setError(_('Unable to open zipfile.'));
502 * __injectRar - private method to inject a rar archive tree and files
504 * @param array uploaded rar
505 * @return boolean success or not
508 private function __injectRar($uploadedRar) {
513 * __injectContent - private method to inject a directory tree and files
515 * @param string the directory to inject
516 * @return boolean success or not
519 private function __injectContent($directory) {
520 if (is_dir($directory)) {
521 $dir_arr = scandir($directory);
522 for ($i = 0; $i < count($dir_arr); $i++) {
523 if ($dir_arr[$i] != '.' && $dir_arr[$i] != '..') {
524 if (is_dir($directory.'/'.$dir_arr[$i])) {
525 if ($this->create($dir_arr[$i], $this->getID())) {
526 if (!$this->__injectContent($directory.'/'.$dir_arr[$i])) {
527 $this->setError(_('Unable to open directory for inject into tree'));
530 rmdir($directory.'/'.$dir_arr[$i]);
534 $d = new Document($this->getGroup());
535 if (function_exists('finfo_open')) {
536 $finfo = finfo_open(FILEINFO_MIME_TYPE);
537 $dir_arr_type = finfo_file($finfo, $directory.'/'.$dir_arr[$i]);
539 $dir_arr_type = 'application/binary';
541 $data = fread(fopen($directory.'/'.$dir_arr[$i], 'r'), filesize($directory.'/'.$dir_arr[$i]));
542 if (!$d->create($dir_arr[$i], $dir_arr_type, $data, $this->getID(), 'no title', 'no description')) {
543 $this->setError(_('Unable to add document from zip injection.'));
546 unlink($directory.'/'.$dir_arr[$i]);
553 $this->setError(_('Unable to open directory for inject into tree'));
558 * __setValueinDB - private function to update columns in db
560 * @param string the column to update
561 * @param int the value to store
562 * @return boolean success or not
565 private function __setValueinDB($column, $value) {
568 case "parent_doc_group": {
569 $qpa = db_construct_qpa();
570 $qpa = db_construct_qpa($qpa, 'UPDATE doc_groups SET ');
571 $qpa = db_construct_qpa($qpa, $column);
572 $qpa = db_construct_qpa($qpa, '=$1
576 $this->Group->getID(),
578 $res = db_query_qpa($qpa);
579 if (!$res || db_affected_rows($res) < 1) {
580 $this->setOnUpdateError(db_error().print_r($res));
586 $this->setOnUpdateError(_('wrong column name'));
595 // c-file-style: "bsd"