3 * FusionForge document manager
5 * Copyright 2000, Quentin Cregan/Sourceforge
6 * Copyright 2002-2003, Tim Perdue/GForge, LLC
7 * Copyright 2009, Roland Mas
8 * Copyright 2010-2011, Franck Villaume - Capgemini
9 * Copyright 2011-2012, Franck Villaume - TrivialDev
10 * Copyright (C) 2011-2012 Alain Peyrat - Alcatel-Lucent
11 * http://fusionforge.org
13 * This file is part of FusionForge. FusionForge is free software;
14 * you can redistribute it and/or modify it under the terms of the
15 * GNU General Public License as published by the Free Software
16 * Foundation; either version 2 of the Licence, or (at your option)
19 * FusionForge is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License along
25 * with FusionForge; if not, write to the Free Software Foundation, Inc.,
26 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 require_once $gfcommon.'include/Error.class.php';
30 require_once $gfcommon.'docman/Parsedata.class.php';
31 require_once $gfcommon.'docman/DocumentManager.class.php';
32 require_once $gfcommon.'docman/DocumentGroup.class.php';
33 require_once $gfcommon.'docman/DocumentStorage.class.php';
36 class Document extends Error {
39 * Associative array of data from db.
41 * @var array $data_array.
55 * @param object The Group object to which this document is associated.
56 * @param int The docid.
57 * @param array The associative array of data.
58 * @return boolean success.
60 function __construct(&$Group, $docid = false, $arr = false) {
62 if (!$Group || !is_object($Group)) {
63 $this->setNotValidGroupObjectError();
66 if ($Group->isError()) {
67 $this->setError('Document:: '. $Group->getErrorMessage());
70 $this->Group =& $Group;
73 if (!$arr || !is_array($arr)) {
74 if (!$this->fetchData($docid)) {
78 $this->data_array =& $arr;
79 if ($this->data_array['group_id'] != $this->Group->getID()) {
80 $this->setError('Document:: '. _('Group_id in db result does not match Group Object'));
81 $this->data_array = null;
85 if (!$this->isPublic()) {
86 $perm =& $this->Group->getPermission();
88 if (!$perm || !is_object($perm) || !$perm->isDocEditor()) {
89 $this->setPermissionDeniedError();
90 $this->data_array = null;
98 * create - use this function to create a new entry in the database.
100 * @param string The filename of this document. Can be a URL.
101 * @param string The filetype of this document. If filename is URL, this should be 'URL';
102 * @param string The contents of this document.
103 * @param int The doc_group id of the doc_groups table.
104 * @param string The title of this document.
105 * @param string The description of this document.
106 * @param int The state id of the document. At creation, can not be deleted status.
107 * @return boolean success.
109 function create($filename, $filetype, $data, $doc_group, $title, $description, $stateid = 0) {
110 if (strlen($title) < 5) {
111 $this->setError(_('Title Must Be At Least 5 Characters'));
114 if (strlen($description) < 10) {
115 $this->setError(_('Document Description Must Be At Least 10 Characters'));
119 $user_id = ((session_loggedin()) ? user_getid() : 100);
121 $doc_initstatus = '3';
122 $perm =& $this->Group->getPermission();
123 if ($perm && is_object($perm) && $perm->isDocEditor()) {
124 if ($stateid && $stateid != 2) {
125 $doc_initstatus = $stateid;
127 $doc_initstatus = '1';
131 $result = db_query_params('SELECT filename, doc_group from docdata_vw
135 array($filename, $doc_group, $doc_initstatus));
137 if (!$result || db_numrows($result) > 0) {
138 $this->setError(_('Document already published in this directory'));
142 $result = db_query_params('SELECT title FROM docdata_vw where title = $1 AND doc_group = $2',
143 array($title, $doc_group));
144 if (!$result || db_numrows($result) > 0) {
145 $this->setError(_('Document already published in this directory'));
149 // If $filetype is "text/plain", $body convert UTF-8 encoding.
150 if (strcasecmp($filetype, "text/plain") === 0 &&
151 function_exists('mb_convert_encoding') &&
152 function_exists('mb_detect_encoding')) {
153 $data = mb_convert_encoding($data, 'UTF-8', mb_detect_encoding($data));
156 $filesize = filesize($data);
157 if (!$filesize) { $filesize = 0 ; }
159 // key words for in-document search
160 if ($this->Group->useDocmanSearch() && $filesize ) {
161 $kw = new Parsedata();
162 $kwords = $kw->get_parse_data($data, htmlspecialchars($title), htmlspecialchars($description), $filetype, $filename);
168 $result = db_query_params('INSERT INTO doc_data (group_id, title, description, createdate, doc_group,
169 stateid, filename, filetype, filesize, data_words, created_by)
170 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)',
171 array($this->Group->getId(),
172 htmlspecialchars($title),
173 htmlspecialchars($description),
184 $docid = db_insertid($result, 'doc_data', 'docid');
186 DocumentStorage::instance()->store($docid, $data);
189 if (!$result || !$docid) {
190 $this->setError(_('Error Adding Document:').' '.db_error().$result);
192 DocumentStorage::instance()->rollback();
198 if (!$this->fetchData($docid)) {
199 $this->setError(_('Error fetching Document'));
201 DocumentStorage::instance()->rollback();
207 $localDg = new DocumentGroup($this->Group, $doc_group);
208 if (!$localDg->update($localDg->getName(), $localDg->getParentID(), 1)) {
209 $this->setError(_('Error updating document group:').$localDg->getErrorMessage());
211 DocumentStorage::instance()->rollback();
216 $this->sendNotice(true);
219 DocumentStorage::instance()->commit();
225 * fetchData() - re-fetch the data for this document from the database.
227 * @param int The document id.
228 * @return boolean success
230 function fetchData($docid) {
231 $res = db_query_params('SELECT * FROM docdata_vw WHERE docid=$1 AND group_id=$2',
232 array($docid, $this->Group->getID()));
233 if (!$res || db_numrows($res) < 1) {
234 $this->setError(_('Document: Invalid docid'));
237 $this->data_array = db_fetch_array($res);
238 db_free_result($res);
243 * getGroup - get the Group object this Document is associated with.
245 * @return Object The Group object.
247 function &getGroup() {
252 * getID - get this docid.
254 * @return int The docid.
257 return $this->data_array['docid'];
261 * getName - get the name of this document.
263 * @return string The name of this document.
266 return $this->data_array['title'];
270 * getDescription - the description of this document.
272 * @return string The description.
274 function getDescription() {
275 return $this->data_array['description'];
279 * isURL - whether this document is a URL and not a local file.
281 * @return boolean is_url.
284 return ($this->data_array['filetype'] == 'URL');
288 * isText - whether this document is a text document and not a binary one.
290 * @return boolean is_text.
293 $doctype = $this->data_array['filetype'];
294 if (preg_match('|^text/|i', $doctype)) { // text plain, text html, text x-patch, etc
301 * isHtml - whether this document is a html document.
303 * @return boolean is_html.
306 $doctype = $this->data_array['filetype'];
307 if (preg_match('/html/i',$doctype)) {
314 * isPublic - whether this document is available to the general public.
316 * @return boolean is_public.
318 function isPublic() {
319 return (($this->data_array['stateid'] == 1) ? true : false);
323 * getStateID - get this stateid.
325 * @return int The stateid.
327 function getStateID() {
328 return $this->data_array['stateid'];
332 * getStateName - the statename of this document.
334 * @return string The statename.
336 function getStateName() {
337 return $this->data_array['state_name'];
341 * getDocGroupID - get this doc_group_id.
343 * @return int The doc_group_id.
345 function getDocGroupID() {
346 return $this->data_array['doc_group'];
350 * getDocGroupName - the doc_group_name of this document.
352 * @return string The docgroupname.
354 function getDocGroupName() {
355 return $this->data_array['group_name'];
359 * getCreatorID - get this creator's user_id.
361 * @return int The user_id.
363 function getCreatorID() {
364 return $this->data_array['created_by'];
368 * getCreatorUserName - the unix name of the person who created this document.
370 * @return string The unix name of the creator.
372 function getCreatorUserName() {
373 return $this->data_array['user_name'];
377 * getCreatorRealName - the real name of the person who created this document.
379 * @return string The real name of the creator.
381 function getCreatorRealName() {
382 return $this->data_array['realname'];
386 * getCreatorEmail - the email of the person who created this document.
388 * @return string The email of the creator.
390 function getCreatorEmail() {
391 return $this->data_array['email'];
395 * getFileName - the filename of this document.
397 * @return string The filename.
399 function getFileName() {
400 return $this->data_array['filename'];
404 * getFileType - the filetype of this document.
406 * @return string The filetype.
408 function getFileType() {
409 return $this->data_array['filetype'];
413 * getFileData - the filedata of this document.
415 * @return string The filedata.
417 function getFileData() {
418 return file_get_contents(DocumentStorage::instance()->get($this->getID()));
422 * getFileSize - Return the size of the document
424 * @return int The file size
426 function getFileSize() {
427 return $this->data_array['filesize'];
431 * getUpdated - get the time this document was updated.
433 * @return int The epoch date this document was updated.
435 function getUpdated() {
436 return $this->data_array['updatedate'];
440 * getCreated - get the time this document was created.
442 * @return int The epoch date this document was created.
444 function getCreated() {
445 return $this->data_array['createdate'];
449 * getLocked - get the lock status of this document.
451 * @return int The lock status of this document.
453 function getLocked() {
454 return $this->data_array['locked'];
458 * getLockdate - get the lock time of this document.
460 * @return int The lock time of this document.
462 function getLockdate() {
463 return $this->data_array['lockdate'];
467 * getLockedBy - get the user id who set lock on this document.
469 * @return int The user id who set lock on this document.
471 function getLockedBy() {
472 return $this->data_array['locked_by'];
476 * getReservedBy - get the owner of the reversed status of this document.
478 * @return int The owner of the reversed status of this document.
480 function getReservedBy() {
481 return $this->data_array['reserved_by'];
485 * getReserved - get the reversed status of this document.
487 * @return int The reversed status of this document.
489 function getReserved() {
490 return $this->data_array['reserved'];
494 * getMonitoredUserEmailAddress - get the email addresses of users who monitor this file
496 * @return string The list of emails comma separated
498 function getMonitoredUserEmailAddress() {
499 $result = db_query_params('select users.email from users,docdata_monitored_docman where users.user_id = docdata_monitored_docman.user_id and docdata_monitored_docman.doc_id = $1', array ($this->getID()));
500 if (!$result || db_numrows($result) < 1) {
506 while ($arr = db_fetch_array($result)) {
510 $values .= $comma.$arr['email'];
518 * isMonitoredBy - get the monitored status of this document for a specific user id.
521 * @return boolean true if monitored by this user
523 function isMonitoredBy($userid = 'ALL') {
524 if ( $userid == 'ALL' ) {
527 $condition = 'user_id='.$userid.' AND';
529 $result = db_query_params('SELECT * FROM docdata_monitored_docman WHERE '.$condition.' doc_id=$1',
530 array($this->getID()));
532 if (!$result || db_numrows($result) < 1)
539 * removeMonitoredBy - remove this document for a specific user id for monitoring.
542 * @return boolean true if success
544 function removeMonitoredBy($userid) {
545 $result = db_query_params('DELETE FROM docdata_monitored_docman WHERE doc_id=$1 AND user_id=$2',
546 array($this->getID(), $userid));
549 $this->setError(_('Unable To Remove Monitor').' : '.db_error());
556 * addMonitoredBy - add this document for a specific user id for monitoring.
559 * @return boolean true if success
561 function addMonitoredBy($userid) {
562 $result = db_query_params('SELECT * FROM docdata_monitored_docman WHERE user_id=$1 AND doc_id=$2',
563 array($userid, $this->getID()));
565 if (!$result || db_numrows($result) < 1) {
566 $result = db_query_params('INSERT INTO docdata_monitored_docman (doc_id,user_id) VALUES ($1,$2)',
567 array($this->getID(), $userid));
570 $this->setError(_('Unable To Add Monitor').' : '.db_error());
578 * clearMonitor - remove all entries of monitoring for this document.
580 * @return boolean true if success.
582 function clearMonitor() {
583 $result = db_query_params('DELETE FROM docdata_monitored_docman WHERE doc_id = $1',
584 array($this->getID()));
586 $this->setError(_('Unable To Clear Monitor').' : '.db_error());
593 * setState - set the stateid of the document.
595 * @param int The state id of the doc_states table.
596 * @return boolean success or not.
598 function setState($stateid) {
599 return $this->setValueinDB('stateid', $stateid);
604 * setDocGroupID - set the doc_group of the document.
606 * @param int The group_id of this document.
607 * @return boolean success or not.
609 function setDocGroupID($newdocgroupid) {
610 return $this->setValueinDB('doc_group', $newdocgroupid);
614 * setLock - set the locking status of the document.
616 * @param int The status of the lock.
617 * @param int The userid who set the lock.
618 * @param time the epoch time.
619 * @return boolean success or not.
621 function setLock($stateLock, $userid = NULL, $thistime = 0) {
622 $res = db_query_params('UPDATE doc_data SET
631 $this->Group->getID(),
634 if (!$res || db_affected_rows($res) < 1) {
635 $this->setOnUpdateError(_('Document lock failed').' '.db_error());
638 $this->data_array['locked'] = $stateLock;
639 $this->data_array['locked_by'] = $userid;
640 $this->data_array['lockdate'] = $thistime;
645 * setReservedBy - set the reserved status of the document and the owner
647 * @param int The status of the reserved
648 * @param int The ID of the owner : by default : noone
649 * @return boolean success
651 function setReservedBy($statusReserved, $idReserver = NULL) {
652 $res = db_query_params('UPDATE doc_data SET
657 array($statusReserved,
659 $this->Group->getID(),
662 if (!$res || db_affected_rows($res) < 1) {
663 $this->setOnUpdateError(_('Document reservation failed').' '.db_error());
666 $this->sendNotice(false);
671 * getFileTypeImage - return the file image for icon
673 * @return string the file image name
676 function getFileTypeImage() {
677 switch ($this->getFileType()) {
682 case "image/vnd.microsoft.icon":
683 case "image/svg+xml": {
684 $image = 'docman/file_type_image.png';
688 case "audio/x-vorbis+ogg":
690 $image = "docman/file_type_sound.png";
693 case "application/pdf": {
694 $image = 'docman/file_type_pdf.png';
699 $image = 'docman/file_type_html.png';
704 case "application/xml":
706 case "text/x-diff": {
707 $image = 'docman/file_type_plain.png';
710 case "application/msword":
711 case "application/vnd.openxmlformats-officedocument.wordprocessingml.document":
712 case "application/vnd.oasis.opendocument.text": {
713 $image = 'docman/file_type_writer.png';
716 case "application/vnd.ms-excel":
717 case "application/vnd.oasis.opendocument.spreadsheet":
718 case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": {
719 $image = 'docman/file_type_spreadsheet.png';
722 case "application/vnd.oasis.opendocument.presentation":
723 case "application/vnd.ms-powerpoint":
724 case "application/vnd.ms-office":
725 case "application/vnd.openxmlformats-officedocument.presentationml.presentation": {
726 $image = 'docman/file_type_presentation.png';
729 case "application/zip":
730 case "application/x-tar":
731 case "application/x-rpm":
732 case "application/x-rar-compressed":
733 case "application/x-bzip2":
734 case "application/x-gzip":
735 case "application/x-lzip":
736 case "application/x-compress":
737 case "application/x-7z-compressed":
738 case "application/x-gtar":
739 case "application/x-stuffitx":
740 case "application/x-lzx":
741 case "application/x-lzh":
742 case "application/x-gca-compressed":
743 case "application/x-apple-diskimage":
744 case "application/x-dgc-compressed":
745 case "application/x-dar":
746 case "application/x-cfs-compressed":
747 case "application/vnd.ms-cab-compressed":
748 case "application/x-alz-compressed":
749 case "application/x-astrotite-afa":
750 case "application/x-ace-compressed":
751 case "application/x-cpio":
752 case "application/x-shar":
753 case "application/x-xz": {
754 $image = 'docman/file_type_archive.png';
758 $image = 'docman/file_type_unknown.png';
765 * update - use this function to update an existing entry in the database.
767 * @param string The filename of this document. Can be a URL.
768 * @param string The filetype of this document. If filename is URL, this should be 'URL';
769 * @param string The contents of this document.
770 * @param int The doc_group id of the doc_groups table.
771 * @param string The title of this document.
772 * @param string The description of this document.
773 * @param int The state id of the doc_states table.
774 * @return boolean success.
776 function update($filename, $filetype, $data, $doc_group, $title, $description, $stateid) {
778 $perm =& $this->Group->getPermission();
779 if (!$perm || !is_object($perm) || !$perm->isDocEditor()) {
780 $this->setPermissionDeniedError();
784 $user = session_get_user();
785 if ($this->getLocked() && ($this->getLockedBy() != $user->getID())) {
786 $this->setPermissionDeniedError();
790 if (strlen($title) < 5) {
791 $this->setError(_('Title Must Be At Least 5 Characters'));
795 if (strlen($description) < 10) {
796 $this->setError(_('Document Description Must Be At Least 10 Characters'));
800 /* TODO : NEED REAL CHECK */
802 $result = db_query_params('SELECT filename, doc_group FROM docdata_vw WHERE filename = $1 AND doc_group = $2 AND stateid = $3 AND docid != $4',
803 array($filename, $doc_group, $stateid, $this->getID()));
804 if (!$result || db_numrows($result) > 0) {
805 $this->setError(_('Document already published in this directory'));
810 $res = db_query_params('UPDATE doc_data SET
822 array(htmlspecialchars($title),
823 htmlspecialchars($description),
831 $this->Group->getID(),
835 if (!$res || db_affected_rows($res) < 1) {
836 $this->setOnUpdateError(db_error());
840 if (filesize($data)) {
841 // key words for in-document search
842 if ($this->Group->useDocmanSearch()) {
843 $kw = new Parsedata();
844 $kwords = $kw->get_parse_data($data, htmlspecialchars($title), htmlspecialchars($description), $filetype, $filename);
849 $res = db_query_params('UPDATE doc_data SET filesize=$1, data_words=$2 WHERE group_id=$3 AND docid=$4',
850 array(filesize($data),
852 $this->Group->getID(),
856 if (!$res || db_affected_rows($res) < 1) {
857 $this->setOnUpdateError(db_error());
861 DocumentStorage::instance()->delete($this->getID())->commit();
862 DocumentStorage::instance()->store($this->getID(), $data);
865 $this->sendNotice(false);
870 * sendNotice - Notifies of document submissions
872 * @param boolean true = new document (default value)
874 function sendNotice($new = true) {
875 $BCC = $this->Group->getDocEmailAddress();
876 if ($this->isMonitoredBy('ALL')) {
877 $BCC .= $this->getMonitoredUserEmailAddress();
879 $dg = new DocumentGroup($this->Group, $this->getDocGroupID());
880 if ($dg->isMonitoredBy('ALL')) {
881 $BCC .= $dg->getMonitoredUserEmailAddress();
883 if (strlen($BCC) > 0) {
884 $sess = session_get_user();
886 $status = _('New document');
888 $status = _('Updated document').' '._('by').' ' . $sess->getRealName();
890 $subject = '['.$this->Group->getPublicName().'] '.$status.' - '.$this->getName();
891 $body = _('Project')._(': ').$this->Group->getPublicName()."\n";
892 $body .= _('Directory:').' '.$this->getDocGroupName()."\n";
893 $body .= _('Document title:').' '.$this->getName()."\n";
894 $body .= _('Document description:').' '.util_unconvert_htmlspecialchars($this->getDescription())."\n";
895 $body .= _('Submitter:').' '.$this->getCreatorRealName()." (".$this->getCreatorUserName().") \n";
897 $body .= _('Updated By:').' '. $sess->getRealName();
899 $body .= "\n\n-------------------------------------------------------\n".
900 _('For more info, visit:').
901 "\n\n" . util_make_url('/docman/?group_id='.$this->Group->getID().'&view=listfile&dirid='.$this->getDocGroupID());
903 util_send_message('', $subject, $body, '', $BCC);
909 * delete - Delete this file
911 * @return boolean success
914 $perm =& $this->Group->getPermission();
915 if (!$perm || !is_object($perm) || !$perm->isDocEditor()) {
916 $this->setPermissionDeniedError();
920 $result = db_query_params('DELETE FROM doc_data WHERE docid=$1',
921 array($this->getID()));
923 $this->setError(_('Error Deleting Document:').' '.db_error());
928 DocumentStorage::instance()->delete($this->getID())->commit();
930 /** we should be able to send a notice that this doc has been deleted .... but we need to rewrite sendNotice
931 * $this->sendNotice(false);
932 * @TODO delete monitoring this file */
937 * trash - move this file to trash
939 * @return boolean success or not.
942 $this->setState('2');
943 $dm = new DocumentManager($this->Group);
944 $this->setDocGroupID($dm->getTrashID());
946 $this->setReservedBy(0);
947 $this->sendNotice(false);
948 $this->clearMonitor();
953 * setValueinDB - private function to update columns in db
955 * @param string the column to update
956 * @param int the value to store
957 * @return boolean success or not
960 private function setValueinDB($column, $value) {
964 $qpa = db_construct_qpa();
965 $qpa = db_construct_qpa($qpa, 'UPDATE doc_data SET ');
966 $qpa = db_construct_qpa($qpa, $column);
967 $qpa = db_construct_qpa($qpa, '=$1
971 $this->Group->getID(),
973 $res = db_query_qpa($qpa);
974 if (!$res || db_affected_rows($res) < 1) {
975 $this->setOnUpdateError(db_error().print_r($qpa));
981 $this->setOnUpdateError(_('wrong column name'));
984 $this->sendNotice(false);
988 function createVersion() {
992 function deleteVersion() {
999 // c-file-style: "bsd"