kenken999/php
0
1<?php2class EditSelectedPage extends EditPage3{4 public $rowIds = array();5 public $parsedSelection = array();6 7 public $updSelectedFields = null;8 public $selectedFields = null;9 10 public $nSelected = 0;11 public $nUpdated = 0;12 13 /**14 * The record data currently being updated15 * @type Array16 */17 public $recordBeingUpdated;18 19 public $currentWhereExpr;20 21 /**22 * The record data currently being updated23 */24 public $recordCount = 0;25 26 public $messages = array();27 28 protected $inlineReportData = array();29 30 31 /**32 * @constructor33 */34 function __construct(&$params)35 {36 parent::__construct($params);37 38 $keyFields = $this->pSet->getTableKeys();39 // parse selection40 foreach( $this->selection as $s )41 {42 $arr = explode("&", refine($s));43 if( count($arr) != count($this->pSet->getTableKeys()) )44 continue;45 46 foreach($arr as $i => $v)47 {48 $parsed[ $keyFields[$i] ] = rawurldecode( $v );49 }50 $this->parsedSelection[] = $parsed;51 }52 }53 54 /**55 * Get the page's fields list56 * @return Array57 */58 protected function getPageFields()59 {60 if( $this->updSelectedFields === NULL )61 {62 $this->updSelectedFields = array_diff( $this->pSet->getUpdateSelectedFields(), $this->pSet->getTableKeys() );63 $denyDuplicateFields = array();64 foreach( $this->updSelectedFields as $f )65 {66 if( !$this->pSet->allowDuplicateValues($f) )67 $denyDuplicateFields[] = $f;68 }69 $this->updSelectedFields = array_diff( $this->updSelectedFields, $denyDuplicateFields );70 71 $updateFields = array();72 foreach( $this->updSelectedFields as $f )73 {74 $editFormat = $this->pSet->getEditFormat( $f );75 if ( $editFormat != EDIT_FORMAT_FILE )76 {77 $updateFields[] = $f;78 }79 }80 $this->updSelectedFields = $updateFields; 81 }82 83 return $this->updSelectedFields;84 }85 86 /**87 * Set keys values88 * @param Array keys89 */90 public function setKeys($keys)91 {92 $this->keys = $keys;93 }94 95 96 /**97 * Get the correct after edit action98 * basing on the table settings99 * @return Number100 */101 protected function getAfterEditAction()102 {103 if( isset( $this->afterEditAction ) && !is_null( $this->afterEditAction ) )104 return $this->afterEditAction;105 106 $action = $this->pSet->getAfterEditAction();107 if( $action != AE_TO_LIST )108 $action = AE_TO_EDIT;109 110 if( $this->isPopupMode() && $this->pSet->checkClosePopupAfterEdit() )111 $action = AE_TO_LIST;112 113 $this->afterEditAction = $action;114 return $this->afterEditAction;115 }116 117 /**118 *119 */120 public function process()121 {122 if( $this->action == "edited" )123 {124 $this->processDataInput();125 $this->readEditValues = !$this->updatedSuccessfully;126 127 if( $this->isPopupMode() )128 {129 $this->reportInlineSaveStatus();130 return;131 }132 133 if( $this->updatedSuccessfully )134 {135 if( $this->afterEditActionRedirect() )136 return;137 }138 $this->cachedRecord = null;139 }140 141 if( $this->captchaExists() )142 {143 $this->displayCaptcha();144 }145 146 $this->prgReadMessage();147 148 // get the record to edit149 if( !$this->readRecord() )150 return;151 152 $this->setPageTitle( GetTableCaption(GoodFieldName( $this->tName ) ) );153 154 $this->prepareReadonlyFields();155 156 $this->doCommonAssignments();157 $this->prepareButtons();158 $this->prepareSteps();159 $this->prepareEditControls();160 161 $this->prepareJsSettings();162 $this->addButtonHandlers();163 $this->addCommonJs();164 165 $this->fillSetCntrlMaps();166 167 $this->displayEditPage();168 }169 170 171 /**172 * Add table settings173 */174 protected function prepareJsSettings()175 {176 $this->pageData['detailsMasterKeys'] = $this->getDetailTablesMasterKeys( $this->getCurrentRecordInternal() );177 178 $this->jsSettings["tableSettings"][ $this->tName ]["selection"] = $this->getSelection();179 $this->jsSettings["tableSettings"][ $this->tName ]["keyFields"] = $this->pSet->getTableKeys();180 $this->jsSettings["tableSettings"][ $this->tName ]["captchaEditFieldName"] = $this->getCaptchaFieldName();181 }182 183 /**184 * Assign basic page's xt variables185 */186 protected function doCommonAssignments()187 {188 $this->message = $this->getMessages();189 parent::doCommonAssignments();190 }191 192 /**193 * No details preview on the Edit Selected page194 */195 protected function prepareDetailsTables()196 {197 }198 199 /**200 * No next/prev buttons on the Edit Selected page201 */202 protected function prepareNextPrevButtons() 203 {204 }205 206 /**207 * Assign buttons xt variables208 */209 protected function prepareButtons()210 {211 parent::prepareButtons();212 213 $this->hideItemType("edit_view");214 $this->hideItemType("prev");215 $this->hideItemType("next");216 217 $this->xt->assign("save_button", false);218 $this->xt->assign("view_page_button", false );219 220 $this->xt->assign("updsel_button", true);221 $this->xt->assign("updselbutton_attrs", "id=\"saveButton".$this->id."\"" );222 223 if ( $this->isPD() )224 {225 foreach ( $this->pSet->updateSelectedButtons() as $itemId => $mLString )226 { 227 $label = str_replace( "%n%", $this->nSelected, GetMLString($mLString) );228 $this->xt->assign($itemId."_label", $label );229 } 230 }231 else232 {233 $label = str_replace( "%n%", $this->nSelected, "Update %n% records" );234 $this->xt->assign("update_selected", $label );235 } 236 }237 238 /**239 * Locks record for editing.240 * Returns false if the page can not continue processing. True otherwise.241 */242 protected function lockRecord()243 {244 // ignore locking245 return true;246 }247 248 /**249 * Print JSON containing a saved record data on ajax-like request250 */251 protected function reportInlineSaveStatus()252 {253 $returnJSON = $this->inlineReportData;254 $returnJSON["success"] = $this->updatedSuccessfully;255 256 if( !$this->isCaptchaOk )257 {258 $returnJSON['wrongCaptchaFieldName'] = $this->getCaptchaFieldName();259 }260 261 $returnJSON['message'] = $this->getMessages();262 263 echo printJSON( $returnJSON );264 exit();265 }266 267 /**268 * Get an array containing the record save status269 * @param Array270 * @return Array271 */272 protected function getRowSaveStatusJSON( $keys )273 {274 $returnJSON = array();275 276 if( $this->action != "edited" || $this->isSimpleMode() )277 return $returnJSON;278 279 $returnJSON['fNamesSelected'] = array_keys( $this->newRecordData ); 280 $returnJSON['message'] = $this->getMessages();281 $returnJSON['lockMessage'] = $this->lockingMessageText;282 283 if( !$this->isCaptchaOk )284 $returnJSON['wrongCaptchaFieldName'] = $this->getCaptchaFieldName();285 286 // successful update. Return new keys and field values287 $data = $this->getRecordByKeys( $keys ); 288 if( !$data )289 $data = $this->newRecordData;290 291 // details tables keys292 $returnJSON['detKeys'] = array();293 foreach( $this->pSet->getDetailTablesArr() as $dt )294 {295 $dkeys = array();296 foreach( $dt["masterKeys"] as $idx => $mk )297 {298 $dkeys[ "masterkey".($idx + 1) ] = $data[ $mk ];299 }300 $returnJSON['detKeys'][ $dt['dDataSourceTable'] ] = $dkeys;301 } 302 303 // prepare field values304 // keys305 $keyParams = array();306 foreach( $this->pSet->getTableKeys() as $i => $k )307 {308 $keyParams[] = "key" . ($i + 1) . "=" . runner_htmlspecialchars( rawurlencode( $keys[ $k ] ) );309 }310 $keylink = "&" . implode("&", $keyParams);311 312 // values313 $values = array();314 $rawValues = array();315 $controlValues = array();316 317 $listPSet = new ProjectSettings( $this->tName, PAGE_LIST, $this->hostPageName, $this->pageTable );318 // override viewControls so that field values are built for the host List page and not for the Edit319 $this->viewControls = new ViewControlsContainer( $listPSet, PAGE_LIST, $this );320 321 322 foreach( $this->pSet->getFieldsList() as $f )323 {324 $value = $this->showDBValue( $f, $data, $keylink );325 $values[ $f ] = $value;326 if( IsBinaryType( $this->pSet->getFieldType( $f ) ) ) {327 $rawValues[ $f ] = "";328 } else {329 $rawValues[ $f ] = runner_substr($data[ $f ], 0, 100);330 $controlValues[ $f ] = $data[ $f ];331 }332 }333 334 $returnJSON['controlValues'] = $controlValues; 335 336 $returnJSON['keys'] = $this->jsKeys;337 $returnJSON['masterKeys'] = $this->getDetailTablesMasterKeys($data);338 $returnJSON['keyFields'] = $this->pSet->getTableKeys();339 $returnJSON['oldKeys'] = array();340 // add old keys341 $i = 0;342 foreach($this->oldKeys as $field => $value)343 {344 $returnJSON['oldKeys'][ $i++ ] = $value;345 }346 347 $returnJSON['vals'] = $values;348 $returnJSON['fields'] = $this->pSet->getFieldsList();349 $returnJSON['rawVals'] = $rawValues;350 $returnJSON['hrefs'] = $this->buildDetailGridLinks( $returnJSON['detKeys'] );351 352 // the record might become non-editable after updating353 if( !$this->IsRecordEditable( false ) )354 $returnJSON['nonEditable'] = true;355 356 $dmapIconsData = $this->getDashMapsIconsData( $data );357 if( count( $dmapIconsData ) )358 $returnJSON['mapIconsData'] = $dmapIconsData; 359 360 $fieldsIconsData = $this->getFieldMapIconsData( $data );361 if( count( $fieldsIconsData ) )362 $returnJSON['fieldsMapIconsData'] = $fieldsIconsData;363 364 $returnJSON['editFields'] = $this->editFields;365 if( $this->forSpreadsheetGrid ) {366 $returnJSON['editFields'] = $listPSet->getInlineEditFields();367 } 368 369 return $returnJSON;370 }371 372 /**373 * It redirects to a new page374 * according to the edit page settings375 * @return Boolean376 */377 protected function afterEditActionRedirect()378 {379 if( $this->isPopupMode() )380 return false;381 382 switch( $this->getAfterEditAction() )383 {384 case AE_TO_EDIT:385 return $this->prgRedirect();386 387 case AE_TO_LIST:388 if( $this->pSet->hasListPage() )389 HeaderRedirect($this->shortTableName, PAGE_LIST, "a=return");390 else391 HeaderRedirect("menu");392 return true;393 394 default:395 return false;396 }397 }398 399 400 /**401 * Get the previous record keys402 * @return Array403 */404 protected function getPrevKeys()405 {406 return array();407 }408 409 /**410 * Get the next record keys411 * @return Array412 */413 protected function getNextKeys()414 {415 return array();416 }417 418 419 /**420 * POST-REDIRECT-GET421 * Redirect after saving the data to avoid saving again on refresh.422 */423 protected function prgRedirect()424 {425 if( $this->stopPRG )426 return false;427 428 if( !$this->updatedSuccessfully || !$this->isSimpleMode() || !no_output_done() )429 return false;430 431 $_SESSION["edit_seletion"] = $this->selection;432 $_SESSION["message_edit"] = $this->getMessages();433 $_SESSION["message_edit_type"] = $this->messageType;434 435 $getParams = $this->getStateUrlParams();436 if( $this->pageName )437 $getParams .= "&page=".$this->pageName; 438 439 HeaderRedirect( $this->pSet->getShortTableName(), $this->getPageType(), $getParams );440 exit();441 return true;442 }443 444 protected function getSingleRecordWhereClause( $keys ) 445 {446 $dc = new DsCommand;447 $dc->keys = $keys;448 $dc->filter = $this->getSecurityCondition();449 $sql = $this->dataSource->prepareSQL($dc );450 return $sql["where"];451 }452 453 /**454 * @param Boolean useOldKeys455 * @return String456 */457 public function getKeysWhereClause( $useOldKeys )458 {459 return $this->currentWhereExpr;460 }461 462 /**463 * Read current values from the database464 * @return Array The current record data465 */466 public function getCurrentRecordInternal()467 {468 if( !is_null( $this->cachedRecord ) )469 return $this->cachedRecord;470 471 $dc = $this->getSubsetDataCommand();472 473 if( $this->eventsObject->exists("BeforeQueryEdit") ) {474 $prep = $this->dataSource->prepareSQL( $dc );475 $where = $prep["where"];476 $sql = $prep["sql"];477 478 $this->eventsObject->BeforeQueryEdit( $sql, $where, $this );479 480 if( $sql != $prep["sql"] ) {481 $this->dataSource->overrideSQL( $dc, $sql );482 } else if( $where != $prep["where"] ) {483 $this->dataSource->overrideWhere( $dc, $where );484 }485 }486 487 $this->nSelected = 0; 488 489 $fields = $this->getPageFields();490 $diffValues = array();491 492 $rs = $this->dataSource->getList( $dc );493 while( $fetchedArray = $rs->fetchAssoc() ) {494 $fetchedArray = $this->cipherer->DecryptFetchedArray( $fetchedArray );495 496 if( !$this->cachedRecord )497 $this->cachedRecord = $fetchedArray;498 else 499 {500 foreach( $fields as $f ) 501 {502 if( $this->cachedRecord[$f] != $fetchedArray[$f] )503 $diffValues[$f] = true;504 }505 }506 ++$this->nSelected;507 }508 509 foreach( $diffValues as $f => $v ) {510 unset( $this->cachedRecord[$f] );511 }512 513 514 if( $this->action != "edited" ) {515 foreach($this->getPageFields() as $fName)516 {517 $aValue = $this->pSet->getAutoUpdateValue($fName);518 if( $aValue !== "" )519 $this->cachedRecord[ $fName ] = $this->pSet->getAutoUpdateValue($fName);520 }521 }522 523 // A successful function call shouldn't return empty array when 524 $this->cachedRecord["..."] = "";525 return $this->cachedRecord;526 }527 528 protected function readRecord()529 {530 $this->getCurrentRecordInternal();531 return true;532 }533 534 535 public function fillControlFlags( $field, $required = false )536 {537 $gf = GoodFieldName($field);538 $data = $this->getCurrentRecordInternal();539 540 $checkbox = "<input type=checkbox class=\"bs-updselbox\" id=updsel_"541 .$gf.$this->id." data-field=\"".runner_htmlspecialchars( $field ) ."\">";542 543 $label = array();544 $label["begin"] = $checkbox; 545 $this->xt->assign($gf."_label", $label );546 547 if( $this->pSet->isRequired( $field ) || $required ) {548 $this->xt->assign( "required_attr_".GoodFieldName( $field ), 'data-required="true"' );549 } 550 }551 552 553 protected function buildNewRecordData()554 {555 // read field values with checkbpxes556 // define temporary arrays. These are required for ASP conversion557 $evalues = array();558 $efilename_values = array();559 $blobfields = array();560 $keys = $this->keys;561 562 if( !$this->selectedFields ) {563 $this->selectedFields = array();564 }565 $newFields = array_intersect( $this->getpageFields(), $this->selectedFields );566 foreach($newFields as $f)567 {568 $control = $this->getControl( $f, $this->id );569 $control->readWebValue($evalues, $blobfields, NULL, NULL, $efilename_values);570 }571 572 $this->newRecordData = $evalues;573 }574 575 /**576 * @return Array577 */578 protected function getNewRecordCopy( $newRecordData )579 {580 return $newRecordData;581 }582 583 /**584 * Process user data input and save it to the database table585 */586 public function processDataInput()587 {588 $this->buildNewRecordData();589 590 if( !$this->recheckUserPermissions() )591 {592 // prevent the page from reading database values593 $this->oldRecordData = $this->newRecordData;594 $this->cachedRecord = $this->newRecordData;595 $this->recordValuesToEdit = null;596 return false;597 }598 599 if( !$this->checkCaptcha() )600 {601 $this->setMessage($this->message);602 return false;603 }604 605 // check Deny Duplicate values606 foreach($this->newRecordData as $f => $value)607 {608 if( !$this->pSet->allowDuplicateValues($f) )609 {610 $this->errorFields[] = $f;611 $this->setMessage( $this->pSet->label( $f ) . " " . mlang_message("INLINE_DENY_DUPLICATES") );612 return false;613 }614 }615 616 $newRecordData = $this->getNewRecordCopy( $this->newRecordData );617 618 $noLockedIdxs = array();619 if ( $this->lockingObj )620 {621 foreach( $this->parsedSelection as $idx => $s )622 {623 if ( $this->lockingObj->LockRecord( $this->tName, $s) )624 {625 $noLockedIdxs[] = $idx; 626 }627 }628 }629 630 /* process the records and update 1 by one */631 foreach( $this->parsedSelection as $idx => $s ) 632 {633 if ( $this->lockingObj )634 {635 if ( in_array($idx, $noLockedIdxs) ) 636 $this->lockingObj->UnlockRecord( $this->tName, $s, "" ); 637 else638 continue;639 }640 641 //restore initial new record data array642 643 /* ASP trick begin */644 // ASP assignments to class members don't create a new copy of array645 // We need to create a new copy explicitly - $newRecordDataTemp646 647 $newRecordDataTemp = $newRecordData;648 $this->newRecordData = $this->getNewRecordCopy( $newRecordDataTemp );649 /* ASP trick end */650 651 $dc = new DsCommand();652 $dc->keys = $s;653 654 $this->currentWhereExpr = $this->getSingleRecordWhereClause( $s );655 656 $rs = $this->dataSource->getSingle( $dc );657 if( $rs ) {658 $fetchedArray = $rs->fetchAssoc();659 }660 661 $fetchedArray = $this->cipherer->DecryptFetchedArray( $fetchedArray );662 if( !$this->isRecordEditable( $fetchedArray ) )663 continue;664 665 $this->setUpdatedLatLng( $this->getNewRecordData(), $fetchedArray );666 667 $this->oldKeys = $s;668 $this->recordBeingUpdated = $fetchedArray;669 670 if( !$this->callBeforeEditEvent() )671 continue;672 673 if( $this->callCustomEditEvent() ) {674 $updateResult = $this->dataSource->updateSingle( $this->getUpdateDataCommand() );675 if( !$updateResult ) {676 $this->setDatabaseError( $this->dataSource->lastError() ); 677 continue;678 }679 // success if at least one record updated680 }681 ++$this->nUpdated;682 683 $this->mergeNewRecordData();684 $this->auditLogEdit( $s );685 $this->callAfterEditEvent();686 687 if( $this->isPopupMode() ) {688 $this->inlineReportData[ $this->rowIds[ $idx ] ] = $this->getRowSaveStatusJSON( $s );689 }690 }691 692 $this->updatedSuccessfully = ($this->nUpdated > 0);693 694 // do save the record695 696 if( !$this->updatedSuccessfully )697 {698 return false;699 }700 701 $this->messageType = MESSAGE_INFO;702 $this->setSuccessfulEditMessage();703 704 $this->callAfterSuccessfulSave();705 706 return true;707 }708 709 /**710 * @deprecated711 * @param Array keys712 * @return Array713 */714 protected function getRecordByKeys( $keys )715 {716 $dc = new DsCommand();717 $dc->keys = $keys;718 $dc->filter = $this->getSecurityCondition();719 720 if( $this->eventsObject->exists("BeforeQueryEdit") ) {721 $prep = $this->dataSource->prepareSQL( $dc );722 $where = $prep["where"];723 $sql = $prep["sql"];724 725 $this->eventsObject->BeforeQueryEdit( $sql, $where, $this );726 727 if( $sql != $prep["sql"] ) {728 $this->dataSource->overrideSQL( $dc, $sql );729 } else if( $where != $prep["where"] ) {730 $this->dataSource->overrideWhere( $dc, $where );731 }732 }733 734 $rs = $this->dataSource->getSingle( $dc );735 736 if( !$rs )737 return null;738 739 return $this->cipherer->DecryptFetchedArray( $rs->fetchAssoc() );740 } 741 742 /**743 * Set a successful edit message744 */745 protected function setSuccessfulEditMessage()746 {747 $message = str_replace( array("%succeed%", "%total%"), array( "<strong>".$this->nUpdated."</strong>", "<strong>".$this->nSelected."</strong>" ), "%succeed% out of %total% records updated successfully.");748 $this->setMessage( $message );749 750 if( $this->nUpdated != $this->nSelected ) {751 $message = str_replace( "%failed%", "<strong>".($this->nSelected - $this->nUpdated)."</strong>" , "%failed% records failed.");752 $this->setMessage( $message );753 }754 }755 756 757 /**758 * Add missing values from oldRecordData to newRecordData759 * This is required for the Audit and the AfterEdit event760 */761 protected function mergeNewRecordData()762 {763 if( !$this->auditObj && !$this->eventsObject->exists("AfterEdit") )764 return;765 766 foreach($this->getOldRecordData() as $f => $v)767 {768 if( !isset( $this->newRecordData[ $f ] ) )769 $this->newRecordData[ $f ] = $v;770 }771 }772 773 /**774 * Call After Record Updated event775 */776 protected function callAfterEditEvent()777 {778 if( !$this->eventsObject->exists("AfterEdit") )779 return;780 781 $this->eventsObject->AfterEdit( $this->newRecordData,782 $this->getKeysWhereClause( false ),783 $this->getOldRecordData(),784 $this->keys,785 $this->mode == EDIT_INLINE,786 $this );787 }788 789 /**790 * Call each control's afterSuccessfulSave method791 */792 protected function callAfterSuccessfulSave()793 {794 foreach($this->editFields as $f)795 {796 $this->getControl($f, $this->id)->afterSuccessfulSave();797 }798 }799 800 801 /**802 * Call Custom Edit event803 */804 protected function callCustomEditEvent()805 {806 if( !$this->eventsObject->exists("CustomEdit") )807 return true;808 809 $usermessage = "";810 $ret = $this->eventsObject->CustomEdit( $this->newRecordData,811 $this->getKeysWhereClause( true ),812 $this->getOldRecordData(),813 $this->oldKeys,814 $usermessage,815 $this->mode == EDIT_INLINE,816 $this );817 818 // this is required for the ASP conversion819 if( !$ret )820 {821 if ( 0 == strlen( $usermessage ) ) {822 ++$this->nUpdated;823 }824 else825 $this->setMessage( $usermessage );826 }827 return $ret;828 }829 830 /**831 * @return Boolean832 */833 protected function isRecordEditable( $data )834 {835 global $globalEvents;836 if( $globalEvents->exists("IsRecordEditable", $this->tName) )837 {838 if( !$globalEvents->IsRecordEditable($data, true, $this->tName) )839 return false;840 }841 842 return true;843 }844 845 /**846 * @param String fName847 * @return Boolean848 */849 protected function checkFieldOnPage( $fName )850 {851 return $this->pSet->appearOnUpdateSelected( $fName );852 }853 854 /**855 * @return Array856 */857 public function getOldRecordData()858 {859 return $this->recordBeingUpdated;860 }861 862 /**863 * @param String message864 */865 public function setMessage( $message )866 {867 $this->messages[] = $message;868 }869 870 /**871 * @param String message872 */873 public function setDatabaseError( $message )874 {875 $this->messages[] = "<strong><<< "."Record was NOT edited"." >>></strong><br>".$message;876 $this->messageType = MESSAGE_ERROR;877 } 878 879 /**880 * @return String881 */882 public function getMessages() 883 {884 return implode( "<br>", $this->messages );885 }886 887 /**888 * @return Boolean889 */890 protected function isPopupMode() 891 {892 return $this->mode == EDIT_SELECTED_POPUP;893 }894 895 /**896 * @return Boolean897 */898 protected function isSimpleMode() 899 {900 return $this->mode == EDIT_SELECTED_SIMPLE;901 }902 903 /**904 *905 */906 public function getSubsetDataCommand( $ignoreFilterField = "" ) {907 $dc = new DsCommand();908 909 $conditions = array();910 foreach( $this->parsedSelection as $s ) {911 // selection condition912 $conditions[] = DataCondition::FieldsEqual( $this->pSet->getTableKeys(), $s );913 }914 915 $dc->filter = DataCondition::_And( array(916 $this->getSecurityCondition(),917 DataCondition::_Or( $conditions )918 ));919 920 return $dc;921 } 922}923?>