Source for file RecordSelector.inc
Documentation is available at RecordSelector.inc
// include the config file
require_once '../../util/AlphaConfig.inc';
require_once $config->get('sysRoot'). 'alpha/model/AlphaDAO.inc';
require_once $config->get('sysRoot'). 'alpha/model/PersonObject.inc';
require_once $config->get('sysRoot'). 'alpha/model/types/Relation.inc';
require_once $config->get('sysRoot'). 'alpha/view/widgets/Button.inc';
* Record selection HTML widget.
* @package alpha::view::widgets
* @author John Collins <dev@alphaframework.org>
* @version $Id: RecordSelector.inc 1453 2011-12-04 15:12:54Z johnc $
* @license http://www.opensource.org/licenses/bsd-license.php The BSD License
* @copyright Copyright (c) 2011, John Collins (founder of Alpha Framework).
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the
* following conditions are met:
* * Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
* * Neither the name of the Alpha Framework nor the names
* of its contributors may be used to endorse or promote
* products derived from this software without specific
* prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* The relation object that we are going to render a view for.
private $relationObject = null;
* The label text to use where required.
* Used to indicate the reading side when accessing from MANY-TO-MANY relation
* (leave blank for other relation types).
private $accessingClassName;
* Javascript to run when the widget opens in a new window.
* The name of the HTML input box for storing the hidden and display values.
private static $logger = null;
* @param Relation $relation
* @param string $accessingClassName
* @throws IllegalArguementException
public function __construct($relation, $label= '', $name= '', $accessingClassName= '') {
self::$logger = new Logger('RecordSelector');
self::$logger->debug('>>__construct(relation=['. $relation. '], label=['. $label. '], name=['. $name. '], accessingClassName=['. $accessingClassName. '])');
if(!$relation instanceof Relation)
$this->relationObject = $relation;
$this->accessingClassName = $accessingClassName;
self::$logger->debug('<<__construct');
* Renders the text boxes and buttons for the widget, that will appear in user forms.
* @param bool $tableTags Include table tags and label (optional)
* @param bool $expanded Render the related fields in expanded format or not (optional)
* @param bool $buttons Render buttons for expanding/contacting the related fields (optional)
public function render($tableTags= true, $expanded= false, $buttons= true) {
self::$logger->debug('>>render(tableTags=['. $tableTags. '], expanded=['. $expanded. '], buttons=['. $buttons. '])');
// render text-box for many-to-one relations
if($this->relationObject->getRelationType() == 'MANY-TO-ONE') {
// value to appear in the text-box
$inputBoxValue = $this->relationObject->getRelatedClassDisplayFieldValue();
$html .= '<tr><th style="width:25%;">';
$html .= '<input type="text" size="70" class="readonly" name="'. $this->name. '_display" id="'. $this->name. '_display" value="'. $inputBoxValue. '" readonly/>';
$js = "$('#recordSelector').dialog('open');
$('#recordSelector').load('". $config->get('sysURL'). "ViewRecordSelector/value/'+document.getElementById('". $this->name. "').value+'/field/". $this->name. "/relatedClass/". $this->relationObject->getRelatedClass(). "/relatedClassField/". $this->relationObject->getRelatedClassField(). "/relatedClassDisplayField/". $this->relationObject->getRelatedClassDisplayField(). "/relationType/". $this->relationObject->getRelationType(). "');";
$tmp = new Button($js, "Insert record link", "relBut", $config->get('sysURL'). "/alpha/images/icons/application_link.png");
$html .= '<input type="text" size="70" class="readonly" name="'. $this->name. '_display" id="'. $this->name. '_display" value="'. $inputBoxValue. '" readonly/>';
$js = "$('#recordSelector').dialog('open');
$('#recordSelector').load('". $config->get('sysURL'). "ViewRecordSelector/value/'+document.getElementById('". $this->name. "').value+'/field/". $this->name. "/relatedClass/". $this->relationObject->getRelatedClass(). "/relatedClassField/". $this->relationObject->getRelatedClassField(). "/relatedClassDisplayField/". $this->relationObject->getRelatedClassDisplayField(). "/relationType/". $this->relationObject->getRelationType(). "');";
$tmp = new Button($js, "Insert record link", "relBut", $config->get('sysURL'). "/alpha/images/icons/application_link.png");
// hidden field to store the actual value of the relation
$html .= '<input type="hidden" name="'. $this->name. '" id="'. $this->name. '" value="'. $this->relationObject->getValue(). '"/>';
if($this->relationObject->getRule() != '') {
$html .= '<input type="hidden" id="'. $this->name. '_msg" value="'. $this->relationObject->getHelper(). '"/>';
$html .= '<input type="hidden" id="'. $this->name. '_rule" value="'. $this->relationObject->getRule(). '"/>';
// render read-only list for one-to-many relations
if($this->relationObject->getRelationType() == 'ONE-TO-MANY') {
$objects = $this->relationObject->getRelatedObjects();
if(count($objects) > 0 && $tableTags) {
// render tags differently
if($this->name == 'tags' && $this->relationObject->getRelatedClass() == 'TagObject') {
$html .= '<tr><th>'. $this->label. '</th><td>';
foreach($objects as $tag) {
$html .= ' <a href="'. $config->get('sysURL'). 'search/q/'. $tag->get('content'). '">'. $tag->get('content'). '</a>';
$html .= '<tr><th style="text-align:center;" colspan="2">';
$tmp = new Button("document.getElementById('relation_field_". $this->name. "').style.display = '';", "Display related objects", $this->name. "DisBut", $config->get('sysURL'). "/alpha/images/icons/arrow_down.png");
$tmp = new Button("document.getElementById('relation_field_". $this->name. "').style.display = 'none';", "Hide related objects", $this->name. "HidBut", $config->get('sysURL'). "/alpha/images/icons/arrow_up.png");
$html .= '<tr><td colspan="2">';
$html .= '<table id="relation_field_'. $this->name. '" style="width:100%; display:'. ($expanded ? '' : 'none'). ';" class="relationTable">';
foreach($objects as $obj) {
// check to see if we are in the admin back-end
if(strpos($_SERVER['REQUEST_URI'], '/tk/') !== false) {
if(isset ($customViewControllerName)) {
if($config->get('sysUseModRewrite'))
$viewURL = $config->get('sysURL'). $customViewControllerName. '/oid/'. $obj->getOID();
$viewURL = $config->get('sysURL'). 'controller/'. $customViewControllerName. '.php?oid='. $obj->getOID();
$viewURL = $config->get('sysURL'). 'alpha/controller/Detail.php?bo='. get_class($obj). '&oid='. $obj->getOID();
if(isset ($customEditControllerName)) {
if($config->get('sysUseModRewrite'))
$editURL = $config->get('sysURL'). $customEditControllerName. '/oid/'. $obj->getOID();
$editURL = $config->get('sysURL'). 'controller/'. $customEditControllerName. '.php?oid='. $obj->getOID();
$editURL = $config->get('sysURL'). 'alpha/controller/Edit.php?bo='. get_class($obj). '&oid='. $obj->getOID();
* If any display headers were set with setRelatedClassHeaderFields, use them otherwise
* use the OID of the related class as the only header.
$headerFields = $this->relationObject->getRelatedClassHeaderFields();
if(count($headerFields) > 0) {
foreach($headerFields as $field) {
$label = $obj->getDataLabel($field);
$value = $obj->get($field);
if($field == 'created_by' || $field == 'updated_by') {
$value = $person->getDisplayName();
$html .= '<em>'. $label. ': </em>'. $value. ' ';
// if the related BO has been updated, render the update time
if($obj->getCreateTS() != $obj->getUpdateTS()) {
$html .= '<em>'. $obj->getDataLabel('updated_ts'). ': </em>'. $obj->get('updated_ts');
$html .= '<em>Updated: </em>'. $obj->get('updated_ts');
$html .= '<em>'. $obj->getDataLabel('OID'). ': </em>'. $obj->get('OID');
// ensures that line returns are rendered
$value = str_replace("\n", '<br>', $obj->get($this->relationObject->getRelatedClassDisplayField()));
$html .= '<p>'. $value. '</p>';
$html .= '<div align="center">';
$html .= '<a href="'. $viewURL. '">View</a>';
// if the current user owns it, they get the edit link
if(isset ($_SESSION['currentUser']) && $_SESSION['currentUser']->getOID() == $obj->getCreatorId())
$html .= ' <a href="'. $editURL. '">Edit</a></div>';
// render text-box for many-to-many relations
if($this->relationObject->getRelationType() == 'MANY-TO-MANY') {
// value to appear in the text-box
$inputBoxValue = $this->relationObject->getRelatedClassDisplayFieldValue($this->accessingClassName);
// replace commas with line returns
$inputBoxValue = str_replace(",", "\n", $inputBoxValue);
$html .= '<tr><th style="width:25%;">';
$html .= '<textarea id="'. $this->name. '_display" style="width:100%;" rows="4" readonly>';
$html .= '<div align="center">';
$js = "$('#recordSelector').dialog('open');
$('#recordSelector').load('". $config->get('sysURL'). "ViewRecordSelector/lookupOIDs/'+document.getElementById('". $this->name. "').value+'/value/'+document.getElementById('". $this->name. "_OID').value+'/field/". $this->name. "/relatedClassLeft/". $this->relationObject->getRelatedClass('left'). "/relatedClassLeftDisplayField/". $this->relationObject->getRelatedClassDisplayField('left'). "/relatedClassRight/". $this->relationObject->getRelatedClass('right'). "/relatedClassRightDisplayField/". $this->relationObject->getRelatedClassDisplayField('right'). "/accessingClassName/". $this->accessingClassName. "/relationType/". $this->relationObject->getRelationType(). "');";
$tmp = new Button($js, "Insert record link", "relBut", $config->get('sysURL'). "/alpha/images/icons/application_link.png");
$html .= '<textarea id="'. $this->name. '_display" style="width:95%;" rows="5" readonly>';
$js = "$('#recordSelector').dialog('open');
$('#recordSelector').load('". $config->get('sysURL'). "ViewRecordSelector/lookupOIDs/'+document.getElementById('". $this->name. "').value+'/value/'+document.getElementById('". $this->name. "_OID').value+'/field/". $this->name. "/relatedClassLeft/". $this->relationObject->getRelatedClass('left'). "/relatedClassLeftDisplayField/". $this->relationObject->getRelatedClassDisplayField('left'). "/relatedClassRight/". $this->relationObject->getRelatedClass('right'). "/relatedClassRightDisplayField/". $this->relationObject->getRelatedClassDisplayField('right'). "/accessingClassName/". $this->accessingClassName. "/relationType/". $this->relationObject->getRelationType(). "');";
$tmp = new Button($js, "Insert record link", "relBut", $config->get('sysURL'). "/alpha/images/icons/application_link.png");
// hidden field to store the OID of the current BO
$html .= '<input type="hidden" name="'. $this->name. '_OID" id="'. $this->name. '_OID" value="'. $this->relationObject->getValue(). '"/>';
// hidden field to store the OIDs of the related BOs on the other side of the rel (this is what we check for when saving)
if($this->relationObject->getSide($this->accessingClassName) == 'left')
$lookupOIDs = $this->relationObject->getLookup()->loadAllFieldValuesByAttribute('leftID', $this->relationObject->getValue(), 'rightID', 'DESC');
$lookupOIDs = $this->relationObject->getLookup()->loadAllFieldValuesByAttribute('rightID', $this->relationObject->getValue(), 'leftID', 'DESC');
$html .= '<input type="hidden" name="'. $this->name. '" id="'. $this->name. '" value="'. implode(',', $lookupOIDs). '"/>';
self::$logger->debug('<<__render [html]');
* Returns the HTML for the record selector that will appear in a pop-up window.
* @param array $lookupOIDs An optional array of related look-up OIDs, only required for rendering MANY-TO-MANY rels
self::$logger->debug('>>renderSelector(lookupOIDs=['. var_export($lookupOIDs, true). '])');
$html = $this->displayPageHead();
if($this->relationObject->getRelationType() == 'MANY-TO-MANY') {
$classNameLeft = $this->relationObject->getRelatedClass('left');
$classNameRight = $this->relationObject->getRelatedClass('right');
if($this->accessingClassName == $classNameLeft) {
$tmpObject = new $classNameRight;
$fieldName = $this->relationObject->getRelatedClassDisplayField('right');
$fieldLabel = $tmpObject->getDataLabel($fieldName);
$oidLabel = $tmpObject->getDataLabel('OID');
$objects = $tmpObject->loadAll(0, 0, 'OID', 'ASC', true);
self::$logger->debug('['. count($objects). '] related ['. $classNameLeft. '] objects loaded');
$tmpObject = new $classNameLeft;
$fieldName = $this->relationObject->getRelatedClassDisplayField('left');
$fieldLabel = $tmpObject->getDataLabel($fieldName);
$oidLabel = $tmpObject->getDataLabel('OID');
$objects = $tmpObject->loadAll(0, 0, 'OID', 'ASC', true);
self::$logger->debug('['. count($objects). '] related ['. $classNameLeft. '] objects loaded');
$html .= '<table cols="3" width="100%" class="bordered">';
$html .= '<th>'. $oidLabel. '</th>';
$html .= '<th>'. $fieldLabel. '</th>';
$html .= '<th>Connect?</th>';
foreach($objects as $obj){
$html .= '<td width="20%">';
$html .= '<td width="60%">';
$html .= $obj->get($fieldName);
$html .= '<td width="20%">';
if(in_array($obj->getOID(), $lookupOIDs)) {
$this->onloadJS .= 'toggelOID(\''. $obj->getOID(). '\',\''. $obj->get($fieldName). '\',true);';
$html .= '<input name = "'. $obj->getOID(). '" type="checkbox" checked onclick="toggelOID(\''. $obj->getOID(). '\',\''. $obj->get($fieldName). '\',this.checked);"/>';
$html .= '<input name = "'. $obj->getOID(). '" type="checkbox" onclick="toggelOID(\''. $obj->getOID(). '\',\''. $obj->get($fieldName). '\',this.checked);"/>';
$html .= '<div align="center" style="padding:10px;">';
$tmp = new Button("$('#recordSelector').dialog('close');", "Cancel", "cancelBut", $config->get('sysURL'). "/alpha/images/icons/cancel.png");
$html .= ' ';
$tmp = new Button("setParentFieldValues(); $('#". $_GET['field']. "_display').blur(); $('#recordSelector').dialog('close');", "Accept", "acceptBut", $config->get('sysURL'). "/alpha/images/icons/accept.png");
$className = $this->relationObject->getRelatedClass();
$tmpObject = new $className;
$label = $tmpObject->getDataLabel($this->relationObject->getRelatedClassDisplayField());
$oidLabel = $tmpObject->getDataLabel('OID');
$objects = $tmpObject->loadAll(0, 0, 'OID', 'DESC');
$html = '<table cols="3" width="100%" class="bordered">';
$html .= '<th>'. $oidLabel. '</th>';
$html .= '<th>'. $label. '</th>';
$html .= '<th>Connect?</th>';
foreach($objects as $obj){
$html .= '<td width="20%">';
$html .= '<td width="60%">';
$html .= $obj->get($this->relationObject->getRelatedClassDisplayField());
$html .= '<td width="20%">';
if($obj->getOID() == $this->relationObject->getValue()) {
$html .= '<img src="'. $config->get('sysURL'). '/alpha/images/icons/accept_ghost.png"/>';
$tmp = new Button("document.getElementById('". $_GET['field']. "').value = '". $obj->getOID(). "'; document.getElementById('". $_GET['field']. "_display').value = '". $obj->get($this->relationObject->getRelatedClassDisplayField()). "'; $('#". $_GET['field']. "_display').blur(); $('#recordSelector').dialog('close');", "", "selBut", $config->get('sysURL'). "/alpha/images/icons/accept.png");
$html .= '<script type="text/javascript">'.
'$(document).ready(function() {';
$html .= $this->onloadJS;
$html .= $this->displayPageFoot();
self::$logger->debug('<<renderSelector[html]');
* Renders the header HTML and JS for the record selector pop-up page.
private function displayPageHead() {
self::$logger->debug('>>displayPageHead()');
$html .= '<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">';
$html .= '<title>Record Selector</title>';
$html .= '<link rel="StyleSheet" type="text/css" href="'. $config->get('sysURL'). 'alpha/lib/jquery/ui/themes/'. $config->get('sysTheme'). '/ui.all.css">';
$html .= '<link rel="StyleSheet" type="text/css" href="'. $config->get('sysURL'). 'alpha/css/alpha.css">';
$html .= '<link rel="StyleSheet" type="text/css" href="'. $config->get('sysURL'). 'config/css/overrides.css">';
$html .= '<script language="JavaScript" src="'. $config->get('sysURL'). '/alpha/scripts/addOnloadEvent.js"></script>';
$html .= '<script language="JavaScript">
var selectedOIDs = new Object();
function toggelOID(oid, displayValue, isSelected) {
selectedOIDs[oid] = displayValue;
delete selectedOIDs[oid];
function setParentFieldValues() {
for(key in selectedOIDs) {
OIDs = OIDs + \',\' + key;
if(displayValues == null)
displayValues = selectedOIDs[key];
displayValues = displayValues + \'\\n\' + selectedOIDs[key];
document.getElementById(\''. $_GET['field']. '\').value = "00000000000";
document.getElementById(\''. $_GET['field']. '_display\').value = "";
document.getElementById(\''. $_GET['field']. '\').value = OIDs;
document.getElementById(\''. $_GET['field']. '_display\').value = displayValues;
self::$logger->debug('<<displayPageHead [html]');
* Renders the footer HTML and JS for the record selector pop-up page.
private function displayPageFoot() {
|