function getWhereClauseParams($table, $searchName, $search, $searchType, &$langKeys) { global $ALLOWED_NUMERIC_QUERY_OPERATORS, $ALLOWED_STRING_QUERY_OPERATORS; $haveAnyFulltextQueryOperators = false; $searchWhereClausePHP = ''; $searchWhereClauseOr = ''; $searchWhereClauseIndent = $searchType == 'autocomplete' ? "\n\t\t\t" : "\n\t\t"; $searchWhereClauseConcat = ''; $searchWhereAssignments = array(); $searchWhereAssignmentsIndent = $searchType == 'autocomplete' ? "\t\t" : "\t"; $searchableColumnsPHPArray = "array(\n"; $searchableColumnsPHPArraySep = ''; if (isset($search['searchableColumns'])) { foreach ($search['searchableColumns'] as $key => $column) { $queryOperator = isset($column['queryOperator']) ? $column['queryOperator'] : ''; if ($queryOperator == 'fulltext') { $haveAnyFulltextQueryOperators = true; break; } } foreach ($search['searchableColumns'] as $key => $column) { $colName = isset($column['columnName']) ? $column['columnName'] : $key; $pfx = isset($column['tableAlias']) ? $column['tableAlias'] : 'pri'; if ($pfx != '') { $pfx = $pfx .= '.'; } $title = isset($column['title']) ? $column['title'] : ''; if ($title == '') { $title = identifierToHumanReadable($colName); } $sqlType = isset($column['sqlType']) ? $column['sqlType'] : ''; $queryOperator = isset($column['queryOperator']) ? $column['queryOperator'] : ''; switch ($sqlType) { case 'integer': case 'smallint': case 'bigint': case 'decimal': if (!in_array($queryOperator, $ALLOWED_NUMERIC_QUERY_OPERATORS)) { $queryOperator = '='; } if (isset($column['unsignedSearch']) && $column['unsignedSearch']) { if ($searchType == 'search') { $searchWhereClausePHP .= $searchWhereClauseIndent . $searchWhereClauseConcat . var_export_normal_precision($searchWhereClauseOr . '(? <> 0 and abs(' . $pfx . $colName . ') ' . $queryOperator . ' ?)', true); if ($searchWhereClauseOr == '') { $searchWhereClauseOr = ' or '; } if ($searchWhereClauseConcat == '') { $searchWhereClauseConcat = '.'; } $searchWhereAssignments[] = $searchWhereAssignmentsIndent . "\$ps->setInt(((\$queryCol == '') || (\$queryCol == " . var_export_normal_precision($pfx . $colName, true) . ")) ? 1 : 0);"; } else { $searchWhereClausePHP .= $searchWhereClauseIndent . $searchWhereClauseConcat . var_export_normal_precision($searchWhereClauseOr . 'abs(' . $pfx . $colName . ') ' . $queryOperator . ' ?', true); if ($searchWhereClauseOr == '') { $searchWhereClauseOr = ' or '; } if ($searchWhereClauseConcat == '') { $searchWhereClauseConcat = '.'; } } if ($sqlType == 'decimal') { $searchWhereAssignments[] = $searchWhereAssignmentsIndent . "\$ps->setDouble(abs((double)trim(\$query)));"; } else { $searchWhereAssignments[] = $searchWhereAssignmentsIndent . "\$ps->setInt(abs((int)trim(\$query)));"; } } else { if ($searchType == 'search') { $searchWhereClausePHP .= $searchWhereClauseIndent . $searchWhereClauseConcat . var_export_normal_precision($searchWhereClauseOr . '(? <> 0 and ' . $pfx . $colName . ' ' . $queryOperator . ' ?)', true); if ($searchWhereClauseOr == '') { $searchWhereClauseOr = ' or '; } if ($searchWhereClauseConcat == '') { $searchWhereClauseConcat = '.'; } $searchWhereAssignments[] = $searchWhereAssignmentsIndent . "\$ps->setInt(((\$queryCol == '') || (\$queryCol == " . var_export_normal_precision($pfx . $colName, true) . ")) ? 1 : 0);"; } else { $searchWhereClausePHP .= $searchWhereClauseIndent . $searchWhereClauseConcat . var_export_normal_precision($searchWhereClauseOr . $pfx . $colName . ' ' . $queryOperator . ' ?', true); if ($searchWhereClauseOr == '') { $searchWhereClauseOr = ' or '; } if ($searchWhereClauseConcat == '') { $searchWhereClauseConcat = '.'; } } if ($sqlType == 'decimal') { $searchWhereAssignments[] = $searchWhereAssignmentsIndent . "\$ps->setDouble(\$query);"; } else { $searchWhereAssignments[] = $searchWhereAssignmentsIndent . "\$ps->setInt(\$query);"; } } $langKey = $searchName . '_search.searchableColumn.' . $colName . '.title'; if (is_array($langKeys)) { $langKeys[$langKey] = $title; } $searchableColumnsPHPArray .= sprintf(<<<EOF %s\t\t(object)array( \t\t\t'name'=>%s, \t\t\t'pfx'=>%s, \t\t\t'title'=>_t(%s, %s), \t\t\t'sqlType'=>%s, \t\t\t'queryOperator'=>%s, \t\t), EOF , $searchableColumnsPHPArraySep, var_export_normal_precision($colName, true), var_export_normal_precision($pfx, true), var_export_normal_precision($langKey, true), var_export_normal_precision($title, true), var_export_normal_precision($sqlType, true), var_export_normal_precision($queryOperator, true)); if ($searchableColumnsPHPArraySep == '') { $searchableColumnsPHPArraySep = "\n"; } break; case 'char': case 'varchar': case 'text': if (!in_array($queryOperator, $ALLOWED_STRING_QUERY_OPERATORS)) { $queryOperator = '='; } if ($queryOperator == 'beginsWith' || $queryOperator == 'contains' || $queryOperator == 'endsWith' || $queryOperator == 'fulltext') { if ($searchType == 'search') { $tmppfx = '(? <> 0 and ('; $tmpsfx = '.\'))\''; } else { $tmppfx = $tmpsfx = ''; } $searchWhereClausePHP .= $searchWhereClauseIndent . $searchWhereClauseConcat . ($searchWhereClauseOr != '' || $tmppfx != '' ? var_export_normal_precision($searchWhereClauseOr . $tmppfx, true) . '.' : ''); if ($queryOperator == 'fulltext') { $searchWhereClausePHP .= '($canDoFulltextSearch ? (' . var_export_normal_precision('? = \'\' or match(' . $pfx . $colName . ') against (? in boolean mode)', true) . ') : ('; } $searchWhereClausePHP .= '($db->hasCaseInsensitiveLike ? ' . var_export_normal_precision($pfx . $colName, true) . ' : ' . var_export_normal_precision('lower(' . $pfx . $colName . ')', true) . ')' . '.\' \'.$db->likeOperator.\' \'.' . '($db->hasCaseInsensitiveLike ? \'?\' : \'lower(?)\')'; if ($queryOperator == 'fulltext') { $searchWhereClausePHP .= '))'; } $searchWhereClausePHP .= $tmpsfx; if ($searchWhereClauseOr == '') { $searchWhereClauseOr = ' or '; } if ($searchWhereClauseConcat == '') { $searchWhereClauseConcat = '.'; } } else { $searchWhereClausePHP .= $searchWhereClauseOr . '(? <> 0 and ' . $pfx . $colName . ' ' . $queryOperator . ' ?)'; if ($searchWhereClauseOr == '') { $searchWhereClauseOr = ' or '; } if ($searchWhereClauseConcat == '') { $searchWhereClauseConcat = '.'; } } if ($searchType == 'search') { $searchWhereAssignments[] = $searchWhereAssignmentsIndent . "\$ps->setInt(((\$queryCol == '') || (\$queryCol == " . var_export_normal_precision($pfx . $colName, true) . ")) ? 1 : 0);"; } if ($queryOperator == 'beginsWith') { $searchWhereAssignments[] = $searchWhereAssignmentsIndent . "\$ps->setString(\$query.'%');"; } else { if ($queryOperator == 'contains') { $searchWhereAssignments[] = $searchWhereAssignmentsIndent . "\$ps->setString('%'.\$query.'%');"; } else { if ($queryOperator == 'endsWith') { $searchWhereAssignments[] = $searchWhereAssignmentsIndent . "\$ps->setString('%'.\$query);"; } else { if ($queryOperator == 'fulltext') { $searchWhereAssignments[] = $searchWhereAssignmentsIndent . "if (\$canDoFulltextSearch) { \$ps->setString(\$ftquery); \$ps->setString(\$ftquery); } else { \$ps->setString('%'.\$query.'%'); }"; } else { $searchWhereAssignments[] = $searchWhereAssignmentsIndent . "\$ps->setString(\$query);"; } } } } $langKey = $searchName . '_search.searchableColumn.' . $colName . '.title'; if (is_array($langKeys)) { $langKeys[$langKey] = $title; } $searchableColumnsPHPArray .= sprintf(<<<EOF %s\t\t(object)array( \t\t\t'name'=>%s, \t\t\t'pfx'=>%s, \t\t\t'title'=>_t(%s, %s), \t\t\t'sqlType'=>%s, \t\t\t'queryOperator'=>%s, \t\t), EOF , $searchableColumnsPHPArraySep, var_export_normal_precision($colName, true), var_export_normal_precision($pfx, true), var_export_normal_precision($langKey, true), var_export_normal_precision($title, true), var_export_normal_precision($sqlType, true), var_export_normal_precision($queryOperator, true)); if ($searchableColumnsPHPArraySep == '') { $searchableColumnsPHPArraySep = "\n"; } break; default: fprintf(STDERR, "WARNING: Invalid sqlType \"%s\" in searchable column \"%s\" in \"%s\" %s in %s table YAML file.\n", $sqlType, $colName, $searchName, $searchType, $table->tableName); } } } $searchableColumnsPHPArray .= "\n\t)"; $andWhere = ''; if (isset($search['andWhere'])) { $andWhere = trim((string) $search['andWhere']); } $andWhereAssignments = array(); if (isset($search['andWhereAssignments']) && is_array($search['andWhereAssignments'])) { foreach ($search['andWhereAssignments'] as $assignment) { $expression = isset($assignment['expression']) && is_string($assignment['expression']) ? $assignment['expression'] : "''"; switch (isset($assignment['psType']) ? $assignment['psType'] : 'string') { case 'boolean': $andWhereAssignments[] = "\t\$ps->setBoolean({$expression});"; break; case 'int': $andWhereAssignments[] = "\t\$ps->setInt({$expression});"; break; case 'float': $andWhereAssignments[] = "\t\$ps->setFloat({$expression});"; break; case 'double': $andWhereAssignments[] = "\t\$ps->setDouble({$expression});"; break; case 'string': default: $andWhereAssignments[] = "\t\$ps->setString({$expression});"; break; case 'match': $andWhereAssignments[] = "\t\$ps->setString('%'.{$expression}.'%');"; break; case 'binary': $andWhereAssignments[] = "\t\$ps->setBinary({$expression});"; break; } } } return array('haveAnyFulltextQueryOperators' => $haveAnyFulltextQueryOperators, 'searchWhereClausePHP' => $searchWhereClausePHP, 'searchWhereAssignments' => $searchWhereAssignments, 'searchableColumnsPHPArray' => $searchableColumnsPHPArray, 'searchableColumnsPHPArraySep' => $searchableColumnsPHPArraySep, 'andWhere' => $andWhere, 'andWhereAssignments' => $andWhereAssignments); }
function processTable($table, $idCol, $cfg) { global $SEARCH_PRESENTATION_DATATABLES_ONLY_PARAMS, $SEARCH_PRESENTATION_DATATABLES_ONLY_COLUMN_PARAMS, $SEARCH_PRESENTATION_AJAXSEARCHGRID_ONLY_PARAMS, $SEARCH_PRESENTATION_AJAXSEARCHGRID_ONLY_COLUMN_PARAMS, $templatesDir, $cfgDir, $docroot, $oldFileLayout, $enableLangFiles; $generatedFileMessage = <<<EOF // DO NOT EDIT THIS FILE. // This file was generated by crudgen. // If you need to customize this file, please edit the corresponding // yaml file in the gencfg directory, and then re-generate this file // by running crudgen, passing in the table name. EOF; $mainOkCheck = $oldFileLayout ? '' : "\nif ((!isset(\$mainOk)) || (!\$mainOk)) exit();\n"; $jaxInclude = $cfg['jaxInclude']; $jaxJQuery = $cfg['jaxJQuery']; $jaxJS = $cfg['jaxJS']; $classAutoloadPaths = isset($cfg['classAutoloadPaths']) ? trim($cfg['classAutoloadPaths']) : ''; $loggedInId = isset($cfg['loggedInId']) ? trim($cfg['loggedInId']) : ''; if ($loggedInId == '') { $loggedInId = '$loggedInUser->id'; } if ($classAutoloadPaths != '') { $classAutoloadPathsInit = sprintf("\$__CLASS_AUTO_LOAD_CLASS_PATHS = %s;\n", $classAutoloadPaths); } else { $classAutoloadPathsInit = ''; } if (!isset($cfg['cruds']) || !is_array($cfg['cruds'])) { $cfg['cruds'] = array(); } foreach ($cfg['cruds'] as $crudName => $crud) { $mainHooksInclude = $oldFileLayout ? "@include './{$crudName}_hooks.include.php';\n" : ''; $viewHooksInclude = $oldFileLayout ? "@include './{$crudName}_view_hooks.include.php';\n" : ''; if ($oldFileLayout) { $controllerIncludes = <<<EOF \t\$headerScripts[] = str_replace('//', '/', dirname(\$_SERVER['SCRIPT_NAME']).'/{$crudName}_controller.js'); \tif (file_exists(dirname(__FILE__).'/{$crudName}_controller_hooks.js')) { \t\t\$headerScripts[] = str_replace('//', '/', dirname(\$_SERVER['SCRIPT_NAME']).'/{$crudName}_controller_hooks.js'); \t} EOF; } else { $controllerIncludes = <<<EOF \t\$headerScripts[] = str_replace('//', '/', dirname(\$_SERVER['SCRIPT_NAME']).'/generated/{$crudName}_controller_generated.js'); \tif (file_exists(dirname(dirname(__FILE__)).'/{$crudName}_controller.js')) { \t\t\$headerScripts[] = str_replace('//', '/', dirname(\$_SERVER['SCRIPT_NAME']).'/{$crudName}_controller.js'); \t} EOF; } $langKeys = array(); if (array_key_exists('tableDescription', $cfg)) { $langKeys['crud.' . $crudName . '.tableDescription'] = $cfg['tableDescription']; } if (array_key_exists('tableDescriptions', $cfg)) { $langKeys['crud.' . $crudName . '.tableDescriptions'] = $cfg['tableDescriptions']; } $outputPath = isset($crud['outputPath']) && is_string($crud['outputPath']) ? $crud['outputPath'] : ''; $outputDir = $docroot . '/' . $outputPath; $generatedOutputDir = $oldFileLayout ? $outputDir : $outputDir . '/generated'; @mkdir($generatedOutputDir, 0777, true); $numDirsDeepUnderHTML = calcDirDepth($outputPath) + ($oldFileLayout ? 0 : 1); if (isset($crud['docRootPath'])) { $docRootPath = '"' . addcslashes($crud['docRootPath']) . '"'; } else { $docRootPath = 'dirname(__FILE__)'; for ($i = 0; $i < $numDirsDeepUnderHTML; $i++) { $docRootPath = "dirname({$docRootPath})"; } } $phpIncludes = getPHPClassesAndIncludes($crud, $numDirsDeepUnderHTML); $postInitPHPIncludes = getPostInitPHPIncludes($crud, $numDirsDeepUnderHTML); $crudSearchCommand = 'search' . ucfirst($table->tableName) . 's'; $crudSearchPresentation = 'dataTables'; $crudSearchTableDisplayColumns = ''; /// TODO: Build these two up based on the column definitions. $crudSearchGridHeaderColumnsHTML = ''; $crudSearchGridBodyColumnsHTML = ''; $crudSearchColumnNames = array(); $crudSearchTableCallbacks = ''; if (isset($crud['crudSearch']) && is_array($crud['crudSearch'])) { $crudSearch = $crud['crudSearch']; // If we encounter a "likePopupSearch" attribute for this crudSearch, it must // reference an existing popupSearch defined in the same YAML file. // When this happens, we merge the popupSearch's attributes with the crudSearch's // attributes, allowing the crudSearch's attributes to override the popupSearch's // attributes of the same name. if (isset($crudSearch['likePopupSearch'])) { if (!is_string($crudSearch['likePopupSearch']) || !isset($cfg['popupSearches']) || !isset($cfg['popupSearches'][$crudSearch['likePopupSearch']]) || !is_array($cfg['popupSearches'][$crudSearch['likePopupSearch']])) { fprintf(STDERR, "Invalid likePopupSearch entry in crudSearch section.\n"); return false; } $crudSearch = array_merge($cfg['popupSearches'][$crudSearch['likePopupSearch']], $crudSearch); } if (isset($crudSearch['searchCommand']) && is_string($crudSearch['searchCommand'])) { $crudSearchCommand = $crudSearch['searchCommand']; } if (isset($crudSearch['searchPresentation']) && is_string($crudSearch['searchPresentation'])) { $crudSearchPresentation = $crudSearch['searchPresentation']; if ($crudSearchPresentation != 'dataTables' && $crudSearchPresentation != 'AJAXSearchGrid') { $crudSearchPresentation = 'dataTables'; } } if ($crudSearchPresentation != 'dataTables') { $badattrs = array_intersect(array_keys($crudSearch), $SEARCH_PRESENTATION_DATATABLES_ONLY_PARAMS); if (!empty($badattrs)) { fprintf(STDERR, "WARNING: The following popup search / CRUD search attributes are ignored when searchPresentation is not set to dataTables:\n %s\n", implode("\n ", $badattrs)); } } if ($crudSearchPresentation != 'AJAXSearchGrid') { $badattrs = array_intersect(array_keys($crudSearch), $SEARCH_PRESENTATION_AJAXSEARCHGRID_ONLY_PARAMS); if (!empty($badattrs)) { fprintf(STDERR, "WARNING: The following popup search / CRUD search attributes are ignored when searchPresentation is not set to AJAXSearchGrid:\n %s\n", implode("\n ", $badattrs)); } } $badcolattrs_dataTables = array(); $badcolattrs_AJAXSearchGrid = array(); if (isset($crudSearch['columns']) && is_array($crudSearch['columns'])) { foreach ($crudSearch['columns'] as $col) { if ($crudSearchPresentation != 'dataTables') { $badattrs = array_intersect(array_keys($col), $SEARCH_PRESENTATION_DATATABLES_ONLY_COLUMN_PARAMS); if (!empty($badattrs)) { $badcolattrs_dataTables = array_unique(array_merge($badcolattrs_dataTables, $badattrs)); } } if ($crudSearchPresentation != 'AJAXSearchGrid') { $badattrs = array_intersect(array_keys($col), $SEARCH_PRESENTATION_AJAXSEARCHGRID_ONLY_COLUMN_PARAMS); if (!empty($badattrs)) { $badcolattrs_AJAXSearchGrid = array_unique(array_merge($badcolattrs_AJAXSearchGrid, $badattrs)); } } } unset($col); } if (!empty($badcolattrs_dataTables)) { fprintf(STDERR, "WARNING: The following popup search / CRUD search column attributes are ignored when searchPresentation is not set to dataTables:\n %s\n", implode("\n ", $badcolattrs_dataTables)); } if (!empty($badcolattrs_AJAXSearchGrid)) { fprintf(STDERR, "WARNING: The following popup search / CRUD search column attributes are ignored when searchPresentation is not set to AJAXSearchGrid:\n %s\n", implode("\n ", $badcolattrs_AJAXSearchGrid)); } unset($badattrs, $badcolattrs_dataTables, $badcolattrs_AJAXSearchGrid); $beforeSearchCallback = isset($crudSearch['beforeSearchCallback']) ? $crudSearch['beforeSearchCallback'] : ''; $modifyURLCallback = isset($crudSearch['modifyURLCallback']) ? $crudSearch['modifyURLCallback'] : ''; $afterSearchCallback = isset($crudSearch['afterSearchCallback']) ? $crudSearch['afterSearchCallback'] : ''; $crudSearchCallbacks = ''; if ($beforeSearchCallback != '') { $crudSearchCallbacks .= ",\n\t\t\tbeforeSearchCallback:{$beforeSearchCallback}"; } if ($modifyURLCallback != '') { $crudSearchCallbacks .= ",\n\t\t\tmodifyURLCallback:{$modifyURLCallback}"; } if ($afterSearchCallback != '') { $crudSearchCallbacks .= ",\n\t\t\tafterSearchCallback:{$afterSearchCallback}"; } if (!isset($crudSearch['columns']) || !is_array($crudSearch['columns'])) { fprintf(STDERR, "Missing or invalid columns list on crudSearch for %s CRUD. Skipping this CRUD.\n", $crudSearchName); continue; } $crudSearchTableDisplayColumns = getDataTableColumns($crudSearch['columns'], 'crud.' . $crudName, $langKeys); if ($crudSearchTableDisplayColumns != '') { $crudSearchTableDisplayColumns .= ",\n"; } $crudSearchColumnNames = is_array($crudSearch['columns']) ? array_keys($crudSearch['columns']) : array(); if (isset($crudSearch['invisibleColumns']) && is_array($crudSearch['invisibleColumns'])) { foreach ($crudSearch['invisibleColumns'] as $icn) { if (!in_array($icn, $crudSearchColumnNames)) { $crudSearchColumnNames[] = $icn; } } } $crudSearchColumnFilters = isset($crudSearch['columnFilters']) && is_array($crudSearch['columnFilters']) ? $crudSearch['columnFilters'] : array(); $tmp = '{'; $sep = ''; foreach ($crudSearchColumnFilters as $funcname => $functext) { $tmp .= $sep . json_encode($funcname) . ':' . $functext; if ($sep == '') { $sep = ','; } } $tmp .= '}'; $crudSearchColumnFilters = $tmp; unset($tmp); $crudSearchExtraQueryParams = isset($crudSearch['extraQueryParams']) && is_array($crudSearch['extraQueryParams']) ? (object) $crudSearch['extraQueryParams'] : (object) array(); list($crudSearchGridHeaderColumnsHTML, $crudSearchGridBodyColumnsHTML) = getAJAXSearchGridColumns($crudSearch['columns'], 'crud.' . $crudName, $langKeys); $srch = array(); $repl = array(); foreach ($langKeys as $langKey => $text) { $srch[] = '<<langkey>>' . $langKey . '<</langkey>>'; $repl[] = '<?php _e(' . var_export_normal_precision($langKey, true) . ', ' . var_export_normal_precision($text, true) . '); ?' . '>'; } $crudSearchGridHeaderColumnsHTML = str_replace($srch, $repl, $crudSearchGridHeaderColumnsHTML); $crudSearchGridBodyColumnsHTML = str_replace($srch, $repl, $crudSearchGridBodyColumnsHTML); $crudSearchDefaultSorts = isset($crudSearch['defaultSorts']) && is_array($crudSearch['defaultSorts']) ? $crudSearch['defaultSorts'] : array(); if (isset($crudSearch['fnDrawCallback']) && is_string($crudSearch['fnDrawCallback'])) { $s = trim($crudSearch['fnDrawCallback']); if ($s != '') { $crudSearchTableCallbacks .= ', fnDrawCallback: ' . $s; } unset($s); } if (isset($crudSearch['fnServerData']) && is_string($crudSearch['fnServerData'])) { $s = trim($crudSearch['fnServerData']); if ($s != '') { $crudSearchTableCallbacks .= ', fnServerData: ' . $s; } unset($s); } } $ajaxAutocompleteInitJS = ''; $ajaxComboboxInitJS = ''; $openContainers = array(); $hiddenFormFields = ''; $formFields = ''; if (!isset($crud['formFields']) || !is_array($crud['formFields'])) { $crud['formFields'] = array(); } foreach ($crud['formFields'] as $fieldName => $field) { $thisFormField = ''; $inputType = isset($field['inputType']) ? $field['inputType'] : 'text'; if ($inputType == 'select' && isset($field['multiple']) && $field['multiple'] && substr($fieldName, -2) != '[]') { fprintf(STDERR, "WARNING: For inputType: select, multiple: Yes, input name must end with '[]' in input \"%s\" in formFields section.\n", $fieldName); } $id = isset($field['id']) ? $field['id'] : $fieldName; if (substr($id, -2) == '[]') { $id = trim(substr($id, 0, strlen($id) - 2)); } $titleLangKey = 'crud.' . $crudName . '.form.input.' . $id . '.label'; $title = isset($field['title']) ? $field['title'] : ucwords(str_replace('_', ' ', $id)); $langKeys[$titleLangKey] = $title; $titleExpr = "_t('" . $titleLangKey . "', " . var_export_normal_precision($title, true) . ")"; if (($placeholder = isset($field['placeholder']) ? $field['placeholder'] : '') != '') { $placeholderLangKey = 'crud.' . $crudName . '.form.input.' . $id . '.placeholder'; $langKeys[$placeholderLangKey] = $placeholder; $placeholderExpr = "<?php _e('" . $placeholderLangKey . "', " . var_export_normal_precision($placeholder, true) . "); ?>"; } else { $placeholderLangKey = ''; $placeholderExpr = ''; } $onclickTag = isset($field['onclick']) && $field['onclick'] != '' ? ' onclick="' . $field['onclick'] . '"' : ''; $onchangeTag = isset($field['onchange']) && $field['onchange'] != '' ? ' onchange="' . $field['onchange'] . '"' : ''; $containerCSSClass = isset($field['containerCSSClass']) && $field['containerCSSClass'] != '' ? trim($field['containerCSSClass']) : ''; $cssClass = isset($field['cssClass']) && $field['cssClass'] != '' ? trim($field['cssClass']) : ''; $helpTopic = isset($field['helpTopic']) && $field['helpTopic'] != '' ? trim($field['helpTopic']) : ''; $onPopupSearch = isset($field['onPopupSearch']) ? trim($field['onPopupSearch']) : ''; $descriptionField = isset($field['descriptionField']) ? $field['descriptionField'] : ''; $descriptionFieldSize = isset($field['descriptionFieldSize']) ? (int) $field['descriptionFieldSize'] : 0; if ($descriptionFieldSize <= 0) { $descriptionFieldSize = 40; } $descriptionFieldMaxLength = isset($field['descriptionFieldMaxLength']) ? (int) $field['descriptionFieldMaxLength'] : 0; if ($descriptionFieldMaxLength < 0) { $descriptionFieldMaxLength = 0; } if ($onPopupSearch != '') { if (substr($onPopupSearch, strlen($onPopupSearch) - 1) != ';') { $onPopupSearch .= ';'; } $onPopupSearch .= 'return false;'; // Default popup search icon (controlled by CSS). $searchDescHTML = '<a class="btn btn-default popupSearchLink" href="#" onclick="' . $onPopupSearch . '"><i class="glyphicon glyphicon-search"></i></a>'; } else { $searchDescHTML = ''; } if ($descriptionField != '') { $descriptionClassTag = getBootstrapTextInputSizeClass($descriptionFieldSize); $descriptionClassTag = trim('form-control ' . $descriptionClassTag); if ($descriptionClassTag != '') { $descriptionClassTag = ' class="' . $descriptionClassTag . '"'; } $searchDescHTML .= ' - <input type="text" name="' . $descriptionField . '" id="' . $descriptionField . '"' . $descriptionClassTag . ($descriptionFieldSize > 0 ? ' size="' . $descriptionFieldSize . '"' : '') . ($descriptionFieldMaxLength > 0 ? ' maxlength="' . $descriptionFieldMaxLength . '"' : '') . ' readonly="readonly" disabled="disabled"/>'; } // For inputs which use label-before-input layout, open the row and emit the // first cell containing the label. For checkboxes, open the row and emit a // blank cell. In both cases, open the second cell so we're ready to emit // the input element. switch ($inputType) { case 'text': case 'password': case 'textarea': case 'select': case 'file': case 'radio': $containerCSSClass = trim('form-group ' . $containerCSSClass); $thisFormField .= '<div class="' . $containerCSSClass . '" id="' . $id . 'TR">' . '<label for="' . $id . '" class="col-sm-2 control-label"><?php echo ' . $titleExpr . '; ?>:</label>' . '<div class="col-sm-10">' . '<div id="fieldErrorMsg_' . $id . '" class="fieldErrorMsg"></div>'; break; case 'checkbox': $containerCSSClass = trim('form-group ' . $containerCSSClass); $thisFormField .= '<div class="' . $containerCSSClass . '" id="' . $id . 'TR">' . '<label class="col-sm-2 control-label"></label>' . '<div class="col-sm-10">' . '<div id="fieldErrorMsg_' . $id . '" class="fieldErrorMsg"></div>' . '<label for="' . $id . '">'; break; } switch ($inputType) { case 'hidden': $hiddenFormFields .= '<input type="hidden" name="' . $fieldName . '" id="' . $id . '"/>' . "\n"; break; case 'text': case 'password': $size = isset($field['size']) ? (int) $field['size'] : 0; $maxlength = isset($field['maxlength']) ? (int) $field['maxlength'] : 0; $cssClass = trim('form-control ' . $cssClass); $cssClass = mergeSizeClass($cssClass, getBootstrapTextInputSizeClass($size)); $readonlyTag = isset($field['readonly']) && $field['readonly'] ? ' readonly="readonly"' : ''; $disabledTag = isset($field['disabled']) && $field['disabled'] ? ' disabled="disabled"' : ''; // Disabling the id field causes updates to become inserts. Don't allow that. if ($fieldName == $idCol) { $disabledTag = ''; } $thisFormField .= '<input type="' . $inputType . '" name="' . $fieldName . '" id="' . $id . '"' . ($size > 0 ? ' size="' . $size . '"' : '') . ($maxlength > 0 ? ' maxlength="' . $maxlength . '"' : '') . ($placeholderExpr != '' ? " placeholder=\"{$placeholderExpr}\"" : '') . ($cssClass != '' ? ' class="' . $cssClass . '"' : '') . ($helpTopic != '' ? ' data-help-topic="' . $helpTopic . '"' : '') . $readonlyTag . $disabledTag . $onclickTag . $onchangeTag . '/>' . $searchDescHTML; break; case 'textarea': $rows = isset($field['rows']) ? (int) $field['rows'] : 20; $cols = isset($field['cols']) ? (int) $field['cols'] : 20; $cssClass = trim('form-control ' . $cssClass); $cssClass = mergeSizeClass($cssClass, getBootstrapTextInputSizeClass($cols)); $readonlyTag = isset($field['readonly']) && $field['readonly'] ? ' readonly="readonly"' : ''; $disabledTag = isset($field['disabled']) && $field['disabled'] ? ' disabled="disabled"' : ''; // Disabling the id field causes updates to become inserts. Don't allow that. if ($fieldName == $idCol) { $disabledTag = ''; } $thisFormField .= '<textarea name="' . $fieldName . '" id="' . $id . '" rows="' . $rows . '" cols="' . $cols . '"' . ($placeholderExpr != '' ? " placeholder=\"{$placeholderExpr}\"" : '') . ($cssClass != '' ? ' class="' . $cssClass . '"' : '') . ($helpTopic != '' ? ' data-help-topic="' . $helpTopic . '"' : '') . $readonlyTag . $disabledTag . $onclickTag . $onchangeTag . '></textarea>' . $searchDescHTML; break; case 'select': $size = isset($field['size']) ? (int) $field['size'] : 0; $cssClass = trim('form-control ' . $cssClass); $multipleTag = isset($field['multiple']) && $field['multiple'] ? ' multiple="multiple"' : ''; $disabledTag = isset($field['disabled']) && $field['disabled'] ? ' disabled="disabled"' : ''; // Disabling the id field causes updates to become inserts. Don't allow that. if ($fieldName == $idCol) { $disabledTag = ''; } $thisFormField .= '<select' . $multipleTag . ' name="' . $fieldName . '" id="' . $id . '"' . ($size > 0 ? ' size="' . $size . '"' : '') . ($cssClass != '' ? ' class="' . $cssClass . '"' : '') . ($helpTopic != '' ? ' data-help-topic="' . $helpTopic . '"' : '') . $disabledTag . $onclickTag . $onchangeTag . ">\n"; if (isset($field['optionsFromAssociativeArray']) && is_string($field['optionsFromAssociativeArray']) && $field['optionsFromAssociativeArray'] != '') { $thisFormField .= '<?php foreach (' . $field['optionsFromAssociativeArray'] . ' as $__val=>$__desc) { ?' . ">\n" . ' <option value="<?php echo htmlspecialchars($__val); ?' . '>">' . '<?php echo htmlspecialchars($__desc); ?' . '>' . "</option>\n" . '<?php } ?' . ">\n"; } else { $options = isset($field['options']) && is_array($field['options']) ? $field['options'] : array(); foreach ($options as $option => $optionParams) { $optionTitleLangKey = 'crud.' . $crudName . '.form.input.' . $id . '.option.' . optionToName($option) . '.title'; $title = isset($optionParams['title']) ? $optionParams['title'] : $option; $langKeys[$optionTitleLangKey] = $title; $optionTitleExpr = "_t('" . $optionTitleLangKey . "', " . var_export_normal_precision($title, true) . ")"; $thisFormField .= ' <option value="' . htmlspecialchars($option) . '">' . '<?php echo ' . $optionTitleExpr . '; ?>' . "</option>\n"; } } $thisFormField .= '</select>' . $searchDescHTML; break; case 'file': $cssClass = trim('form-control ' . $cssClass); $readonlyTag = isset($field['readonly']) && $field['readonly'] ? ' readonly="readonly"' : ''; $disabledTag = isset($field['disabled']) && $field['disabled'] ? ' disabled="disabled"' : ''; // Disabling the id field causes updates to become inserts. Don't allow that. if ($fieldName == $idCol) { $disabledTag = ''; } $acceptTag = isset($field['accept']) ? trim($field['accept']) : ''; if ($acceptTag != '') { $acceptTag = " accept=\"{$acceptTag}\""; } $thisFormField .= '<input type="' . $inputType . '" name="' . $fieldName . '" id="' . $id . '"' . $acceptTag . ($cssClass != '' ? ' class="' . $cssClass . '"' : '') . ($helpTopic != '' ? ' data-help-topic="' . $helpTopic . '"' : '') . $readonlyTag . $disabledTag . $onclickTag . $onchangeTag . '/>' . $searchDescHTML; break; case 'checkbox': $disabledTag = isset($field['disabled']) && $field['disabled'] ? ' disabled="disabled"' : ''; $value = isset($field['value']) ? $field['value'] : '1'; // Disabling the id field causes updates to become inserts. Don't allow that. if ($fieldName == $idCol) { $disabledTag = ''; } $thisFormField .= '<input type="' . $inputType . '" name="' . $fieldName . '" id="' . $id . '"' . ' value="' . $value . '"' . ($cssClass != '' ? ' class="' . $cssClass . '"' : '') . ($helpTopic != '' ? ' data-help-topic="' . $helpTopic . '"' : '') . $disabledTag . $onclickTag . $onchangeTag . '/>' . ' <?php echo ' . $titleExpr . '; ?></label>' . $searchDescHTML; break; case 'radio': $disabledTag = isset($field['disabled']) && $field['disabled'] ? ' disabled="disabled"' : ''; // Disabling the id field causes updates to become inserts. Don't allow that. if ($fieldName == $idCol) { $disabledTag = ''; } if (isset($field['optionsFromAssociativeArray']) && is_string($field['optionsFromAssociativeArray']) && $field['optionsFromAssociativeArray'] != '') { $thisFormField .= '<?php $__i = 0;' . "\n" . 'foreach (' . $field['optionsFromAssociativeArray'] . ' as $__val=>$__desc) { ?' . ">\n\t" . '$__i++;' . "\n" . '<div><input type="radio" name="' . $fieldName . '" id="' . $id . '__<?php echo $__i; ?>" value="<?php echo htmlspecialchars($__val); ?' . '>"' . ($cssClass != '' ? ' class="' . $cssClass . '"' : '') . ($helpTopic != '' ? ' data-help-topic="' . $helpTopic . '"' : '') . $disabledTag . '/>' . '<label for="' . $id . '__<?php echo $__i; ?>"><?php echo htmlspecialchars($__desc); ?' . '>' . "</label></div>\n" . '<?php } ?' . ">\n"; } else { $options = isset($field['options']) && is_array($field['options']) ? $field['options'] : array(); $__i = 0; foreach ($options as $option => $optionParams) { $__i++; $optionTitleLangKey = 'crud.' . $crudName . '.form.input.' . $id . '.option.' . optionToName($option) . '.title'; $title = isset($optionParams['title']) ? $optionParams['title'] : $option; $langKeys[$optionTitleLangKey] = $title; $optionTitleExpr = "_t(" . $optionTitleLangKey . "', " . var_export_normal_precision($title, true) . ")"; $thisFormField .= '<div><input type="radio" name="' . $fieldName . '" id="' . $id . '__' . $__i . '" value="' . htmlspecialchars($option) . '"' . ($cssClass != '' ? ' class="' . $cssClass . '"' : '') . ($helpTopic != '' ? ' data-help-topic="' . $helpTopic . '"' : '') . $disabledTag . '/>' . '<label for="' . $id . '__' . $__i . '">' . '<?php echo ' . $optionTitleExpr . '; ?>' . "</label></div>\n"; } } $thisFormField .= $searchDescHTML . "\n"; break; case 'htmlfragment': $thisFormField .= isset($field['html']) ? $field['html'] . "\n" : ''; break; case 'tabs': // *** WARNING: Tab-related elements manipulate $formFields directly instead of populating $thisFormField. $tabsPosition = isset($field['tabsPosition']) ? strtolower(trim($field['tabsPosition'])) : 'top'; switch ($tabsPosition) { case 'top': $tabbableClasses = 'tabbable'; break; case 'left': $tabbableClasses = 'tabbable tabs-left'; break; case 'right': $tabbableClasses = 'tabbable tabs-right'; break; case 'bottom': $tabbableClasses = 'tabbable tabs-below'; break; default: fprintf(STDERR, "inputType:tabs tabsPosition attribute must be one of top, left, right, bottom, or omitted (defaults to top).\n"); return false; } $ulOpenHTML = " <ul class=\"nav nav-tabs\">\n"; $ulCloseHTML = " </ul>"; $formFields .= "<div class=\"{$tabbableClasses}\">\n"; if ($tabsPosition != 'bottom') { $formFields .= $ulOpenHTML; } $openContainers[] = (object) array('id' => $id, 'type' => 'tabs', 'tabsPosition' => $tabsPosition, 'tabInsertIdx' => strlen($formFields), 'openGroupId' => null, 'numGroups' => 0, 'closingHTMLPart1' => $tabsPosition == 'bottom' ? $ulOpenHTML : '', 'closingHTMLPart2' => '', 'closingHTMLPart3' => ($tabsPosition == 'bottom' ? $ulCloseHTML : '') . "\n</div> <!-- {$id} -->\n</div> <!-- class=\"tabbable\" -->\n"); if ($tabsPosition != 'bottom') { $formFields .= $ulCloseHTML; } $formFields .= "\n <div id=\"{$id}\" class=\"tab-content\">\n"; break; case 'tabsClose': // *** WARNING: Tab-related elements manipulate $formFields directly instead of populating $thisFormField. if (empty($openContainers)) { fprintf(STDERR, "inputType:tabsClose with no corresponding inputType:tabs in formFields section.\n"); return false; } $tc = array_pop($openContainers); if ($tc->type != 'tabs') { fprintf(STDERR, "inputType:tabsClose trying to close non-tabs container of type %s.\n", $tc->type); return false; } if ($tc->openGroupId !== null) { // Close the last tab in this tabs container. $formFields .= " </div> <!-- {$tc->openGroupId} -->\n\n"; } $formFields .= $tc->closingHTMLPart1 . $tc->closingHTMLPart2 . $tc->closingHTMLPart3; break; case 'tab': // *** WARNING: Tab-related elements manipulate $formFields directly instead of populating $thisFormField. if (empty($openContainers)) { fprintf(STDERR, "tab found outside of tabs ... tabsClose in formFields section.\n"); return false; } $tc = $openContainers[count($openContainers) - 1]; if ($tc->type != 'tabs') { fprintf(STDERR, "inputType:tab inside non-tabs container of type %s.\n", $tc->type); return false; } $tc->numGroups++; if ($tc->openGroupId !== null) { // Close the previous tab in this tabs container. $formFields .= " </div> <!-- {$tc->openGroupId} -->\n\n"; } $tc->openGroupId = $id; $liClasses = $tc->numGroups == 1 ? 'active' : ''; $liClassTag = $liClasses != '' ? " class=\"{$liClasses}\"" : ''; $tabLinkHTML = " <li{$liClassTag}><a href=\"#{$id}\" data-toggle=\"tab\"><?php echo {$titleExpr}; ?></a></li>\n"; // For active (first) tab, in addition to the .active selector, the .fade.in // selector must also be present in order to indicate that the fade is complete; // otherwise, the content for the active tab will not be visible until the // user actually clicks on a tab. $divClasses = 'tab-pane fade' . ($tc->numGroups == 1 ? ' in active' : ''); $divClassTag = $divClasses != '' ? " class=\"{$divClasses}\"" : ''; if ($tc->tabsPosition == 'bottom') { $tc->closingHTMLPart2 .= $tabLinkHTML; } else { $formFields = substr($formFields, 0, $tc->tabInsertIdx) . $tabLinkHTML . substr($formFields, $tc->tabInsertIdx); } $formFields .= "\n <div{$divClassTag} id=\"{$id}\">\n"; $tc->tabInsertIdx += strlen($tabLinkHTML); break; case 'accordion': $openContainers[] = (object) array('id' => $id, 'type' => 'accordion', 'tabsPosition' => '', 'tabInsertIdx' => -1, 'openGroupId' => null, 'numGroups' => 0, 'closingHTMLPart1' => '', 'closingHTMLPart2' => '', 'closingHTMLPart3' => "\n</div> <!-- {$id} -->\n"); $thisFormField .= "<div class=\"accordion\" id=\"{$id}\">\n"; break; case 'accordionClose': if (empty($openContainers)) { fprintf(STDERR, "inputType:accordionClose with no corresponding inputType:accordion in formFields section.\n"); return false; } $tc = array_pop($openContainers); if ($tc->type != 'accordion') { fprintf(STDERR, "inputType:accordionClose trying to close non-accordion container of type %s.\n", $tc->type); return false; } if ($tc->openGroupId !== null) { // Close the last group in this accordion container. $thisFormField .= <<<EOF </div> <!-- class=\\"accordion-inner\\" --> </div> <!-- {$tc->openGroupId}-body --> </div> <!-- class=\\"accordion-group\\" --> EOF; } $thisFormField .= $tc->closingHTMLPart1 . $tc->closingHTMLPart2 . $tc->closingHTMLPart3; break; case 'accordionGroup': if (empty($openContainers)) { fprintf(STDERR, "accordionGroup found outside of accordion ... accordionClose in formFields section.\n"); return false; } $tc = $openContainers[count($openContainers) - 1]; if ($tc->type != 'accordion') { fprintf(STDERR, "inputType:accordionGroup inside non-accordion container of type %s.\n", $tc->type); return false; } $tc->numGroups++; if ($tc->openGroupId !== null) { // Close the previous group in this accordion container. $thisFormField .= <<<EOF </div> <!-- class=\\"accordion-inner\\" --> </div> <!-- {$tc->openGroupId}-body --> </div> <!-- class=\\"accordion-group\\" --> EOF; } $tc->openGroupId = $id; $accordionBodyClasses = 'accordion-body collapse'; if ($tc->numGroups == 1) { $accordionBodyClasses .= ' in'; } $accordionBodyClassesTag = $accordionBodyClasses != '' ? " class=\"{$accordionBodyClasses}\"" : ''; $thisFormField .= <<<EOF <div class="accordion-group" id="{$id}"> <div class="accordion-heading"> <a class="accordion-toggle" data-toggle="collapse" data-parent="#{$tc->id}" href="#{$id}-body"><?php echo {$titleExpr}; ?></a> </div> <div id="{$id}-body"{$accordionBodyClassesTag}> <div class="accordion-inner"> EOF; break; /// TODO: Add support for other input types. Button may be the only remaining desirable type which is not supported. /// TODO: Add support for other input types. Button may be the only remaining desirable type which is not supported. default: fprintf(STDERR, "Invalid inputType \"%s\" on input \"%s\" in formFields section. Skipping this input.\n", $inputType, $fieldName); continue; } // switch ($inputType) // Close the layout cell and row. switch ($inputType) { case 'text': case 'password': case 'textarea': case 'select': case 'file': case 'radio': case 'checkbox': $thisFormField .= "</div></div>\n"; break; } if ($inputType == 'text' || $inputType == 'textarea') { $ajaxAutocompleteCommand = isset($field['ajaxAutocompleteCommand']) ? $field['ajaxAutocompleteCommand'] : ''; $ajaxAutocompleteMinLength = isset($field['ajaxAutocompleteMinLength']) ? (int) trim($field['ajaxAutocompleteMinLength']) : 0; if ($ajaxAutocompleteMinLength <= 0) { $ajaxAutocompleteMinLength = 2; } if ($ajaxAutocompleteCommand != '') { $ajaxAutocompleteInitJS .= sprintf(<<<EOF \t\$('#%s').ajaxAutocomplete({ \t\tautocompleteCommand:'%s', \t\tminimumInputLength:%d \t}); EOF , $id, $ajaxAutocompleteCommand, $ajaxAutocompleteMinLength); } // if ($ajaxAutocompleteCommand != '') if (isset($field['ajaxCombobox']) && is_array($field['ajaxCombobox'])) { $arr = $field['ajaxCombobox']; unset($arr['rowFetcher']); $ajaxComboboxInitJS .= "\t\$('#" . $id . "').ajaxCombobox(" . json_encode($arr) . ");\n"; } } // if (($inputType == 'text') || ($inputType == 'textarea')) if ($thisFormField != '') { $formFields .= $thisFormField; } } // foreach ($crud['formFields'] as $fieldName=>$field) if (!empty($openContainers)) { fprintf(STDERR, "One or more open containers (tabs or accordion) without corresponding close (tabsClose, accordionClose) found in formFields section.\n"); return false; } $includesByClass = array(); $filterCode = ''; if (!isset($crud['filters']) || !is_array($crud['filters'])) { $crud['filters'] = array(); } foreach ($crud['filters'] as $fieldName => $filterGroup) { if (!is_array($filterGroup)) { continue; } foreach ($filterGroup as $filter) { if (!isset($filter['class']) || !is_string($filter['class'])) { continue; } $filterClass = $filter['class']; $filterInclude = isset($filter['include']) && is_string($filter['include']) ? $filter['include'] : ''; if (!isset($filter['params']) || !is_array($filter['params'])) { $filter['params'] = array(); } $filterParams = $filter['params']; if (!isset($filterParams['valueName'])) { $filterParams['valueName'] = $fieldName; } if ($filterInclude != '') { if (!isset($includesByClass[$filterClass])) { $includesByClass[$filterClass] = array($filterInclude); } else { if (!in_array($filterInclude, $includesByClass[$filterClass])) { $includesByClass[$filterClass][] = $filterInclude; } } } $paramsEnc = var_export_normal_precision($filterParams, true); $filterCode .= "\t\t\$__filter = new {$filterClass}({$paramsEnc});\n\t\t\$__filter->filter(\$db, \$row);\n\n"; } } $filterCode .= "\t\tunset(\$__filter);\n"; foreach ($includesByClass as $cls => $incs) { foreach ($incs as $inc) { $phpIncludes .= "if (!class_exists('{$cls}', false)) include {$docRootPath}.'/{$inc}';\n"; } } $includesByClass = array(); $validationCode = ''; if (!isset($crud['validators']) || !is_array($crud['validators'])) { $crud['validators'] = array(); } foreach ($crud['validators'] as $fieldName => $validatorGroup) { if (!is_array($validatorGroup)) { continue; } $fieldNameEnc = var_export_normal_precision($fieldName, true); $validatorNameCounts = array(); foreach ($validatorGroup as $validator) { if (!isset($validator['class']) || !is_string($validator['class'])) { continue; } $validatorClass = $validator['class']; $validatorInclude = isset($validator['include']) && is_string($validator['include']) ? $validator['include'] : ''; if (isset($validatorNameCounts[$validatorClass])) { $validatorNameCounts[$validatorClass]++; } else { $validatorNameCounts[$validatorClass] = 1; } if (!isset($validator['params']) || !is_array($validator['params'])) { $validator['params'] = array(); } $validatorParams = $validator['params']; if (!is_array($validatorParams)) { $validatorParams = array(); } if (!isset($validatorParams['valueName'])) { $validatorParams['valueName'] = $fieldName; } if ($validatorInclude != '') { if (!isset($includesByClass[$validatorClass])) { $includesByClass[$validatorClass] = array($validatorInclude); } else { if (!in_array($validatorInclude, $includesByClass[$validatorClass])) { $includesByClass[$validatorClass][] = $validatorInclude; } } } $phpCondition = isset($validator['phpCondition']) && is_string($validator['phpCondition']) ? trim($validator['phpCondition']) : ''; $phpc1 = $phpCondition != '' ? '( ' : ''; $phpc2 = $phpCondition != '' ? " && ({$phpCondition}) )" : ''; if (isset($validatorParams['errorMsg'])) { $errorMsg = $validatorParams['errorMsg']; unset($validatorParams['errorMsg']); $paramsEnc = var_export_normal_precision($validatorParams, true); if (substr($paramsEnc, -1) == ')') { $validatorErrorMsgLangKey = "crud.{$crudName}.validator.{$fieldName}.{$validatorClass}" . ($validatorNameCounts[$validatorClass] > 1 ? '.' . $validatorNameCounts[$validatorClass] : '') . '.errorMsg'; $langKeys[$validatorErrorMsgLangKey] = $errorMsg; $paramsEnc = rtrim(substr($paramsEnc, 0, strlen($paramsEnc) - 1), ", \t\n\r\v") . ",\n 'errorMsg'=>_t('{$validatorErrorMsgLangKey}', " . var_export_normal_precision($errorMsg, true) . "),\n )\n"; } } else { $paramsEnc = var_export_normal_precision($validatorParams, true); } $validationCode .= "\t\tif {$phpc1}(!isset(\$result->fieldErrors[{$fieldNameEnc}])){$phpc2} {\n" . "\t\t\t\$__validator = new {$validatorClass}({$paramsEnc});\n" . "\t\t\t\$__validatorError = \$__validator->validate(\$db, \$row);\n" . "\t\t\tif (\$__validatorError != '') {\n" . "\t\t\t\t\$result->fieldErrors[{$fieldNameEnc}] = \$__validatorError;\n" . "\t\t\t}\n" . "\t\t}\n" . "\n"; } } $validationCode .= "\t\tunset(\$__validator);\n" . "\t\tunset(\$__validatorError);\n"; foreach ($includesByClass as $cls => $incs) { foreach ($incs as $inc) { $phpIncludes .= "if (!class_exists('{$cls}', false)) include {$docRootPath}.'/{$inc}';\n"; } } $crudLoadCommand = 'load' . ucfirst($table->tableName); $crudLoadSynchronousOrAsynchronous = 'asynchronous'; if (isset($crud['crudLoad'])) { $load = $crud['crudLoad']; if (isset($load['loadCommand']) && is_string($load['loadCommand'])) { $crudLoadCommand = $load['loadCommand']; } if (isset($load['synchronous']) && $load['synchronous']) { $crudLoadSynchronousOrAsynchronous = 'synchronous'; } } $addFocusJS = isset($crud['addFocusField']) && is_string($crud['addFocusField']) ? $crud['addFocusField'] : ''; if ($addFocusJS != '') { $addFocusJS = "\t\t\t\$('#{$table->tableName}Form #{$addFocusJS}').focus();"; } $editFocusJS = isset($crud['editFocusField']) && is_string($crud['editFocusField']) ? $crud['editFocusField'] : ''; if ($editFocusJS != '') { $editFocusJS = "\t\t\t\$('#{$table->tableName}Form #{$editFocusJS}').focus();"; } $onlyUpdateColumns = isset($crud['onlyUpdateColumns']) ? $crud['onlyUpdateColumns'] : array(); if (!is_array($onlyUpdateColumns)) { $onlyUpdateColumns = array(); } if (empty($onlyUpdateColumns)) { $onlyUpdateColumns = 'array()'; } else { $onlyUpdateColumns = "array('" . implode("', '", $onlyUpdateColumns) . "')"; } $neverUpdateColumns = isset($crud['neverUpdateColumns']) ? $crud['neverUpdateColumns'] : array(); if (!is_array($neverUpdateColumns)) { $neverUpdateColumns = array(); } if (empty($neverUpdateColumns)) { $neverUpdateColumns = 'array()'; } else { $neverUpdateColumns = "array('" . implode("', '", $neverUpdateColumns) . "')"; } $javaScriptFiles = ''; if (isset($crud['javaScriptFiles']) && is_array($crud['javaScriptFiles'])) { foreach ($crud['javaScriptFiles'] as $file) { $javaScriptFiles .= "\$headerScripts[] = '{$file}';\n"; } } $cssFiles = ''; if (isset($crud['cssFiles']) && is_array($crud['cssFiles'])) { foreach ($crud['cssFiles'] as $file) { $cssFiles .= "\$headerStylesheets[] = '{$file}';\n"; } } $allowAddSimilar = isset($crud['allowAddSimilar']) && $crud['allowAddSimilar'] ? true : false; $viewInclude = $oldFileLayout ? "dirname(__FILE__).'/{$crudName}_view.include.php'" : "dirname(dirname(__FILE__)).'/{$crudName}_view.include.php'"; $searchFor = array('{{generatedFileMessage}}', '{{mainOkCheck}}', '{{classAutoloadPathsInit}}', '{{mainHooksInclude}}', '{{viewHooksInclude}}', '{{controllerIncludes}}', '{{docRootPath}}', '{{jaxInclude}}', '{{jaxJQuery}}', '{{jaxJS}}', '{{phpIncludes}}', '{{postInitPHPIncludes}}', '{{tableDescription}}', '{{tableDescriptions}}', '{{tableName}}', '{{uTableName}}', '{{crudName}}', '{{ucrudName}}', '{{idCol}}', '{{uIdCol}}', '{{loggedInId}}', '{{crudSearchCommand}}', '{{crudSearchCallbacks}}', '{{crudSearchTableDisplayColumns}}', '{{crudSearchGridHeaderColumnsHTML}}', '{{crudSearchGridBodyColumnsHTML}}', '{{crudSearchColumnNamesJSON}}', '{{crudSearchColumnFilters}}', '{{crudSearchExtraQueryParamsJSON}}', '{{crudSearchDefaultSortsJSON}}', '{{crudSearchTableCallbacks}}', '{{ajaxAutocompleteInitJS}}', '{{ajaxComboboxInitJS}}', '{{crudLoadCommand}}', '{{hiddenFormFields}}', '{{formFields}}', '{{filterCode}}', '{{validationCode}}', '{{addFocusJS}}', '{{editFocusJS}}', '{{onlyUpdateColumns}}', '{{neverUpdateColumns}}', '{{javaScriptFiles}}', '{{cssFiles}}', '{{allowAddSimilar}}', '{{viewInclude}}'); $replaceWith = array($generatedFileMessage, $mainOkCheck, $classAutoloadPathsInit, $mainHooksInclude, $viewHooksInclude, $controllerIncludes, $docRootPath, $jaxInclude, $jaxJQuery, $jaxJS, $phpIncludes, $postInitPHPIncludes, $cfg['tableDescription'], $cfg['tableDescriptions'], $table->tableName, ucfirst($table->tableName), $crudName, ucfirst($crudName), $idCol, ucfirst($idCol), $loggedInId, $crudSearchCommand, $crudSearchCallbacks, $crudSearchTableDisplayColumns, $crudSearchGridHeaderColumnsHTML, $crudSearchGridBodyColumnsHTML, json_encode($crudSearchColumnNames), $crudSearchColumnFilters, json_encode($crudSearchExtraQueryParams), json_encode($crudSearchDefaultSorts), $crudSearchTableCallbacks, $ajaxAutocompleteInitJS, $ajaxComboboxInitJS, $crudLoadCommand, $hiddenFormFields, $formFields, $filterCode, $validationCode, $addFocusJS, $editFocusJS, $onlyUpdateColumns, $neverUpdateColumns, $javaScriptFiles, $cssFiles, $allowAddSimilar ? 'true' : 'false', $viewInclude); $content = str_replace($searchFor, $replaceWith, file_get_contents($templatesDir . '/main.php')); $fn = $generatedOutputDir . '/' . $crudName . ($oldFileLayout ? '' : '_generated.include') . '.php'; if (!file_exists($fn) || file_get_contents($fn) != $content) { file_put_contents($fn, $content); } if ($enableLangFiles) { $langfn = $outputDir . '/' . $crudName . '.php.strings'; $langResources = loadLangResourceFileContents($langfn); $anyResourcesChanged = false; foreach ($langKeys as $key => $val) { if (getLangResource($langResources, $key) != $val) { setLangResource($langResources, $key, $val); $anyResourcesChanged = true; } } if ($anyResourcesChanged) { saveLangResourceFileContents($langfn, $langResources); } } if (!$oldFileLayout) { $fn = $outputDir . '/' . $crudName . '.php'; if (!file_exists($fn)) { file_put_contents($fn, sprintf(<<<EOF <?php \$mainOk = true; include dirname(__FILE__).'/generated/%s_generated.include.php'; // Add any custom functionality or hook functions for the main page below. EOF , $crudName)); } } $content = str_replace($searchFor, $replaceWith, filterCRUDLoadSynchronousOrAsynchronous(filterSearchPresentation(file_get_contents($templatesDir . '/view.include.php'), $crudSearchPresentation), $crudLoadSynchronousOrAsynchronous)); $fn = $generatedOutputDir . '/' . $crudName . '_view' . ($oldFileLayout ? '' : '_generated') . '.include.php'; if (!file_exists($fn) || file_get_contents($fn) != $content) { file_put_contents($fn, $content); } if (!$oldFileLayout) { $fn = $outputDir . '/' . $crudName . '_view.include.php'; if (!file_exists($fn)) { file_put_contents($fn, sprintf(<<<EOF <?php include dirname(__FILE__).'/generated/%s_view_generated.include.php'; // Add any custom functionality or hook functions for the view below. EOF , $crudName)); } } $content = str_replace($searchFor, $replaceWith, filterCRUDLoadSynchronousOrAsynchronous(filterSearchPresentation(file_get_contents($templatesDir . '/controller.js'), $crudSearchPresentation), $crudLoadSynchronousOrAsynchronous)); $fn = $generatedOutputDir . '/' . $crudName . '_controller' . ($oldFileLayout ? '' : '_generated') . '.js'; if (!file_exists($fn) || file_get_contents($fn) != $content) { file_put_contents($fn, $content); } if (!$oldFileLayout) { $fn = $outputDir . '/' . $crudName . '_controller.js'; if (!file_exists($fn)) { file_put_contents($fn, sprintf(<<<EOF // Add any custom functionality or hook functions for the controller below. EOF , $crudName)); } } } return true; }