Пример #1
0
 protected function _actionGenerateDataWriter(array $addOn, DevHelper_Config_Base $config, array $dataClass)
 {
     list($className, $contents) = DevHelper_Generator_Code_DataWriter::generate($addOn, $config, $dataClass);
     $path = DevHelper_Generator_File::write($className, $contents);
     $config->updateDataClassFile($dataClass['name'], 'data_writer', $className, $path);
     $this->_getConfigModel()->saveAddOnConfig($addOn, $config);
     return $this->responseRedirect(XenForo_ControllerResponse_Redirect::RESOURCE_CREATED, XenForo_Link::buildAdminLink('add-ons/data-manager', $addOn, array('dataClass' => $dataClass['name'])));
 }
Пример #2
0
 protected function _generateImageCode()
 {
     $imageField = DevHelper_Generator_Db::getImageField($this->_dataClass['fields']);
     if ($imageField === false) {
         // no image field...
         return '';
     }
     if (count($this->_dataClass['primaryKey']) > 1) {
         throw new XenForo_Exception(sprintf('Cannot generate image code for %s: too many fields in primary key', $this->_getClassName()));
     }
     $idField = reset($this->_dataClass['primaryKey']);
     $getFunctionName = self::generateGetDataFunctionName($this->_addOn, $this->_config, $this->_dataClass);
     $variableName = self::getVariableName($this->_addOn, $this->_config, $this->_dataClass);
     $variableNamePlural = self::getVariableNamePlural($this->_addOn, $this->_config, $this->_dataClass);
     $dwClassName = DevHelper_Generator_Code_DataWriter::getClassName($this->_addOn, $this->_config, $this->_dataClass);
     $configPrefix = $this->_config->getPrefix();
     $imagePath = "{$configPrefix}/{$this->_dataClass['camelCase']}";
     $imagePath = strtolower($imagePath);
     $this->_addMethod($getFunctionName, '', array(), "\n\n// build image urls and make them ready for all the records\n\$imageSizes = XenForo_DataWriter::create('{$dwClassName}')->getImageSizes();\nforeach (\${$variableNamePlural} as &\${$variableName}) {\n    \${$variableName}['images'] = array();\n    if (!empty(\${$variableName}['{$imageField}'])) {\n        foreach (\$imageSizes as \$imageSizeCode => \$imageSize) {\n            \${$variableName}['images'][\$imageSizeCode] = \$this->getImageUrl(\${$variableName}, \$imageSizeCode);\n        }\n    }\n}\n\n        ", '100');
     $this->_addMethod('getImageFilePath', 'public static', array(sprintf('$%s', $variableName) => sprintf('array $%s', $variableName), '$size' => '$size = \'l\''), "\n\n\$internal = self::_getImageInternal(\${$variableName}, \$size);\n\nif (!empty(\$internal)) {\n    return XenForo_Helper_File::getExternalDataPath() . \$internal;\n} else {\n    return '';\n}\n\n        ");
     $this->_addMethod('getImageUrl', 'public static', array(sprintf('$%s', $variableName) => sprintf('array $%s', $variableName), '$size' => '$size = \'l\''), "\n\n\$internal = self::_getImageInternal(\${$variableName}, \$size);\n\nif (!empty(\$internal)) {\n    return XenForo_Application::\$externalDataUrl . \$internal;\n} else {\n    return '';\n}\n\n        ");
     $this->_addMethod('_getImageInternal', 'protected static', array(sprintf('$%s', $variableName) => sprintf('array $%s', $variableName), '$size'), "\n\nif (empty(\${$variableName}['{$idField}']) OR empty(\${$variableName}['{$imageField}'])) {\n    return '';\n}\n\nreturn '/{$imagePath}/' . \${$variableName}['{$idField}'] . '_' . \${$variableName}['{$imageField}'] . strtolower(\$size) . '.jpg';\n\n        ");
     return true;
 }
Пример #3
0
    public static function generate(array $addOn, DevHelper_Config_Base $config, array $dataClass, array $info)
    {
        $className = self::getClassName($addOn, $config, $dataClass);
        $variableName = strtolower(substr($dataClass['camelCase'], 0, 1)) . substr($dataClass['camelCase'], 1);
        $modelClassName = DevHelper_Generator_Code_Model::getClassName($addOn, $config, $dataClass);
        $commentAutoGeneratedStart = DevHelper_Generator_File::COMMENT_AUTO_GENERATED_START;
        $commentAutoGeneratedEnd = DevHelper_Generator_File::COMMENT_AUTO_GENERATED_END;
        $dataWriterClassName = DevHelper_Generator_Code_DataWriter::getClassName($addOn, $config, $dataClass);
        $otherDataClassStuff = '';
        $filterParams = array();
        foreach ($dataClass['fields'] as $field) {
            if ($field['name'] != $dataClass['id_field']) {
                $filterParams[$field['name']] = $field['type'];
            }
        }
        $filterParams = DevHelper_Generator_File::varExport($filterParams, 2);
        $templateList = DevHelper_Generator_Template::getTemplateTitle($addOn, $config, $dataClass, $dataClass['name'] . '_list');
        $templateEdit = DevHelper_Generator_Template::getTemplateTitle($addOn, $config, $dataClass, $dataClass['name'] . '_edit');
        $templateDelete = DevHelper_Generator_Template::getTemplateTitle($addOn, $config, $dataClass, $dataClass['name'] . '_delete');
        $viewListClassName = self::getViewClassName($addOn, $config, $dataClass, 'list');
        $viewEditClassName = self::getViewClassName($addOn, $config, $dataClass, 'edit');
        $viewDeleteClassName = self::getViewClassName($addOn, $config, $dataClass, 'delete');
        // create the phrases
        $phraseClassName = DevHelper_Generator_Phrase::getPhraseName($addOn, $config, $dataClass, $dataClass['name']);
        $phraseAdd = DevHelper_Generator_Phrase::getPhraseName($addOn, $config, $dataClass, $dataClass['name'] . '_add');
        $phraseEdit = DevHelper_Generator_Phrase::getPhraseName($addOn, $config, $dataClass, $dataClass['name'] . '_edit');
        $phraseDelete = DevHelper_Generator_Phrase::getPhraseName($addOn, $config, $dataClass, $dataClass['name'] . '_delete');
        $phraseSave = DevHelper_Generator_Phrase::getPhraseName($addOn, $config, $dataClass, $dataClass['name'] . '_save');
        $phraseConfirmDeletion = DevHelper_Generator_Phrase::getPhraseName($addOn, $config, $dataClass, $dataClass['name'] . '_confirm_deletion');
        $phrasePleaseConfirm = DevHelper_Generator_Phrase::getPhraseName($addOn, $config, $dataClass, $dataClass['name'] . '_please_confirm');
        $phraseNotFound = DevHelper_Generator_Phrase::getPhraseName($addOn, $config, $dataClass, $dataClass['name'] . '_not_found');
        $phraseNoResults = DevHelper_Generator_Phrase::getPhraseName($addOn, $config, $dataClass, $dataClass['name'] . '_no_results');
        DevHelper_Generator_Phrase::generatePhrase($addOn, $phraseClassName, $dataClass['camelCaseWSpace']);
        DevHelper_Generator_Phrase::generatePhrase($addOn, $phraseAdd, 'Add New ' . $dataClass['camelCaseWSpace']);
        DevHelper_Generator_Phrase::generatePhrase($addOn, $phraseEdit, 'Edit ' . $dataClass['camelCaseWSpace']);
        DevHelper_Generator_Phrase::generatePhrase($addOn, $phraseDelete, 'Delete ' . $dataClass['camelCaseWSpace']);
        DevHelper_Generator_Phrase::generatePhrase($addOn, $phraseSave, 'Save ' . $dataClass['camelCaseWSpace']);
        DevHelper_Generator_Phrase::generatePhrase($addOn, $phraseConfirmDeletion, 'Confirm Deletion of ' . $dataClass['camelCaseWSpace']);
        DevHelper_Generator_Phrase::generatePhrase($addOn, $phrasePleaseConfirm, 'Please confirm that you want to delete the following ' . $dataClass['camelCaseWSpace']);
        DevHelper_Generator_Phrase::generatePhrase($addOn, $phraseNotFound, 'The requested ' . $dataClass['camelCaseWSpace'] . ' could not be found');
        DevHelper_Generator_Phrase::generatePhrase($addOn, $phraseNoResults, 'No clues of ' . $dataClass['camelCaseWSpace'] . ' at this moment...');
        // finished creating pharses
        // create the templates
        $templateListTemplate = <<<EOF
<xen:title>{xen:phrase {$phraseClassName}}</xen:title>

<xen:topctrl>
\t<a href="{xen:adminlink '{$info['routePrefix']}/add'}" class="button" accesskey="a">+ {xen:phrase {$phraseAdd}}</a>
</xen:topctrl>

<xen:require css="filter_list.css" />
<xen:require js="js/xenforo/filter_list.js" />

<xen:form action="{xen:adminlink '{$info['routePrefix']}'}" class="section">
\t<xen:if is="{\$all{$dataClass['camelCase']}}">
\t\t<h2 class="subHeading">
\t\t\t<link rel="xenforo_template" type="text/html" href="filter_list_controls.html" />
\t\t\t{xen:phrase {$phraseClassName}}
\t\t</h2>
\t
\t\t<ol class="FilterList Scrollable">
\t\t\t<xen:foreach loop="\$all{$dataClass['camelCase']}" value="\${$variableName}">
\t\t\t\t<xen:listitem
\t\t\t\t\tid="{\${$variableName}.{$dataClass['id_field']}}"
\t\t\t\t\thref="{xen:adminlink '{$info['routePrefix']}/edit', \${$variableName}}"
\t\t\t\t\tlabel="{\${$variableName}.{$dataClass['title_field']}}"
\t\t\t\t\tdelete="{xen:adminlink '{$info['routePrefix']}/delete', \${$variableName}}" />
\t\t\t</xen:foreach>
\t\t</ol>
\t
\t\t<p class="sectionFooter">{xen:phrase showing_x_of_y_items, 'count=<span class="FilterListCount">{xen:count \$all{$dataClass['camelCase']}}</span>', 'total={xen:count \$all{$dataClass['camelCase']}}'}</p>
\t<xen:else />
\t\t<div class="noResults">{xen:phrase {$phraseNoResults}}</div>
\t</xen:if>
</xen:form>
EOF;
        DevHelper_Generator_Template::generateAdminTemplate($addOn, $templateList, $templateListTemplate);
        // finished template_list
        $templateEditFields = '';
        foreach ($dataClass['fields'] as $field) {
            if ($field['name'] == $dataClass['id_field']) {
                continue;
            }
            if ($field['name'] == $dataClass['title_field']) {
                $fieldPhraseName = DevHelper_Generator_Phrase::generatePhraseAutoCamelCaseStyle($addOn, $config, $dataClass, $field['name']);
                $templateEditFields .= <<<EOF

\t<xen:textboxunit label="{xen:phrase {$fieldPhraseName}}:" name="{$field['name']}" value="{\${$variableName}.{$field['name']}}" data-liveTitleTemplate="{xen:if {\${$variableName}.{$dataClass['id_field']}},
\t\t'{xen:phrase {$phraseEdit}}: <em>%s</em>',
\t\t'{xen:phrase {$phraseAdd}}: <em>%s</em>'}" />
EOF;
                continue;
            }
            if (substr($field['name'], -3) == '_id') {
                // link to another dataClass?
                $other = substr($field['name'], 0, -3);
                if ($config->checkDataClassExists($other)) {
                    // yeah!
                    $otherDataClass = $config->getDataClass($other);
                    $fieldPhraseName = DevHelper_Generator_Phrase::generatePhraseAutoCamelCaseStyle($addOn, $config, $otherDataClass, $otherDataClass['name']);
                    $templateEditFields .= <<<EOF

\t<xen:selectunit label="{xen:phrase {$fieldPhraseName}}:" name="{$field['name']}" value="{\${$variableName}.{$field['name']}}">
\t\t<xen:option value="">&nbsp;</xen:option>
\t\t<xen:options source="\$all{$otherDataClass['camelCase']}" />
\t</xen:selectunit>
EOF;
                    $otherDataClassModelClassName = DevHelper_Generator_Code_Model::getClassName($addOn, $config, $otherDataClass);
                    $otherDataClassStuff .= <<<EOF
'all{$otherDataClass['camelCase']}' => \$this->getModelFromCache('{$otherDataClassModelClassName}')->getList(),
EOF;
                    continue;
                }
            }
            // special case with display_order
            if ($field['name'] == 'display_order') {
                $fieldPhraseName = DevHelper_Generator_Phrase::generatePhraseAutoCamelCaseStyle($addOn, $config, $dataClass, $field['name']);
                $templateEditFields .= <<<EOF

\t<xen:spinboxunit label="{xen:phrase {$fieldPhraseName}}:" name="{$field['name']}" value="{\${$variableName}.{$field['name']}}" />
EOF;
                continue;
            }
            $fieldPhraseName = DevHelper_Generator_Phrase::generatePhraseAutoCamelCaseStyle($addOn, $config, $dataClass, $field['name']);
            $extra = '';
            if ($field['type'] == XenForo_DataWriter::TYPE_STRING and (empty($field['length']) or $field['length'] > 255)) {
                $extra .= ' rows="5"';
            }
            $templateEditFields .= <<<EOF

\t<xen:textboxunit label="{xen:phrase {$fieldPhraseName}}:" name="{$field['name']}" value="{\${$variableName}.{$field['name']}}" {$extra}/>
EOF;
        }
        $templateEditTemplate = <<<EOF
<xen:title>{xen:if '{\${$variableName}.{$dataClass['id_field']}}', '{xen:phrase {$phraseEdit}}', '{xen:phrase {$phraseAdd}}'}</xen:title>

<xen:form action="{xen:adminlink '{$info['routePrefix']}/save'}" class="AutoValidator" data-redirect="yes">

\t{$templateEditFields}

\t<xen:submitunit save="{xen:phrase {$phraseSave}}">
\t\t<input type="button" name="delete" value="{xen:phrase {$phraseDelete}}"
\t\t\taccesskey="d" class="button OverlayTrigger"
\t\t\tdata-href="{xen:adminlink '{$info['routePrefix']}/delete', \${$variableName}}"
\t\t\t{xen:if '!{\${$variableName}.{$dataClass['id_field']}}', 'style="display: none"'}
\t\t/>
\t</xen:submitunit>
\t
\t<input type="hidden" name="{$dataClass['id_field']}" value="{\${$variableName}.{$dataClass['id_field']}}" />
</xen:form>
EOF;
        DevHelper_Generator_Template::generateAdminTemplate($addOn, $templateEdit, $templateEditTemplate);
        // finished template_edit
        $templateDeleteTemplate = <<<EOF
<xen:title>{xen:phrase {$phraseConfirmDeletion}}: {\${$variableName}.{$dataClass['title_field']}}</xen:title>
<xen:h1>{xen:phrase {$phraseConfirmDeletion}}</xen:h1>

<xen:navigation>
\t<xen:breadcrumb href="{xen:adminlink '{$info['routePrefix']}/edit', \${$variableName}}">{\${$variableName}.{$dataClass['title_field']}}</xen:breadcrumb>
</xen:navigation>

<xen:require css="delete_confirmation.css" />

<xen:form action="{xen:adminlink '{$info['routePrefix']}/delete', \${$variableName}}" class="deleteConfirmForm formOverlay">

\t<p>{xen:phrase {$phrasePleaseConfirm}}:</p>
\t<strong><a href="{xen:adminlink '{$info['routePrefix']}/edit', \${$variableName}}">{\${$variableName}.{$dataClass['title_field']}}</a></strong>

\t<xen:submitunit save="{xen:phrase {$phraseDelete}}" />
\t
\t<input type="hidden" name="_xfConfirm" value="1" />
</xen:form>
EOF;
        DevHelper_Generator_Template::generateAdminTemplate($addOn, $templateDelete, $templateDeleteTemplate);
        // finished creating our templates
        $contents = <<<EOF
<?php
class {$info['controller']} extends XenForo_ControllerAdmin_Abstract {

\t{$commentAutoGeneratedStart}

\tpublic function actionIndex() {
\t\t\$model = \$this->_get{$dataClass['camelCase']}Model();
\t\t\$all{$dataClass['camelCase']} = \$model->getAll{$dataClass['camelCase']}();
\t\t
\t\t\$viewParams = array(
\t\t\t'all{$dataClass['camelCase']}' => \$all{$dataClass['camelCase']}
\t\t);
\t\t
\t\treturn \$this->responseView('{$viewListClassName}', '{$templateList}', \$viewParams);
\t}
\t
\tpublic function actionAdd() {
\t\t\$viewParams = array(
\t\t\t'{$variableName}' => array(),
\t\t\t{$otherDataClassStuff}
\t\t);
\t\t
\t\treturn \$this->responseView('{$viewEditClassName}', '{$templateEdit}', \$viewParams);
\t}
\t
\tpublic function actionEdit() {
\t\t\$id = \$this->_input->filterSingle('{$dataClass['id_field']}', XenForo_Input::UINT);
\t\t\${$variableName} = \$this->_get{$dataClass['camelCase']}OrError(\$id);
\t\t
\t\t\$viewParams = array(
\t\t\t'{$variableName}' => \${$variableName},
\t\t\t{$otherDataClassStuff}
\t\t);
\t\t
\t\treturn \$this->responseView('{$viewEditClassName}', '{$templateEdit}', \$viewParams);
\t}
\t
\tpublic function actionSave() {
\t\t\$this->_assertPostOnly();
\t\t
\t\t\$id = \$this->_input->filterSingle('{$dataClass['id_field']}', XenForo_Input::UINT);

\t\t\$dwInput = \$this->_input->filter({$filterParams});
\t\t
\t\t\$dw = \$this->_get{$dataClass['camelCase']}DataWriter();
\t\tif (\$id) {
\t\t\t\$dw->setExistingData(\$id);
\t\t}
\t\t\$dw->bulkSet(\$dwInput);
\t\t\$dw->save();

\t\treturn \$this->responseRedirect(
\t\t\tXenForo_ControllerResponse_Redirect::SUCCESS,
\t\t\tXenForo_Link::buildAdminLink('{$info['routePrefix']}')
\t\t);
\t}
\t
\tpublic function actionDelete() {
\t\t\$id = \$this->_input->filterSingle('{$dataClass['id_field']}', XenForo_Input::UINT);
\t\t\${$variableName} = \$this->_get{$dataClass['camelCase']}OrError(\$id);
\t\t
\t\tif (\$this->isConfirmedPost()) {
\t\t\t\$dw = \$this->_get{$dataClass['camelCase']}DataWriter();
\t\t\t\$dw->setExistingData(\$id);
\t\t\t\$dw->delete();

\t\t\treturn \$this->responseRedirect(
\t\t\t\tXenForo_ControllerResponse_Redirect::SUCCESS,
\t\t\t\tXenForo_Link::buildAdminLink('{$info['routePrefix']}')
\t\t\t);
\t\t} else {
\t\t\t\$viewParams = array(
\t\t\t\t'{$variableName}' => \${$variableName}
\t\t\t);

\t\t\treturn \$this->responseView('{$viewDeleteClassName}', '{$templateDelete}', \$viewParams);
\t\t}
\t}
\t
\t
\tprotected function _get{$dataClass['camelCase']}OrError(\$id, array \$fetchOptions = array()) {
\t\t\$info = \$this->_get{$dataClass['camelCase']}Model()->get{$dataClass['camelCase']}ById(\$id, \$fetchOptions);
\t\t
\t\tif (empty(\$info)) {
\t\t\tthrow \$this->responseException(\$this->responseError(new XenForo_Phrase('{$phraseNotFound}'), 404));
\t\t}
\t\t
\t\treturn \$info;
\t}
\t
\tprotected function _get{$dataClass['camelCase']}Model() {
\t\treturn \$this->getModelFromCache('{$modelClassName}');
\t}
\t
\tprotected function _get{$dataClass['camelCase']}DataWriter() {
\t\treturn XenForo_DataWriter::create('{$dataWriterClassName}');
\t}

\t{$commentAutoGeneratedEnd}
}
EOF;
        return array($className, $contents);
    }
Пример #4
0
    protected function _generateTemplates($variableName, $variableNamePlural, $imageField)
    {
        if (count($this->_dataClass['primaryKey']) > 1) {
            throw new XenForo_Exception(sprintf('Cannot generate %s: too many fields in primary key', $this->_info['controller']));
        }
        $idField = reset($this->_dataClass['primaryKey']);
        $dataTitle = "\${$variableName}" . (empty($this->_dataClass['title_field']) ? ".{$idField}" : (is_array($this->_dataClass['title_field']) ? ".{$this->_dataClass['title_field'][0]}.{$this->_dataClass['title_field'][1]}" : ".{$this->_dataClass['title_field']}"));
        // create the phrases
        $phraseClassName = $this->_getPhraseName('');
        $phrasePlural = $this->_getPhrasePluralName();
        $phraseAdd = $this->_getPhraseName('_add');
        $phraseEdit = $this->_getPhraseName('_edit');
        $phraseDelete = $this->_getPhraseName('_delete');
        $phraseSave = $this->_getPhraseName('_save');
        $phraseConfirmDeletion = $this->_getPhraseName('_confirm_deletion');
        $phrasePleaseConfirm = $this->_getPhraseName('_please_confirm');
        $phraseNotFound = $this->_getPhraseName('_not_found');
        $phraseNoResults = $this->_getPhraseName('_no_results');
        $this->_generatePhrase($phraseClassName, $this->_dataClass['camelCaseWSpace']);
        if (!empty($this->_dataClass['camelCasePluralWSpace'])) {
            $this->_generatePhrase($phrasePlural, $this->_dataClass['camelCasePluralWSpace']);
        } else {
            $this->_generatePhrase($phrasePlural, $this->_dataClass['camelCaseWSpace'] . ' (Plural)');
        }
        $this->_generatePhrase($phraseAdd, 'Add New ' . $this->_dataClass['camelCaseWSpace']);
        $this->_generatePhrase($phraseEdit, 'Edit ' . $this->_dataClass['camelCaseWSpace']);
        $this->_generatePhrase($phraseDelete, 'Delete ' . $this->_dataClass['camelCaseWSpace']);
        $this->_generatePhrase($phraseSave, 'Save ' . $this->_dataClass['camelCaseWSpace']);
        $this->_generatePhrase($phraseConfirmDeletion, 'Confirm Deletion of ' . $this->_dataClass['camelCaseWSpace']);
        $this->_generatePhrase($phrasePleaseConfirm, 'Please confirm that you want to delete the following ' . strtolower($this->_dataClass['camelCaseWSpace']));
        $this->_generatePhrase($phraseNotFound, 'The requested ' . strtolower($this->_dataClass['camelCaseWSpace']) . ' could not be found');
        $this->_generatePhrase($phraseNoResults, 'No ' . strtolower($this->_dataClass['camelCaseWSpace']) . ' could be found...');
        // finished creating pharses
        $templateList = $this->_getTemplateTitle('_list');
        $templateListItems = $this->_getTemplateTitle('_list_items');
        $templateEdit = $this->_getTemplateTitle('_edit');
        $templateDelete = $this->_getTemplateTitle('_delete');
        // create the templates
        $listItemExtras = '';
        if ($imageField !== false) {
            $listItemExtras .= <<<EOF
\t\t<xen:beforelabel>
\t\t\t{xen:if '{\${$variableName}.images.s}', '<img src="{\${$variableName}.images.s}" style="float: left; height: 30px;" />'}
\t\t</xen:beforelabel>
EOF;
        }
        $templateListItemsTemplate = <<<EOF
<xen:foreach loop="\${$variableNamePlural}" value="\${$variableName}">
\t<xen:listitem
\t\tid="{\${$variableName}.{$idField}}"
\t\thref="{xen:adminlink '{$this->_info['routePrefix']}/edit', \${$variableName}}"
\t\tdelete="{xen:adminlink '{$this->_info['routePrefix']}/delete', \${$variableName}}">
\t\t<xen:label>{{$dataTitle}}</xen:label>{$listItemExtras}
\t</xen:listitem>
</xen:foreach>
EOF;
        $this->_generateAdminTemplate($templateListItems, $templateListItemsTemplate);
        // finished template_list_items
        $templateListTemplate = <<<EOF
<xen:title>{xen:phrase {$phrasePlural}}</xen:title>

<xen:topctrl>
\t<a href="{xen:adminlink '{$this->_info['routePrefix']}/add'}" class="button" accesskey="a">+ {xen:phrase {$phraseAdd}}</a>
</xen:topctrl>

<xen:require css="filter_list.css" />
<xen:require js="js/xenforo/filter_list.js" />

<xen:form action="{xen:adminlink '{$this->_info['routePrefix']}'}" class="section">
\t<xen:if is="{\${$variableNamePlural}}">
\t\t<h2 class="subHeading">
\t\t\t<xen:include template="filter_list_controls" />
\t\t\t{xen:phrase {$phrasePlural}}
\t\t</h2>

\t\t<ol class="FilterList Scrollable">
\t\t\t<xen:include template="{$templateListItems}" />
\t\t</ol>

\t\t<p class="sectionFooter">{xen:phrase showing_x_of_y_items, 'count=<span class="FilterListCount">{xen:count \${$variableNamePlural}}</span>', 'total={xen:count \${$variableNamePlural}}'}</p>
\t<xen:else />
\t\t<div class="noResults">{xen:phrase {$phraseNoResults}}</div>
\t</xen:if>
</xen:form>
EOF;
        $this->_generateAdminTemplate($templateList, $templateListTemplate);
        // finished template_list
        $templateEditFormExtra = 'class="AutoValidator" data-redirect="yes"';
        $templateEditFields = '';
        $extraViewParamsForTemplateEdit = array();
        $filterParams = array();
        if (!empty($this->_dataClass['phrases'])) {
            foreach ($this->_dataClass['phrases'] as $phraseType) {
                $fieldPhraseName = DevHelper_Generator_Phrase::generatePhraseAutoCamelCaseStyle($this->_addOn, $this->_config, $this->_dataClass, $this->_dataClass['name'] . '_' . $phraseType);
                $templateEditFields .= <<<EOF

\t<xen:textboxunit label="{xen:phrase {$fieldPhraseName}}:" name="_phrases[{$phraseType}]" value="{\${$variableName}.phrases.{$phraseType}}" />
EOF;
            }
        }
        foreach ($this->_dataClass['fields'] as $field) {
            if ($field['name'] == $idField) {
                continue;
            }
            if (empty($field['required'])) {
                // ignore non-required fields
                continue;
            }
            if ($field['name'] == $imageField) {
                // ignore image field
                continue;
            }
            // queue this field for validation
            $filterParams[$field['name']] = $field['type'];
            if ($field['name'] == $this->_dataClass['title_field']) {
                $fieldPhraseName = DevHelper_Generator_Phrase::generatePhraseAutoCamelCaseStyle($this->_addOn, $this->_config, $this->_dataClass, $field['name']);
                $templateEditFields .= <<<EOF

\t<xen:textboxunit label="{xen:phrase {$fieldPhraseName}}:" name="{$field['name']}" value="{\${$variableName}.{$field['name']}}" data-liveTitleTemplate="{xen:if {\${$variableName}.{$idField}},
\t\t'{xen:phrase {$phraseEdit}}: <em>%s</em>',
\t\t'{xen:phrase {$phraseAdd}}: <em>%s</em>'}" />
EOF;
                continue;
            }
            if (substr($field['name'], -3) == '_id') {
                // link to another dataClass?
                $other = substr($field['name'], 0, -3);
                if ($this->_config->checkDataClassExists($other)) {
                    // yeah!
                    $otherDataClass = $this->_config->getDataClass($other);
                    $fieldPhraseName = DevHelper_Generator_Phrase::generatePhraseAutoCamelCaseStyle($this->_addOn, $this->_config, $otherDataClass, $otherDataClass['name']);
                    $templateEditFields .= <<<EOF

\t<xen:selectunit label="{xen:phrase {$fieldPhraseName}}:" name="{$field['name']}" value="{\${$variableName}.{$field['name']}}">
\t\t<xen:option value="">&nbsp;</xen:option>
\t\t<xen:options source="\$list{$otherDataClass['camelCase']}" />
\t</xen:selectunit>
EOF;
                    $otherDataClassModelClassName = DevHelper_Generator_Code_Model::getClassName($this->_addOn, $this->_config, $otherDataClass);
                    $extraViewParamsForTemplateEdit["list{$otherDataClass['camelCase']}"] = "\$viewParams['list{$otherDataClass['camelCase']}'] = \$this->getModelFromCache('{$otherDataClassModelClassName}')->getList();";
                    continue;
                } elseif ($other === $this->_dataClass['name'] . '_parent') {
                    // just a parent of this same class
                    $fieldPhraseName = DevHelper_Generator_Phrase::generatePhraseAutoCamelCaseStyle($this->_addOn, $this->_config, $this->_dataClass, $other);
                    $templateEditFields .= <<<EOF

\t<xen:selectunit label="{xen:phrase {$fieldPhraseName}}:" name="{$field['name']}" value="{\${$variableName}.{$field['name']}}">
\t\t<xen:option value="">&nbsp;</xen:option>
\t\t<xen:options source="\$list{$this->_dataClass['camelCase']}" />
\t</xen:selectunit>
EOF;
                    $thisDataClassModelClassName = DevHelper_Generator_Code_Model::getClassName($this->_addOn, $this->_config, $this->_dataClass);
                    $extraViewParamsForTemplateEdit["list{$this->_dataClass['camelCase']}"] = "\$viewParams['list{$this->_dataClass['camelCase']}'] = \$this->getModelFromCache('{$thisDataClassModelClassName}')->getList();";
                    continue;
                }
            }
            if ($field['name'] == 'display_order') {
                // special case with display_order
                $templateEditFields .= <<<EOF

\t<xen:spinboxunit label="{xen:phrase display_order}:" name="{$field['name']}" value="{\${$variableName}.{$field['name']}}" />
EOF;
                continue;
            }
            if ($field['type'] == 'string' && !empty($field['allowedValues'])) {
                // render field with enum type as a list of selections
                $fieldPhraseName = DevHelper_Generator_Phrase::generatePhraseAutoCamelCaseStyle($this->_addOn, $this->_config, $this->_dataClass, $field['name']);
                $templateEditFields .= <<<EOF

\t<xen:selectunit label="{xen:phrase {$fieldPhraseName}}:" name="{$field['name']}" value="{\${$variableName}.{$field['name']}}">
\t\t<xen:option value="">&nbsp;</xen:option>
EOF;
                foreach ($field['allowedValues'] as $allowedValue) {
                    $allowedValuePhraseName = DevHelper_Generator_Phrase::generatePhraseAutoCamelCaseStyle($this->_addOn, $this->_config, $this->_dataClass, $field['name'] . '_' . $allowedValue);
                    $templateEditFields .= <<<EOF
\t\t<xen:option value="{$allowedValue}">{xen:phrase {$allowedValuePhraseName}}</xen:option>
EOF;
                }
                $templateEditFields .= <<<EOF
\t</xen:selectunit>
EOF;
                continue;
            }
            if ($field['type'] == 'uint' and substr($field['name'], 0, 3) === 'is_') {
                // special case with boolean fields
                $fieldPhraseName = DevHelper_Generator_Phrase::generatePhraseAutoCamelCaseStyle($this->_addOn, $this->_config, $this->_dataClass, $field['name']);
                $templateEditFields .= <<<EOF
\t<xen:checkboxunit label="">
\t\t<xen:option name="{$field['name']}" value="1" label="{xen:phrase {$fieldPhraseName}}" selected="{\${$variableName}.{$field['name']}}" />
\t</xen:checkboxunit>
EOF;
                continue;
            }
            $fieldPhraseName = DevHelper_Generator_Phrase::generatePhraseAutoCamelCaseStyle($this->_addOn, $this->_config, $this->_dataClass, $field['name']);
            $extra = '';
            if ($field['type'] == XenForo_DataWriter::TYPE_STRING and (empty($field['length']) or $field['length'] > 255)) {
                $extra .= ' rows="5"';
            }
            $templateEditFields .= <<<EOF

\t<xen:textboxunit label="{xen:phrase {$fieldPhraseName}}:" name="{$field['name']}" value="{\${$variableName}.{$field['name']}}" {$extra}/>
EOF;
        }
        if ($imageField !== false) {
            $fieldPhraseImage = DevHelper_Generator_Phrase::generatePhraseAutoCamelCaseStyle($this->_addOn, $this->_config, $this->_dataClass, 'image');
            $templateEditFormExtra = 'enctype="multipart/form-data"';
            $templateEditFields .= <<<EOF
\t<xen:uploadunit label="{xen:phrase {$fieldPhraseImage}}:" name="image" value="">
\t\t<div id="imageHtml">
\t\t\t<xen:if is="{\${$variableName}.images}">
\t\t\t\t<xen:foreach loop="\${$variableName}.images" key="\$imageSizeCode" value="\$image">
\t\t\t\t\t<img src="{\$image}" alt="{\$imageSizeCode}" title="{\$imageSizeCode}" />
\t\t\t\t</xen:foreach>
\t\t\t</xen:if>
\t\t</div>
\t</xen:uploadunit>
EOF;
        }
        $templateEditTemplate = <<<EOF
<xen:title>{xen:if '{\${$variableName}.{$idField}}', '{xen:phrase {$phraseEdit}}', '{xen:phrase {$phraseAdd}}'}</xen:title>

<xen:form action="{xen:adminlink '{$this->_info['routePrefix']}/save'}" {$templateEditFormExtra}>

\t{$templateEditFields}

\t<xen:submitunit save="{xen:phrase {$phraseSave}}">
\t\t<input type="button" name="delete" value="{xen:phrase {$phraseDelete}}"
\t\t\taccesskey="d" class="button OverlayTrigger"
\t\t\tdata-href="{xen:adminlink '{$this->_info['routePrefix']}/delete', \${$variableName}}"
\t\t\t{xen:if '!{\${$variableName}.{$idField}}', 'style="display: none"'}
\t\t/>
\t</xen:submitunit>

\t<input type="hidden" name="{$idField}" value="{\${$variableName}.{$idField}}" />
</xen:form>
EOF;
        $this->_generateAdminTemplate($templateEdit, $templateEditTemplate);
        // add extra code for add, edit actions
        if (!empty($extraViewParamsForTemplateEdit)) {
            $this->_addMethod('actionAdd', 'public', array(), implode("\n", $extraViewParamsForTemplateEdit), '100');
            $this->_addMethod('actionEdit', 'public', array(), implode("\n", $extraViewParamsForTemplateEdit), '100');
        }
        // add input fields to action save
        if (!empty($this->_dataClass['phrases'])) {
            $phraseCode = "\$phrases = \$this->_input->filterSingle('_phrases', XenForo_Input::ARRAY_SIMPLE);\n";
            foreach ($this->_dataClass['phrases'] as $phraseType) {
                $dataWriterClassName = DevHelper_Generator_Code_DataWriter::getClassName($this->_addOn, $this->_config, $this->_dataClass);
                $dataPhraseConstantName = DevHelper_Generator_Code_DataWriter::generateDataPhraseConstant($this->_addOn, $this->_config, $this->_dataClass, $phraseType);
                $phraseCode .= "if (isset(\$phrases['{$phraseType}']))\n{\n";
                $phraseCode .= "    \$dw->setExtraData({$dataWriterClassName}::{$dataPhraseConstantName}, \$phrases['{$phraseType}']);\n";
                $phraseCode .= "}\n";
            }
            $this->_addMethod('actionSave', 'public', array(), "\n\n// get phrases from input data\n{$phraseCode}\n\n        ", '100');
        }
        if (!empty($filterParams)) {
            $filterParamsExported = DevHelper_Generator_File::varExport($filterParams, 1);
            $this->_addMethod('actionSave', 'public', array(), "\n\n// get regular fields from input data\n\$dwInput = \$this->_input->filter({$filterParamsExported});\n\$dw->bulkSet(\$dwInput);\n\n            ", '200');
        }
        if ($imageField !== false) {
            // add image to action save
            $this->_addMethod('actionSave', 'public', array(), "\n\n// try to save the uploaded image if any\n\$image = XenForo_Upload::getUploadedFile('image');\nif (!empty(\$image)) {\n    \$dw->setImage(\$image);\n}\n\n            ", '300');
        }
        // finished template_edit
        $templateDeleteTemplate = <<<EOF
<xen:title>{xen:phrase {$phraseConfirmDeletion}}: {{$dataTitle}}</xen:title>
<xen:h1>{xen:phrase {$phraseConfirmDeletion}}</xen:h1>

<xen:navigation>
\t<xen:breadcrumb href="{xen:adminlink '{$this->_info['routePrefix']}/edit', \${$variableName}}">{{$dataTitle}}</xen:breadcrumb>
</xen:navigation>

<xen:require css="delete_confirmation.css" />

<xen:form action="{xen:adminlink '{$this->_info['routePrefix']}/delete', \${$variableName}}" class="deleteConfirmForm formOverlay">

\t<p>{xen:phrase {$phrasePleaseConfirm}}:</p>
\t<strong><a href="{xen:adminlink '{$this->_info['routePrefix']}/edit', \${$variableName}}">{{$dataTitle}}</a></strong>

\t<xen:submitunit save="{xen:phrase {$phraseDelete}}" />

\t<input type="hidden" name="_xfConfirm" value="1" />
</xen:form>
EOF;
        $this->_generateAdminTemplate($templateDelete, $templateDeleteTemplate);
        // finished creating our templates
        return array($templateList, $templateEdit, $templateDelete);
    }