function processTable($table, $idCol, $cfg) { global $ALLOWED_QUERY_OPERATORS, $ALLOWED_PS_TYPES, $SEARCH_PRESENTATION_DATATABLES_ONLY_PARAMS, $SEARCH_PRESENTATION_DATATABLES_ONLY_COLUMN_PARAMS, $SEARCH_PRESENTATION_AJAXSEARCHGRID_ONLY_PARAMS, $SEARCH_PRESENTATION_AJAXSEARCHGRID_ONLY_COLUMN_PARAMS, $templatesDir, $docroot, $enableLangFiles; $generatedFileMessage = <<<EOF // DO NOT EDIT THIS FILE. // This file was generated by searchgen. // 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 searchgen, passing in the table name. EOF; $canDoFulltextSearchPHP = 'in_array($db->getDialect(), array(\'mysql\'))'; $jaxInclude = $cfg['jaxInclude']; $jaxJQuery = $cfg['jaxJQuery']; $jaxJS = $cfg['jaxJS']; // --------------------------- // Create the search includes. // --------------------------- if (!isset($cfg['searches']) || !is_array($cfg['searches'])) { $cfg['searches'] = array(); } foreach ($cfg['searches'] as $searchName => $search) { $langKeys = array(); $searchCommand = isset($search['searchCommand']) && trim($search['searchCommand']) != '' ? trim($search['searchCommand']) : 'search' . ucfirst($searchName) . 's'; $extraSelectColumns = ''; if (isset($search['extraSelectColumns'])) { if (is_array($search['extraSelectColumns'])) { $extraSelectColumns = implode(', ', $search['extraSelectColumns']); } else { $extraSelectColumns = trim((string) $search['extraSelectColumns']); } } $outputPath = isset($search['outputPath']) && is_string($search['outputPath']) ? $search['outputPath'] : ''; if ($outputPath == '') { $outputPath = 'include/search'; } $outputDir = $docroot . '/' . $outputPath; @mkdir($outputDir, 0777, true); $numDirsDeepUnderHTML = calcDirDepth($outputPath); if (isset($search['docRootPath'])) { $docRootPath = '"' . addcslashes($search['docRootPath']) . '"'; } else { $docRootPath = 'dirname(__FILE__)'; for ($i = 0; $i < $numDirsDeepUnderHTML; $i++) { $docRootPath = "dirname({$docRootPath})"; } } $phpIncludes = getPHPClassesAndIncludes($search, $numDirsDeepUnderHTML); $searchTemplate = isset($search['searchTemplate']) && is_string($search['searchTemplate']) ? $search['searchTemplate'] : ''; if ($searchTemplate == '') { $searchTemplate = 'search.include.php'; } $joins = ''; if (isset($search['joins'])) { $joins = trim($search['joins']); } $arr = getWhereClauseParams($table, $searchName, $search, 'search', $langKeys); $haveAnyFulltextQueryOperators = $arr['haveAnyFulltextQueryOperators']; $searchWhereClausePHP = $arr['searchWhereClausePHP']; $searchWhereAssignments = $arr['searchWhereAssignments']; $searchableColumnsPHPArray = $arr['searchableColumnsPHPArray']; $searchableColumnsPHPArraySep = $arr['searchableColumnsPHPArraySep']; $andWhere = $arr['andWhere']; $andWhereAssignments = $arr['andWhereAssignments']; $groupBy = ''; if (isset($search['groupBy'])) { $groupBy = trim((string) $search['groupBy']); } $rowProcessingPHPCode = ''; if (isset($search['rowProcessingPHPCode'])) { $rowProcessingPHPCode = (string) $search['rowProcessingPHPCode']; } $unsetForbiddenColumns = array(); if (isset($search['forbiddenColumns'])) { foreach ($search['forbiddenColumns'] as $colName) { $unsetForbiddenColumns[] = "\t\tunset(\$row->{$colName});"; } } $searchFor = array('{{generatedFileMessage}}', '{{docRootPath}}', '{{jaxInclude}}', '{{jaxJQuery}}', '{{jaxJS}}', '{{phpIncludes}}', '{{searchName}}', '{{uSearchName}}', '{{tableName}}', '{{uTableName}}', '{{searchCommand}}', '{{searchableColumnsPHPArray}}', '{{idCol}}', '{{uIdCol}}', '{{extraSelectColumns}}', '{{joins}}', '{{canDoFulltextSearchPHP}}', '{{searchWhereClausePHP}}', '{{andWhere}}', '{{searchWhereAssignments}}', '{{andWhereAssignments}}', '{{groupBy}}', '{{rowProcessingPHPCode}}', '{{unsetForbiddenColumns}}'); $replaceWith = array($generatedFileMessage, $docRootPath, $jaxInclude, $jaxJQuery, $jaxJS, $phpIncludes, $searchName, ucfirst($searchName), $table->tableName, ucfirst($table->tableName), $searchCommand, $searchableColumnsPHPArray, $idCol, ucfirst($idCol), $extraSelectColumns != '' ? ', ' . $extraSelectColumns : '', $joins != '' ? ' ' . $joins : '', $canDoFulltextSearchPHP, $searchWhereClausePHP, $andWhere != '' ? ' and (' . $andWhere . ')' : '', implode("\n", $searchWhereAssignments), implode("\n", $andWhereAssignments), $groupBy, $rowProcessingPHPCode, implode("\n", $unsetForbiddenColumns)); $content = str_replace($searchFor, $replaceWith, filterFullTextSearchCode(file_get_contents($templatesDir . '/' . $searchTemplate), $haveAnyFulltextQueryOperators)); $fn = $outputDir . '/' . $searchName . '_search.include.php'; if (!file_exists($fn) || file_get_contents($fn) != $content) { file_put_contents($fn, $content); } if ($enableLangFiles) { $langfn = $outputDir . '/' . $searchName . '_search.include.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); } } } // foreach ($cfg['searches'] as $searchName=>$search) // --------------------------------------- // Create the autcomplete search includes. // --------------------------------------- if (!isset($cfg['autocompleteSearches']) || !is_array($cfg['autocompleteSearches'])) { $cfg['autocompleteSearches'] = array(); } foreach ($cfg['autocompleteSearches'] as $searchName => $search) { $langKeys = array(); $searchCommand = isset($search['searchCommand']) && trim($search['searchCommand']) != '' ? trim($search['searchCommand']) : 'autocomplete' . ucfirst($searchName) . 's'; $extraSelectColumns = ''; if (isset($search['extraSelectColumns'])) { if (is_array($search['extraSelectColumns'])) { $extraSelectColumns = implode(', ', $search['extraSelectColumns']); } else { $extraSelectColumns = trim((string) $search['extraSelectColumns']); } } $outputPath = isset($search['outputPath']) && is_string($search['outputPath']) ? $search['outputPath'] : ''; if ($outputPath == '') { $outputPath = 'include/search'; } $outputDir = $docroot . '/' . $outputPath; @mkdir($outputDir, 0777, true); $numDirsDeepUnderHTML = calcDirDepth($outputPath); if (isset($search['docRootPath'])) { $docRootPath = '"' . addcslashes($search['docRootPath']) . '"'; } else { $docRootPath = 'dirname(__FILE__)'; for ($i = 0; $i < $numDirsDeepUnderHTML; $i++) { $docRootPath = "dirname({$docRootPath})"; } } $phpIncludes = getPHPClassesAndIncludes($search, $numDirsDeepUnderHTML); $searchTemplate = isset($search['searchTemplate']) && is_string($search['searchTemplate']) ? $search['searchTemplate'] : ''; if ($searchTemplate == '') { $searchTemplate = 'autocomplete.include.php'; } $idColumn = isset($search['idColumn']) && is_string($search['idColumn']) ? $search['idColumn'] : $idCol; $idColumnPSType = isset($search['idColumnPSType']) && is_string($search['idColumnPSType']) ? $search['idColumnPSType'] : 'int'; if (!in_array($idColumnPSType, $ALLOWED_PS_TYPES)) { fprintf(STDERR, "Invalid idColumnPSType \"%s\" in autocompleteSearches.", $idColumnPSType); return false; } switch ($idColumnPSType) { case 'boolean': case 'int': $getIdParam = sprintf("\t\t\$%s = isset(\$params['%s']) ? (int)trim(\$params['%s']) : 0;", $idColumn, $idColumn, $idColumn); break; case 'float': $getIdParam = sprintf("\t\t\$%s = isset(\$params['%s']) ? (float)trim(\$params['%s']) : 0;", $idColumn, $idColumn, $idColumn); break; case 'double': $getIdParam = sprintf("\t\t\$%s = isset(\$params['%s']) ? (double)trim(\$params['%s']) : 0;", $idColumn, $idColumn, $idColumn); break; case 'string': case 'match': case 'binary': default: $getIdParam = sprintf("\t\t\$%s = isset(\$params['%s']) ? (string)\$params['%s'] : '';", $idColumn, $idColumn, $idColumn); break; } $altIdColumn = isset($search['altIdColumn']) && is_string($search['altIdColumn']) ? $search['altIdColumn'] : ''; if ($altIdColumn != '') { $altIdColumnPSType = isset($search['altIdColumnPSType']) && is_string($search['altIdColumnPSType']) ? $search['altIdColumnPSType'] : 'int'; if (!in_array($altIdColumnPSType, $ALLOWED_PS_TYPES)) { fprintf(STDERR, "Invalid altIdColumnPSType \"%s\" in autocompleteSearches.", $altIdColumnPSType); return false; } switch ($altIdColumnPSType) { case 'boolean': case 'int': $getAltIdParam = sprintf("\t\$%s = isset(\$params['%s']) ? (int)trim(\$params['%s']) : null;", $altIdColumn, $altIdColumn, $altIdColumn); break; case 'float': $getAltIdParam = sprintf("\t\$%s = isset(\$params['%s']) ? (float)trim(\$params['%s']) : null;", $altIdColumn, $altIdColumn, $altIdColumn); break; case 'double': $getAltIdParam = sprintf("\t\$%s = isset(\$params['%s']) ? (double)trim(\$params['%s']) : null;", $altIdColumn, $altIdColumn, $altIdColumn); break; case 'string': case 'match': case 'binary': default: $getAltIdParam = sprintf("\t\$%s = isset(\$params['%s']) ? (string)\$params['%s'] : null;", $altIdColumn, $altIdColumn, $altIdColumn); break; } } else { $altIdColumnPSType = ''; $getAltIdParam = ''; } $joins = ''; if (isset($search['joins'])) { $joins = trim($search['joins']); } $dummyLangKeys = array(); $arr = getWhereClauseParams($table, $searchName, $search, 'autocomplete', $dummyLangKeys); unset($dummyLangKeys); $haveAnyFulltextQueryOperators = $arr['haveAnyFulltextQueryOperators']; $searchWhereClausePHP = $arr['searchWhereClausePHP']; $searchWhereAssignments = $arr['searchWhereAssignments']; $andWhere = $arr['andWhere']; $andWhereAssignments = $arr['andWhereAssignments']; $searchResultLabelExpression = isset($search['searchResultLabelExpression']) ? trim($search['searchResultLabelExpression']) : ''; $searchResultValueExpression = isset($search['searchResultValueExpression']) ? trim($search['searchResultValueExpression']) : '$row->' . $idColumn; $searchFor = array('{{generatedFileMessage}}', '{{docRootPath}}', '{{jaxInclude}}', '{{jaxJQuery}}', '{{jaxJS}}', '{{phpIncludes}}', '{{searchName}}', '{{uSearchName}}', '{{tableName}}', '{{uTableName}}', '{{searchCommand}}', '{{idCol}}', '{{uIdCol}}', '{{idColumnPSType}}', '{{uIdColumnPSType}}', '{{getIdParam}}', '{{altIdCol}}', '{{uAltIdCol}}', '{{altIdColumnPSType}}', '{{uAltIdColumnPSType}}', '{{getAltIdParam}}', '{{extraSelectColumns}}', '{{joins}}', '{{canDoFulltextSearchPHP}}', '{{searchWhereClausePHP}}', '{{andWhere}}', '{{searchWhereAssignments}}', '{{andWhereAssignments}}', '{{searchResultLabelExpression}}', '{{searchResultValueExpression}}'); $replaceWith = array($generatedFileMessage, $docRootPath, $jaxInclude, $jaxJQuery, $jaxJS, $phpIncludes, $searchName, ucfirst($searchName), $table->tableName, ucfirst($table->tableName), $searchCommand, $idColumn, ucfirst($idColumn), $idColumnPSType, ucfirst($idColumnPSType), $getIdParam, $altIdColumn, ucfirst($altIdColumn), $altIdColumnPSType, ucfirst($altIdColumnPSType), $getAltIdParam, $extraSelectColumns != '' ? ', ' . $extraSelectColumns : '', $joins != '' ? ' ' . $joins : '', $canDoFulltextSearchPHP, $searchWhereClausePHP, $andWhere != '' ? ' and (' . $andWhere . ')' : '', implode("\n", $searchWhereAssignments), implode("\n", $andWhereAssignments), $searchResultLabelExpression, $searchResultValueExpression); $content = str_replace($searchFor, $replaceWith, filterAltIdAutocompleteCode(filterFullTextSearchCode(file_get_contents($templatesDir . '/' . $searchTemplate), $haveAnyFulltextQueryOperators), $altIdColumn != '')); $fn = $outputDir . '/' . $searchName . '_autocomplete.include.php'; if (!file_exists($fn) || file_get_contents($fn) != $content) { file_put_contents($fn, $content); } if ($enableLangFiles) { $langfn = $outputDir . '/' . $searchName . '_autocomplete.include.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); } } } // foreach ($cfg['autocompleteSearches'] as $searchName=>$search) // ----------------------------------------- // Create the popup search JavaScript files. // ----------------------------------------- if (!isset($cfg['popupSearches']) || !is_array($cfg['popupSearches'])) { $cfg['popupSearches'] = array(); } foreach ($cfg['popupSearches'] as $popupSearchName => $popupSearch) { if (!is_array($popupSearch)) { continue; } // If we encounter a "likePopupSearch" attribute for this popupSearch, it must // reference an existing popupSearch defined in the same YAML file, which may // or may not reference another popupSearch. Circular references are not allowed. // When this happens, we merge the referenced popupSearch's attributes with this // popupSearch's attributes, allowing this popupSearch's attributes to override // the referenced popupSearch's attributes of the same name. $referencedPopupSearchNames = array(); while (isset($popupSearch['likePopupSearch'])) { if (!is_string($popupSearch['likePopupSearch']) || !isset($cfg['popupSearches']) || !isset($cfg['popupSearches'][$popupSearch['likePopupSearch']]) || !is_array($cfg['popupSearches'][$popupSearch['likePopupSearch']])) { fprintf(STDERR, "Invalid likePopupSearch entry in popupSearch/* section.\n"); return false; } $referencedPopupSearchName = $popupSearch['likePopupSearch']; unset($popupSearch['likePopupSearch']); if (in_array($referencedPopupSearchName, $referencedPopupSearchNames)) { fprintf(STDERR, "Circular reference in likePopupSearch: %s.\n", implode('->', $referencedPopupSearchNames)); return false; } $referencedPopupSearchNames[] = $referencedPopupSearchName; $popupSearch = array_merge($cfg['popupSearches'][$referencedPopupSearchName], $popupSearch); } unset($referencedPopupSearchNames, $referencedPopupSearchName); $langKeys = array(); $langKeys['popupSearch.' . $popupSearchName . '.tableDescription'] = $cfg['tableDescription']; $langKeys['popupSearch.' . $popupSearchName . '.tableDescriptions'] = $cfg['tableDescriptions']; $outputPath = isset($popupSearch['outputPath']) && is_string($popupSearch['outputPath']) ? $popupSearch['outputPath'] : ''; if ($outputPath == '') { $outputPath = 'js/search'; } $outputDir = $docroot . '/' . $outputPath; @mkdir($outputDir, 0777, true); $popupSearchTemplate = isset($popupSearch['popupSearchTemplate']) && is_string($popupSearch['popupSearchTemplate']) ? $popupSearch['popupSearchTemplate'] : ''; if ($popupSearchTemplate == '') { $popupSearchTemplate = 'popupSearch.js'; } if (!isset($popupSearch['searchCommand']) || !is_string($popupSearch['searchCommand'])) { continue; } $searchCommand = $popupSearch['searchCommand']; if (isset($popupSearch['searchPresentation']) && is_string($popupSearch['searchPresentation'])) { $searchPresentation = $popupSearch['searchPresentation']; if ($searchPresentation != 'dataTables' && $searchPresentation != 'AJAXSearchGrid') { $searchPresentation = 'dataTables'; } } else { $searchPresentation = 'dataTables'; } if ($searchPresentation != 'dataTables') { $badattrs = array_intersect(array_keys($popupSearch), $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 ($searchPresentation != 'AJAXSearchGrid') { $badattrs = array_intersect(array_keys($popupSearch), $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($popupSearch['columns']) && is_array($popupSearch['columns'])) { foreach ($popupSearch['columns'] as $col) { if ($searchPresentation != '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 ($searchPresentation != '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); if (!isset($popupSearch['idColumn'])) { fprintf(STDERR, "Missing idColumn on popupSearch %s. Skipping this popupSearch.\n", $popupSearchName); continue; } $idColumn = $popupSearch['idColumn']; $beforeSearchCallback = isset($popupSearch['beforeSearchCallback']) ? $popupSearch['beforeSearchCallback'] : ''; $modifyURLCallback = isset($popupSearch['modifyURLCallback']) ? $popupSearch['modifyURLCallback'] : ''; $afterSearchCallback = isset($popupSearch['afterSearchCallback']) ? $popupSearch['afterSearchCallback'] : ''; $popupSearchCallbacks = ''; if ($beforeSearchCallback != '') { $popupSearchCallbacks .= ",\n\t\tbeforeSearchCallback:{$beforeSearchCallback}"; } if ($modifyURLCallback != '') { $popupSearchCallbacks .= ",\n\t\tmodifyURLCallback:{$modifyURLCallback}"; } if ($afterSearchCallback != '') { $popupSearchCallbacks .= ",\n\t\tafterSearchCallback:{$afterSearchCallback}"; } $rowSelectJavaScriptCallbackFunction = isset($popupSearch['rowSelectJavaScriptCallbackFunction']) ? $popupSearch['rowSelectJavaScriptCallbackFunction'] : ''; if (!isset($popupSearch['columns']) || !is_array($popupSearch['columns'])) { fprintf(STDERR, "Missing or invalid columns list on popupSearch %s. Skipping this popupSearch.\n", $popupSearchName); continue; } $popupSearchColumns = getDataTableColumns($popupSearch['columns'], 'popupSearch.' . $popupSearchName, $langKeys); $popupSearchColumnNames = is_array($popupSearch['columns']) ? array_keys($popupSearch['columns']) : array(); if (isset($popupSearch['invisibleColumns']) && is_array($popupSearch['invisibleColumns'])) { foreach ($popupSearch['invisibleColumns'] as $icn) { if (!in_array($icn, $popupSearchColumnNames)) { $popupSearchColumnNames[] = $icn; } } } $popupSearchColumnFilters = isset($popupSearch['columnFilters']) && is_array($popupSearch['columnFilters']) ? $popupSearch['columnFilters'] : array(); $tmp = '{'; $sep = ''; foreach ($popupSearchColumnFilters as $funcname => $functext) { $tmp .= $sep . json_encode($funcname) . ':' . $functext; if ($sep == '') { $sep = ','; } } $tmp .= '}'; $popupSearchColumnFilters = $tmp; unset($tmp); $popupSearchExtraQueryParams = isset($popupSearch['extraQueryParams']) && is_array($popupSearch['extraQueryParams']) ? (object) $popupSearch['extraQueryParams'] : (object) array(); $popupSearchDefaultSorts = isset($popupSearch['defaultSorts']) && is_array($popupSearch['defaultSorts']) ? $popupSearch['defaultSorts'] : array(); $actionsLangKey = 'popupSearch.' . $popupSearchName . '.actions'; $actionsText = 'Actions'; $langKeys[$actionsLangKey] = $actionsText; $selectLangKey = 'popupSearch.' . $popupSearchName . '.select'; $selectText = 'Select'; $langKeys[$selectLangKey] = $selectText; list($popupSearchGridHeaderColumnsHTML, $popupSearchGridBodyColumnsHTML) = getAJAXSearchGridColumns($popupSearch['columns'], 'popupSearch.' . $popupSearchName, $langKeys); $popupSearchGridHeaderColumnsHTML .= sprintf("<th><<langkey>>%s<</langkey>></th>\n", $actionsLangKey); $popupSearchGridBodyColumnsHTML .= sprintf("<td><a href=\"#\" ng-click=\"rowSelectLinkClicked(i, rows[i], \$event)\"><<langkey>>%s<</langkey>></a></td>\n", $selectLangKey); $popupSearchGridHeaderColumnsHTMLJSON = json_encode($popupSearchGridHeaderColumnsHTML); $popupSearchGridBodyColumnsHTMLJSON = json_encode($popupSearchGridBodyColumnsHTML); $srch = array(); $repl = array(); foreach ($langKeys as $langKey => $text) { $srch[] = '<<langkey>>' . $langKey . '<<\\/langkey>>'; // special escaping, since we're working with JSON $repl[] = '"+_t(' . var_export_normal_precision($langKey, true) . ', ' . var_export_normal_precision($text, true) . ')+"'; } $popupSearchGridHeaderColumnsHTMLJSON = str_replace($srch, $repl, $popupSearchGridHeaderColumnsHTMLJSON); $popupSearchGridBodyColumnsHTMLJSON = str_replace($srch, $repl, $popupSearchGridBodyColumnsHTMLJSON); $rowSelectCallbackCode = ''; if ($rowSelectJavaScriptCallbackFunction != '') { // Note the '+ and +' around $idColumn. This is intended to be part of // a JavaScript string contcatenation which is building the html variable // to be returned for the actions table column. $rowSelectCallbackCode .= "if (typeof({$rowSelectJavaScriptCallbackFunction}) == \\'function\\') { {$rowSelectJavaScriptCallbackFunction}('+{$idColumn}+'); }; "; } $rowSelectCode = "if (activePopupSearch != null) { activePopupSearch.rowSelected(activePopupSearch.dataTable.fnGetData('+oObj.iDataRow+')); }; " . $rowSelectCallbackCode . "if (activePopupSearch != null) { activePopupSearch.hide(); }; return false;"; if ($popupSearchColumns != '') { $popupSearchColumns .= ",\n"; } $popupSearchColumns .= "\t\t{ sName:'actions', sTitle:_t('" . $actionsLangKey . "', '" . $actionsText . "'), aTargets:[ci++], bSortable:false, bUseRendered:false, sType:'html', sClass:'center nowrap', fnRender:function(oObj) {\n" . "\t\t\tvar {$idColumn} = parseInt(oObj.aData[findDataTableColIdx({$popupSearchName}_aoColumnDefs, '{$idColumn}')]) || 0;\n" . "\t\t\tvar html = '<a href=\"#\" onclick=\"{$rowSelectCode}\">'+_t('" . $selectLangKey . "', '" . $selectText . "')+'</a>';\n" . "\t\t\treturn html;\n" . "\t\t}}\n"; unset($rowSelectCallbackCode, $rowSelectCode); $popupSearchTableCallbacks = ''; if (isset($popupSearch['fnDrawCallback']) && is_string($popupSearch['fnDrawCallback'])) { $s = trim($popupSearch['fnDrawCallback']); if ($s != '') { $popupSearchTableCallbacks .= ', fnDrawCallback: ' . $s; } unset($s); } if (isset($popupSearch['fnServerData']) && is_string($popupSearch['fnServerData'])) { $s = trim($popupSearch['fnServerData']); if ($s != '') { $popupSearchTableCallbacks .= ', fnServerData: ' . $s; } unset($s); } // Create the popup search JS file. $searchFor = array('{{generatedFileMessage}}', '{{popupSearchName}}', '{{popupSearchColumns}}', '{{popupSearchGridHeaderColumnsHTMLJSON}}', '{{popupSearchGridBodyColumnsHTMLJSON}}', '{{popupSearchColumnNamesJSON}}', '{{popupSearchColumnFilters}}', '{{popupSearchExtraQueryParamsJSON}}', '{{popupSearchDefaultSortsJSON}}', '{{popupSearchTableCallbacks}}', '{{searchCommand}}', '{{idColumn}}', '{{idColumnJSON}}', '{{popupSearchCallbacks}}', '{{rowSelectJavaScriptCallbackFunction}}', '{{rowSelectJavaScriptCallbackFunctionJSON}}'); $replaceWith = array($generatedFileMessage, $popupSearchName, $popupSearchColumns, $popupSearchGridHeaderColumnsHTMLJSON, $popupSearchGridBodyColumnsHTMLJSON, json_encode($popupSearchColumnNames), $popupSearchColumnFilters, json_encode($popupSearchExtraQueryParams), json_encode($popupSearchDefaultSorts), $popupSearchTableCallbacks, $searchCommand, $idColumn, json_encode($idColumn), $popupSearchCallbacks, $rowSelectJavaScriptCallbackFunction, json_encode($rowSelectJavaScriptCallbackFunction)); $content = str_replace($searchFor, $replaceWith, filterSearchPresentation(file_get_contents($templatesDir . '/' . $popupSearchTemplate), $searchPresentation)); $fn = $outputDir . '/' . $popupSearchName . '.js'; if (!file_exists($fn) || file_get_contents($fn) != $content) { file_put_contents($fn, $content); } if ($enableLangFiles) { $langfn = $outputDir . '/' . $popupSearchName . '.js.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); } } } // foreach ($cfg['popupSearches'] as $popupSearchName=>$popupSearch) // --------------------------- // Create the loader includes. // --------------------------- if (!isset($cfg['loaders']) || !is_array($cfg['loaders'])) { $cfg['loaders'] = array(); } foreach ($cfg['loaders'] as $loaderName => $loader) { $outputPath = isset($loader['outputPath']) && is_string($loader['outputPath']) ? $loader['outputPath'] : ''; if ($outputPath == '') { $outputPath = 'include/search'; } $outputDir = $docroot . '/' . $outputPath; @mkdir($outputDir, 0777, true); $numDirsDeepUnderHTML = calcDirDepth($outputPath); if (isset($loader['docRootPath'])) { $docRootPath = '"' . addcslashes($loader['docRootPath']) . '"'; } else { $docRootPath = 'dirname(__FILE__)'; for ($i = 0; $i < $numDirsDeepUnderHTML; $i++) { $docRootPath = "dirname({$docRootPath})"; } } $phpIncludes = getPHPClassesAndIncludes($loader, $numDirsDeepUnderHTML); $searchCommand = isset($loader['searchCommand']) && trim($loader['searchCommand']) != '' ? trim($loader['searchCommand']) : 'load' . ucfirst($loaderName); $loaderTemplate = isset($loader['loaderTemplate']) && is_string($loader['loaderTemplate']) ? $loader['loaderTemplate'] : ''; if ($loaderTemplate == '') { $loaderTemplate = 'load.include.php'; } $idColumn = isset($loader['idColumn']) && is_string($loader['idColumn']) ? $loader['idColumn'] : $idCol; $idColumnPSType = isset($loader['idColumnPSType']) && is_string($loader['idColumnPSType']) ? $loader['idColumnPSType'] : 'int'; if (!in_array($idColumnPSType, $ALLOWED_PS_TYPES)) { $idColumnPSType = 'int'; } switch ($idColumnPSType) { case 'boolean': case 'int': $getIdParam = sprintf("\t\$%s = isset(\$params['%s']) ? (int)trim(\$params['%s']) : 0;", $idColumn, $idColumn, $idColumn); if ($idColumnPSType == 'boolean') { $emptyIdCheck = sprintf('($%s < 0)', $idColumn); } else { $emptyIdCheck = sprintf('($%s <= 0)', $idColumn); } break; case 'float': $getIdParam = sprintf("\t\$%s = isset(\$params['%s']) ? (float)trim(\$params['%s']) : 0;", $idColumn, $idColumn, $idColumn); $emptyIdCheck = sprintf('($%s <= 0.0)', $idColumn); break; case 'double': $getIdParam = sprintf("\t\$%s = isset(\$params['%s']) ? (double)trim(\$params['%s']) : 0;", $idColumn, $idColumn, $idColumn); $emptyIdCheck = sprintf('($%s <= 0.0)', $idColumn); break; case 'string': case 'match': case 'binary': default: $getIdParam = sprintf("\t\$%s = isset(\$params['%s']) ? (string)\$params['%s'] : '';", $idColumn, $idColumn, $idColumn); $emptyIdCheck = sprintf("(\$%s == '')", $idColumn); break; } $andWhere = ''; if (isset($loader['andWhere'])) { $andWhere = trim((string) $loader['andWhere']); } $andWhereAssignments = array(); if (isset($loader['andWhereAssignments']) && is_array($loader['andWhereAssignments'])) { foreach ($loader['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; } } } $initRelationDAOs = array(); $loadRelations = array(); $psidx = 0; $psInitCode = array(); if (isset($loader['relations'])) { foreach ($loader['relations'] as $relationName => $relation) { $rtn = $relation['table']; $urtn = ucfirst($rtn); $useDAO = !isset($relation['useDAO']) || $relation['useDAO'] ? true : false; if ($useDAO || !isset($relation['sqlQuery'])) { $initRelationDAOs[] = "\t\${$rtn}DAO = new {$urtn}DAO(\$db);"; } $relationType = isset($relation['relationType']) ? $relation['relationType'] : 'many'; if ($relationType != 'one' && $relationType != 'many') { $relationType = 'many'; } $offset = isset($relation['offset']) ? (int) $relation['offset'] : 0; if ($offset < 0) { $offset = 0; } if ($relationType == 'one') { $limit = 1; } else { $limit = isset($relation['limit']) ? (int) $relation['limit'] : 0; if ($limit < 0) { $limit = 0; } } $code = ''; if (isset($relation['sqlQuery'])) { // Retrieve the row(s) using an SQL query. $psidx++; $psInitCode[] = "\t\$ps{$psidx} = new PreparedStatement(<<<EOF\n{$relation['sqlQuery']}\nEOF\n\t, {$offset}, {$limit});\n"; $code .= "\t\t\$ps{$psidx}->clearParams();\n"; if (isset($relation['sqlQueryAssignments']) && is_array($relation['sqlQueryAssignments'])) { foreach ($relation['sqlQueryAssignments'] as $assignment) { $expression = isset($assignment['expression']) && is_string($assignment['expression']) ? $assignment['expression'] : "''"; switch (isset($assignment['psType']) ? $assignment['psType'] : 'string') { case 'boolean': $code .= "\t\t\$ps{$psidx}->setBoolean({$expression});\n"; break; case 'int': $code .= "\t\t\$ps{$psidx}->setInt({$expression});\n"; break; case 'float': $code .= "\t\t\$ps{$psidx}->setFloat({$expression});\n"; break; case 'double': $code .= "\t\t\$ps{$psidx}->setDouble({$expression});\n"; break; case 'string': default: $code .= "\t\t\$ps{$psidx}->setString({$expression});\n"; break; case 'match': $code .= "\t\t\$ps{$psidx}->setString('%'.{$expression}.'%');\n"; break; case 'binary': $code .= "\t\t\$ps{$psidx}->setBinary({$expression});\n"; break; } } } $code .= "\t\t"; if ($relationType == 'one') { $code .= '$relRows = ' . $emptyIdCheck . ' ? array() : '; } else { $code .= '$row->' . $relationName . ' = ' . $emptyIdCheck . ' ? array() : '; } if ($useDAO) { $code .= '$' . $rtn . "DAO->findWithPreparedStatement(\$ps{$psidx});\n"; } else { $code .= "\$db->fetchAllObjects(\$db->executeQuery(\$ps{$psidx}), true);\n"; } if ($relationType == 'one') { $code .= "\t\t\$row->{$relationName} = empty(\$relRows) ? null : \$relRows[0];"; $code .= "\t\tunset(\$relRows);"; } } else { $queryOperator = isset($relation['queryOperator']) ? $relation['queryOperator'] : '='; if (!in_array($queryOperator, $ALLOWED_QUERY_OPERATORS)) { $queryOperator = '='; } $local = isset($relation['local']) ? $relation['local'] : ''; if ($local == '') { continue; } $foreign = isset($relation['foreign']) ? $relation['foreign'] : ''; if ($foreign == '') { continue; } $orderBy = isset($relation['orderBy']) ? $relation['orderBy'] : ''; if ($orderBy == '') { $orderBy = $foreign; } if (!$useDAO) { $psidx++; $code .= '\\t\\t$ps{$psidx} = ' . $rtn . 'DAO->findBy' . ucfirst($foreign) . 'PS($row->' . $local . ', \'' . $queryOperator . '\', \'' . $orderBy . '\', ' . $offset . ', ' . $limit . ');'; } $code .= "\t\t"; if ($relationType == 'one') { $code .= '$relRows = ' . $emptyIdCheck . ' ? array() : '; } else { $code .= '$row->' . $relationName . ' = ' . $emptyIdCheck . ' ? array() : '; } if ($useDAO) { $code .= '$' . $rtn . 'DAO->findBy' . ucfirst($foreign) . '($row->' . $local . ', \'' . $queryOperator . '\', \'' . $orderBy . '\', ' . $offset . ', ' . $limit . ');'; } else { $code .= "\${$db->fetchAllObjects}(\$db->executeQuery(\$ps{$psidx}), true);\n"; } if ($relationType == 'one') { $code .= "\n\t\t\$row->{$relationName} = empty(\$relRows) ? null : \$relRows[0];"; $code .= "\n\t\tunset(\$relRows);"; } } if (empty($loadRelations)) { $loadRelations[] = "\tforeach (\$rows as &\$row) {"; } $loadRelations[] = $code; } if (!empty($loadRelations)) { $loadRelations[] = "\t}\n\tunset(\$row);"; } if (!empty($psInitCode)) { $loadRelations = array_merge($psInitCode, $loadRelations); } } $rowProcessingPHPCode = ''; if (isset($loader['rowProcessingPHPCode'])) { $rowProcessingPHPCode = (string) $loader['rowProcessingPHPCode']; if (trim($rowProcessingPHPCode) != '') { $rowProcessingPHPCode = <<<EOF \tforeach (\$rows as &\$row) { {$rowProcessingPHPCode} \t} \tunset(\$row); // release reference to last element EOF; } else { $rowProcessingPHPCode = ''; } } $unsetForbiddenColumns = array(); if (isset($loader['forbiddenColumns'])) { foreach ($loader['forbiddenColumns'] as $colName) { if (empty($unsetForbiddenColumns)) { $unsetForbiddenColumns[] = "\tforeach (\$rows as &\$row) {"; } $unsetForbiddenColumns[] = "\t\tunset(\$row->{$colName});"; } if (!empty($unsetForbiddenColumns)) { $unsetForbiddenColumns[] = "\t}\n\tunset(\$row);"; } } // Create the loader include. $searchFor = array('{{generatedFileMessage}}', '{{docRootPath}}', '{{jaxInclude}}', '{{jaxJQuery}}', '{{jaxJS}}', '{{phpIncludes}}', '{{loaderName}}', '{{uLoaderName}}', '{{tableName}}', '{{uTableName}}', '{{searchCommand}}', '{{idCol}}', '{{uIdCol}}', '{{idColumnPSType}}', '{{uIdColumnPSType}}', '{{getIdParam}}', '{{emptyIdCheck}}', '{{andWhere}}', '{{andWhereAssignments}}', '{{initRelationDAOs}}', '{{loadRelations}}', '{{rowProcessingPHPCode}}', '{{unsetForbiddenColumns}}'); $replaceWith = array($generatedFileMessage, $docRootPath, $jaxInclude, $jaxJQuery, $jaxJS, $phpIncludes, $loaderName, ucfirst($loaderName), $table->tableName, ucfirst($table->tableName), $searchCommand, $idColumn, ucfirst($idColumn), $idColumnPSType, ucfirst($idColumnPSType), $getIdParam, $emptyIdCheck, $andWhere != '' ? ' and (' . $andWhere . ')' : '', implode("\n", $andWhereAssignments), implode("\n", $initRelationDAOs), !empty($loadRelations) ? implode("\n", $loadRelations) : '', $rowProcessingPHPCode, implode("\n", $unsetForbiddenColumns)); $content = str_replace($searchFor, $replaceWith, file_get_contents($templatesDir . '/' . $loaderTemplate)); $fn = $outputDir . '/' . $loaderName . '_load.include.php'; if (!file_exists($fn) || file_get_contents($fn) != $content) { file_put_contents($fn, $content); } } return true; }
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; }