Translations of this page:

Document review reminder

This component is designed to display files that are due for review upto 1 year away from today. The component will only provide a list of files, including how many days till the document review is due or how overdue it is. The component is very configurable allowing you to show documents to everyone with permissions, or just the reviewer (if specified) or just the owner, or both (again, if specified).

This article will explain how to configure and setup the document review component.

1. navigate into the folder intranet/common/classes

2. Create the file “TemplaterComponentDocumentDue.php” (keep in mind if you’re using a linux system, the name is case-sensetive), and place in it the following:

<?php
/**
 * A component that is designed to show documents due for review
 *
 * @author Daniel Munn [daniel.munn@claromentis.com]
 */
 
require_once("../common/metadata_pkg.php");
 
class TemplaterComponentDocumentDue implements TemplaterComponent
{
	const DEFAULT_UNIT_QUANTITY = 7; // Default is 7 days;
	const DEFAULT_MAX_RENDER = 20; // 20 documents max
	const FILE_MAX_LENGTH = 25; // Default maximum length of file names (will chop and display ... otherwise)
	
	const DUE_TYPE_REVIEWER = 1;
	const DUE_TYPE_OWNER = 2;
	const DUE_TYPE_ALL = 4;
	
	
	protected $data = array(); // Data storage
	protected $docs; // This just stores document information already pulled to prevent too much repetative information
	protected $html; //Rendering information
	protected $lasterror; //Output information;
	protected $attributes; //Attribute array
	protected $mode; // Mode of access (ALL | OWNER | REVIEWER);
	
	protected $metadate; // Metadata key (date)
	protected $metakey; //Metadata Key
	protected $displaymax; // Records to display;
	protected $datemax; //Maximum date we're checking against
	protected $datemin; //Today's date
	
	/**
	 * Class constuctor
	 */
	public function __construct() 
	{
		//With the constructor, we just ensure dates are pre-filled
		$this->datemax = new DateDay();
		$this->datemin = new DateDay();
	}
 
	/**
	 * Attribute array values:
	 *		* metadate (str) - Metadata key to bind source to
	 *		* metareviewer (str) - metadata key for finding reviewer
	 *		* display (int) - [Optional - default 10 for time period specified] Number of document anniversary events to render
	 *		* timeframe (day/week/month) - [Optional - default day]Unit by which to retrieve information window
	 *		* noevents (str) - [Optional] Message to show if there are no events
	 *		* showtoall (bool) - Show all documents (in relevent permission groups) to persons instead of listing them specific to reviewer or owner
	 *		* hidetoowner (bool) - Hide documents meant for owner, can only be used when metareviewer is set; will suppress all documents that are intended for owner (even if they dont have a reviewer) - cannot be used in conjuntion with showtoall
	 */
	public function Show($attributes) 
	{
		global $db;
		
		//Commit attributes to class
		$this->attributes = &$attributes;
		
		//Check Metakey Information
		if (!$this->CheckMetadata())
			return $this->lasterror; //Output error, and hold execution
		
		//Attribute: Display - this will control how many birthdays are shown
		$this->CheckDisplay();
		
		//Attribute: Timeframe - this will control how far forward (in time) this component will scope
		$this->CheckTimeFrame();
 
		 // Process metadata information including date processing - this passes information to Data;
		$this->ProcessMetadata();
		
		//This is a continuation from the above method, we process the data into a html format
		$this->ProcessData();
		
		//Return html processed by ProcessData;
		return $this->html;
	}
 
	/**
	 * Check for an associated metakey and ensure its correct type (if required populate error)
	 * @param str $metakey - Metakey to check for
	 * @param int $metatype - Type of metadata we are looking for
	 * @param bool $required - Is this a required metakey?
	 * @return bool - Returns true if metakey exists and of correct type, false otherwise 
	 */
	protected function CheckMetakey ($metakey, $metatype = META_TYPE_DATE, $required = true)
	{
		global $g_meta_field_factory;
		$dmfpobj = $g_meta_field_factory->GetPrototypeByKey($metakey);
		if (!$dmfpobj) //If object isnt instantiated loading failed and thus key does not exist
		{
			if ($required)
				$this->lasterror = $g_meta_field_factory->GetErrMsg();
			return false;
		}
		if ($dmfpobj->IsRepeatable())
		{
			if ($required)
				$this->lasterror = "The Metadata specified is marked as repeatable, which is wrong.";
			return false;
		}
		if ($dmfpobj->GetMetaType() != $metatype) 
		{
			if ($required)
				$this->lasterror = "Metakey specified [${metakey}] holds an incorrect data type. [WANTED ${metatype}; GOT ".$dmfpobj->GetMetaType()."]";
			return false;
		}
		else
		{
			return true;
		}
		//Now we check to see if meta owner is set
	}
	
	/**
	 * Check and store information from the attribute: "metakey"
	 * @param array $attributes - Pointer to attributes array;
	 */
	protected function CheckMetadata() 
	{
		//Check the metadate input
		if (!$this->CheckMetakey($this->attributes['metadate'], META_TYPE_DATE))
			return false;
		
		if ($this->attributes['showtoall'] == 'true') 
		{
			$this->mode = self::DUE_TYPE_ALL;
			if (!$this->CheckMetaKey($this->attributes['metareviewer'], META_TYPE_USER))
			{
				$this->mode = $this->mode | self::DUE_TYPE_OWNER;
			}
		} 
		else
		{
			if (!$this->CheckMetaKey($this->attributes['metareviewer'], META_TYPE_USER))
			{
				$this->mode = self::DUE_TYPE_OWNER;
			}
			else
			{
				$this->mode = self::DUE_TYPE_OWNER | self::DUE_TYPE_REVIEWER;
			}
		}
		if ($this->attributes['hidetoowner'] == 'true' && !($this->mode & self::DUE_TYPE_ALL) )
		{
			if (! ( $this->mode & self::DUE_TYPE_REVIEWER ))
			{
				$this->html .= 'Unable to hide to owner when viewtype does not include reviewers';
			}
			else
				if ( $this->mode & self::DUE_TYPE_OWNER )
					$this->mode -= self::DUE_TYPE_OWNER; //Deduct DUE_TYPE_OWNER from mode of display
		}
		return true;
	}
	
	/**
	 * Check and store information from the attribute: "display"
	 * 
	 * @param array $attributes - Pointer to attributes array;
	 */
	protected function CheckDisplay () 
	{
		$this->displaymax = self::DEFAULT_MAX_RENDER;
		if (isset($this->attributes['display'])) 
		{
			if (is_numeric($this->attributes['display'])) 
			{
				if ($this->attributes['display'] > 0) 
				{
					$this->displaymax = $this->attributes['display']; //We have a valid positive integer, we commit that to maxrender
				}
			}
		}
	}
	
	/**
	 * CheckTimeframe - checks and processes timeframe input from component
	 * 
	 * @param array $attributes - Pointer to attributes array;
	 */
	protected function CheckTimeframe ()
	{
		if (isset($this->attributes['timeframe'])) 
		{
			@$this->datemax->modify('+'.$this->attributes['timeframe']);
			if (!$this->datemax->after($this->datemin))
				$this->datemax->modify('+'.self::DEFAULT_UNIT_QUANTITY.' days');
		}
		else
		{
			$this->datemax->modify('+'.self::DEFAULT_UNIT_QUANTITY.' days');
		}
	}
	
	/**
	 * ProcessMetadata - Recurse through stored information and places information in temporary data storage
	 */
	protected function ProcessMetadata() {
		global $db;
		
		require_once("../common/erms_pkg.php"); //We need this for ERMS access
		
		$this->data = array(); //Unset existing data [as we commit to session]
		$tcount = 0; // Count of records processed
		
		$pred_reviewer_key = 'me_' . $this->attributes['metareviewer']; 
		$pred_review_key = 'me_' . $this->attributes['metadate'];
		
		$docs_list_provider = new DocsListProvider(); //We use the document list provider to actually do most of the hard work for us.
		$qp = new QueryPart(''); //Empty query part
		$qp_filled = false;
		if (! ($this->mode & self::DUE_TYPE_ALL) ) //The query we generate will automatically do this, we need to only add constraint if necessary.
		{
			if (! ( $this->mode & self::DUE_TYPE_OWNER ) && ($this->mode & self::DUE_TYPE_REVIEWER) ) //If they do not want the owner to see document
			{
				//This is something for reviewers but not owners - however we'll show to owners 
				$qp = new QueryPart(" ( ( u.id = int:owner_id AND ${pred_reviewer_key}.intval = 0 ) OR ( u.id <> int:owner_id AND ${pred_reviewer_key}.intval = int:owner_id ) ) "); //Empty query part
			}
			elseif (! ( $this->mode & self::DUE_TYPE_REVIEWER ) && ($this->mode & self::DUE_TYPE_OWNER) )
			{
				// No to reviewers (maybe not configured?) and where user is owner
				$qp = new QueryPart(" ( u.id = int:owner_id ) ");
			}
			else
			{
				//Document is either owned by owner, or to be reviewed by reviewer
				$qp = new QueryPart(" ( u.id = int:owner_id OR ${pred_reviewer_key}.intval = int:owner_id ) ");
			}
			$qp_filled = true; // Flag that we've done something
		}
		
		//Default bindings for above query parts
		$qp->Bind('owner_id', $_SESSION['SESSION_UID']);
		
		//Break the querypart into actionable sql
		$qp_query = $qp->AsPart();
		if($qp_filled)
			$qp_query .= " AND "; // append AND to query part
		
		//I hate that I need to break this up the way I do
		if($this->attributes['metareviewer'])
		{
			$document_query = $docs_list_provider->GetSQLForDocuments("d.doc_id, d.title, dp.folder_id, u.id owner_id, ${pred_review_key}.intval doc_review_date, ${pred_reviewer_key}.intval doc_review_id, dc.title dc_title", array('u', 'dp', 'dc'), array($this->attributes['metareviewer'], $this->attributes['metadate']), null, null, true, " ORDER BY ${pred_review_key}.intval ASC ", $qp_query . " ${pred_review_key}.intval > 0 GROUP BY d.doc_id, d.doc_id, d.title, dp.folder_id, u.id, ${pred_review_key}.intval, ${pred_reviewer_key}.intval");
		}
		else
		{
			$document_query = $docs_list_provider->GetSQLForDocuments("d.doc_id, d.title, dp.folder_id, u.id owner_id, ${pred_review_key}.intval doc_review_date, 0 doc_review_id, dc.title dc_title", array('u', 'dp', 'dc'), array($this->attributes['metadate']), null, null, true, " ORDER BY ${pred_review_key}.intval ASC ", $qp_query . " ${pred_review_key}.intval > 0 GROUP BY d.doc_id, d.doc_id, d.title, dp.folder_id, u.id, ${pred_review_key}.intval");
		} 
		$document_query->BindPart($qp); // Ensure bound values are transfered
		
		if ( $this->displaymax > 0 )
			$document_query->setLimit($this->displaymax);
		
		$result = $db->query($document_query);
				
		while($row = $result->fetchArray()) {
			/*$tcount++;
			if ($tcount > $this->displaymax && $this->displaymax > 0)
				break; // Break out of while*/
			//We need to re-structure the information comitted to array, give it a little more meaning;
			$row['doc_review_date'] = new DateDay($row['doc_review_date']);
			$datecalc = $row['doc_review_date']->toDays() - $this->datemin->toDays();
			$this->data[$datecalc][] = $row;
		}
	}
 
	/**
	 * ProcessData - Take information from temporary data store and render into an output format 
	 */
	protected function ProcessData() {
		global $cfg_erms_user_friendly_urls;
		$this->html .= "<ul>";
		
		if ( count($this->data) > 0 )
		{
			ksort($this->data); //Lets order our array properly
			$rendered = 0; // Count the number that have been processed
			foreach ($this->data as $daynumber => $dayarray) //Cycle through array of data breaking into days and relevent array
			{
				if ($rendered >= $this->displaymax && $this->displaymax > 0)
					break; // Break out if we've reached max rendered 
				foreach ($dayarray as $dayevent) //Break down array of events for specified day
				{
					//Now, we have broken information into a specific event; this is a rendering aspect of this loop
 
				
					//Conditional styling based on li class
					if($daynumber < 0)
						$li_open = '<li class="tcdd_overdue">'; //A document is overdue 
					elseif ($daynumber <= 7)
						$li_open = '<li class="tcdd_due">'; //Document is due within the next week for review
					else
						$li_open = '<li>'; //Its a document that is due for review soon
 
 
					if ($rendered >= $this->displaymax && $this->displaymax > 0)
						break; // Break out if we've reached max rendered
 
					$this->html .= $li_open;
 
					//Information URL for document
					$dayevent['info_url'] = ERMSDataObj::GetDetailsUrlStatic($dayevent['doc_id'], $dayevent['folder_id']);
					
					//Retrieve the title of the document if dc_title, use that, if not use standard title.
					$document_title = ( strlen($dayevent['dc_title']) > 0 ) ? $dayevent['dc_title'] : $dayevent['title'];
 
					//Prepare url link based on information we know about.
					$dayevent['link_url'] = '/intranet/documents/' . $dayevent['folder_id'] . '/' . $dayevent['doc_id']. "/" . rawurlencode($document_title); // Build URL
					
					if ( $daynumber < 0 ) 
					{
						$daynumber *= -1;
						$daylabel = ($daynumber <> 1) ? 'days' : 'day';
						$daymessage = htmlentities("(${daynumber} ${daylabel} overdue)");
					}
					else
					{
						$daylabel = ($daynumber <> 1) ? 'days' : 'day';
						$daymessage = htmlentities("(${daynumber} ${daylabel})");
					}
					if ( strlen($dayevent['title']) > self::FILE_MAX_LENGTH + 3)
					{
						$dayevent['title'] = substr($dayevent['title'], 0, self::FILE_MAX_LENGTH) . '...';
					}
					
					$this->html .= sprintf('<a href="%s"><img src="/intranet/documents/images/info.gif" /></a>&nbsp;<a href="%s">%s</a><span>&nbsp;%s</span>', htmlentities($dayevent['info_url']), htmlentities($dayevent['link_url']), htmlentities($dayevent['title']), htmlentities($daymessage));
					$rendered++; //Beancounting for maxrender
 
					$this->html .= '</li>';
				}
			}
		}
		else
		{
			$this->html .= '<li>';
			if (strlen($this->attributes['noevents']) > 0)
			{
				$this->html .= htmlentities($this->attributes['noevents']);
			}
			else
			{
				$this->html .= 'No upcoming events';
			}
			$this->html .= "</li>";
		}
		$this->html .= "</ul>";
	}
 }
 ?>

Once completed, its time to configure this component: Firstly we’ll start by creating a user metadata information field

  • Login and go to your administration panel
  • Click the Metadata button
  • Select the Edit button, next to Documents, in the Documents metadata region this component only works with documents metadata

  • On the page that appears, select “Create new field”
  • In this instance we’re going to create a metadata field that holds the review date of our files:
    • You should preserve the name doc_review_date - this way it is compatible with reports
    • Ensure that you remember the Key (you’ll need it later)
    • Change the type to “Date” (not to be confused with DateTime)
    • DO NOT select repeatabdle
  • Click save once your done; however there is another field to add (Document reviewer).
    • Ensure that you remember the Key (you’ll need it later)
    • Change the type to “User Select”
    • DO NOT select repeatable

3. Paste the following component code anywhere on a templater file

<component class="TemplaterComponentDocumentDue" timeframe="1 year" metadate="doc_review_date"  noevent="There are no documents due for review." showtoall="true" display="5">
  • display - This is an optional field which controls the number of events shown, if omitted it will show every event in the specified timeframe.
  • timeframe - How far in the future would you like for this to provide events for? eg: 1 week, 3 months etc; this will only support upto 1 year; if omitted, it will default to 1 week.
  • metadate - Above when setting up your metadata field, I said to make sure you remember the key - type your key into this value. This parameter is not option
  • metareviewer - Again above we set this field up, however this metadata input is optional, however will change behaviour of other options
  • noevent - An optional input that specifies a message to show when there are no events in the timeframe specified.
  • showtoall - This optional input overrides any other functionality, will show documents due for review to any users with permissions to read them.
  • hidetoowner - This optional input will supress the document being listed to owner where it has a reviewer (providing that reviewer metadata is specified); documents with no reviewer will be shown to owner.

Suggested location(s):

/interface_{custom}/main/intranet_home.html  

or

/interface_{custom}/main/right_column.html
 
components/document_due_review.txt · Last modified: 07/06/2011 10:49 by sweta
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki