| 1 | <?php |
| 2 | |
| 3 | namespace wcf\acp\form; |
| 4 | |
| 5 | use wcf\data\IStorableObject; |
| 6 | use wcf\data\package\Package; |
| 7 | use wcf\data\package\PackageCache; |
| 8 | use wcf\data\template\group\TemplateGroup; |
| 9 | use wcf\data\template\group\TemplateGroupNodeTree; |
| 10 | use wcf\data\template\Template; |
| 11 | use wcf\data\template\TemplateAction; |
| 12 | use wcf\form\AbstractFormBuilderForm; |
| 13 | use wcf\system\cache\builder\TemplateGroupCacheBuilder; |
| 14 | use wcf\system\database\util\PreparedStatementConditionBuilder; |
| 15 | use wcf\system\exception\IllegalLinkException; |
| 16 | use wcf\system\form\builder\container\FormContainer; |
| 17 | use wcf\system\form\builder\data\processor\CustomFormDataProcessor; |
| 18 | use wcf\system\form\builder\data\processor\VoidFormDataProcessor; |
| 19 | use wcf\system\form\builder\field\HiddenFormField; |
| 20 | use wcf\system\form\builder\field\SingleSelectionFormField; |
| 21 | use wcf\system\form\builder\field\SourceCodeFormField; |
| 22 | use wcf\system\form\builder\field\TextFormField; |
| 23 | use wcf\system\form\builder\field\validation\FormFieldValidationError; |
| 24 | use wcf\system\form\builder\field\validation\FormFieldValidator; |
| 25 | use wcf\system\form\builder\IFormDocument; |
| 26 | use wcf\system\template\TemplateEngine; |
| 27 | use wcf\system\WCF; |
| 28 | |
| 29 | /** |
| 30 | * Shows the form for adding new templates. |
| 31 | * |
| 32 | * @author Marcel Werk |
| 33 | * @copyright 2001-2024 WoltLab GmbH |
| 34 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> |
| 35 | */ |
| 36 | class TemplateAddForm extends AbstractFormBuilderForm |
| 37 | { |
| 38 | /** |
| 39 | * @inheritDoc |
| 40 | */ |
| 41 | public $activeMenuItem = 'wcf.acp.menu.link.template.add'; |
| 42 | |
| 43 | /** |
| 44 | * @inheritDoc |
| 45 | */ |
| 46 | public $neededPermissions = ['admin.template.canManageTemplate']; |
| 47 | |
| 48 | /** |
| 49 | * @inheritDoc |
| 50 | */ |
| 51 | public $objectActionClass = TemplateAction::class; |
| 52 | |
| 53 | /** |
| 54 | * @inheritDoc |
| 55 | */ |
| 56 | public $objectEditLinkController = TemplateEditForm::class; |
| 57 | |
| 58 | /** |
| 59 | * id of copied template |
| 60 | * @var int |
| 61 | */ |
| 62 | public int $copy = 0; |
| 63 | |
| 64 | /** |
| 65 | * copied template object |
| 66 | * @var Template |
| 67 | */ |
| 68 | public Template $copiedTemplate; |
| 69 | |
| 70 | #[\Override] |
| 71 | protected function createForm() |
| 72 | { |
| 73 | parent::createForm(); |
| 74 | |
| 75 | $this->form->appendChildren([ |
| 76 | FormContainer::create('general') |
| 77 | ->appendChildren([ |
| 78 | SingleSelectionFormField::create('templateGroupID') |
| 79 | ->label('wcf.acp.template.group') |
| 80 | ->options(new TemplateGroupNodeTree(), true) |
| 81 | ->addValidator( |
| 82 | new FormFieldValidator('sharedTemplate', function (SingleSelectionFormField $formField) { |
| 83 | $templateGroupID = $formField->getSaveValue(); |
| 84 | $templateGroup = TemplateGroupCacheBuilder::getInstance()->getData( |
| 85 | [], |
| 86 | $templateGroupID |
| 87 | ); |
| 88 | \assert($templateGroup instanceof TemplateGroup); |
| 89 | |
| 90 | $tplNameFormField = $formField->getDocument()->getNodeById('templateName'); |
| 91 | \assert($tplNameFormField instanceof TextFormField); |
| 92 | $tplName = $tplNameFormField->getSaveValue(); |
| 93 | |
| 94 | if ( |
| 95 | TemplateEngine::isSharedTemplate($tplName) |
| 96 | && $templateGroup->templateGroupFolderName !== '_wcf_shared/' |
| 97 | ) { |
| 98 | $formField->addValidationError( |
| 99 | new FormFieldValidationError( |
| 100 | 'invalid', |
| 101 | 'wcf.acp.template.group.error.shared' |
| 102 | ) |
| 103 | ); |
| 104 | } elseif ( |
| 105 | !TemplateEngine::isSharedTemplate($tplName) |
| 106 | && $templateGroup->templateGroupFolderName === '_wcf_shared/' |
| 107 | ) { |
| 108 | $formField->addValidationError( |
| 109 | new FormFieldValidationError( |
| 110 | 'invalid', |
| 111 | 'wcf.acp.template.group.error.notShared' |
| 112 | ) |
| 113 | ); |
| 114 | } |
| 115 | }) |
| 116 | ), |
| 117 | TextFormField::create('templateName') |
| 118 | ->required() |
| 119 | ->label('wcf.global.name') |
| 120 | ->addValidator( |
| 121 | new FormFieldValidator('fileName', function (TextFormField $formField) { |
| 122 | $tplName = $formField->getSaveValue(); |
| 123 | if (!\preg_match('/^[a-z0-9_\-]+$/i', $tplName)) { |
| 124 | $formField->addValidationError( |
| 125 | new FormFieldValidationError('invalid', 'wcf.acp.template.name.error.invalid') |
| 126 | ); |
| 127 | } |
| 128 | }) |
| 129 | ) |
| 130 | ->addValidator( |
| 131 | new FormFieldValidator('unique', function (TextFormField $formField) { |
| 132 | $templateGroupIDFormField = $formField->getDocument()->getNodeById('templateGroupID'); |
| 133 | \assert($templateGroupIDFormField instanceof SingleSelectionFormField); |
| 134 | |
| 135 | $conditionBuilder = new PreparedStatementConditionBuilder(); |
| 136 | $conditionBuilder->add('templateName = ?', [$formField->getSaveValue()]); |
| 137 | $conditionBuilder->add('templateGroupID = ?', [ |
| 138 | $templateGroupIDFormField->getSaveValue() |
| 139 | ]); |
| 140 | |
| 141 | if (isset($this->copiedTemplate)) { |
| 142 | $conditionBuilder->add( |
| 143 | '(packageID = ? OR application = ?)', |
| 144 | [$this->copiedTemplate->packageID, $this->copiedTemplate->application] |
| 145 | ); |
| 146 | } else { |
| 147 | $conditionBuilder->add('packageID = ?', [1]); |
| 148 | } |
| 149 | |
| 150 | if ($this->formAction === 'edit') { |
| 151 | $conditionBuilder->add('templateID <> ?', [$this->formObject->getObjectID()]); |
| 152 | } |
| 153 | |
| 154 | $sql = "SELECT COUNT(*) |
| 155 | FROM wcf" . WCF_N . "_template |
| 156 | " . $conditionBuilder; |
| 157 | $statement = WCF::getDB()->prepareStatement($sql); |
| 158 | $statement->execute($conditionBuilder->getParameters()); |
| 159 | |
| 160 | if ($statement->fetchSingleColumn()) { |
| 161 | $formField->addValidationError( |
| 162 | new FormFieldValidationError( |
| 163 | 'notUnique', |
| 164 | 'wcf.acp.template.name.error.notUnique' |
| 165 | ) |
| 166 | ); |
| 167 | } |
| 168 | }) |
| 169 | ), |
| 170 | ]), |
| 171 | FormContainer::create('source') |
| 172 | ->label('wcf.acp.template.source') |
| 173 | ->appendChildren([ |
| 174 | SourceCodeFormField::create('templateSource') |
| 175 | ->language('smarty') |
| 176 | ->label('wcf.acp.template.source'), |
| 177 | ]), |
| 178 | HiddenFormField::create('copy') |
| 179 | ->value($this->copy), |
| 180 | ]); |
| 181 | |
| 182 | $this->form->getDataHandler() |
| 183 | ->addProcessor( |
| 184 | new CustomFormDataProcessor( |
| 185 | 'source', |
| 186 | static function (IFormDocument $document, array $parameters) { |
| 187 | $parameters['source'] = $parameters['data']['templateSource']; |
| 188 | |
| 189 | return $parameters; |
| 190 | }, |
| 191 | function (IFormDocument $document, array $data, IStorableObject $object) { |
| 192 | \assert($object instanceof Template); |
| 193 | $data['templateSource'] = $object->getSource(); |
| 194 | |
| 195 | return $data; |
| 196 | } |
| 197 | ) |
| 198 | ) |
| 199 | ->addProcessor(new VoidFormDataProcessor('copy')) |
| 200 | ->addProcessor(new VoidFormDataProcessor('templateSource')); |
| 201 | |
| 202 | if ($this->formAction === 'create') { |
| 203 | $this->form->getDataHandler() |
| 204 | ->addProcessor( |
| 205 | new CustomFormDataProcessor( |
| 206 | 'application', |
| 207 | function (IFormDocument $document, array $parameters) { |
| 208 | if (isset($this->copiedTemplate)) { |
| 209 | $parameters['data']['application'] = $this->copiedTemplate->application; |
| 210 | } else { |
| 211 | $sql = "SELECT packageID |
| 212 | FROM wcf" . WCF_N . "_template |
| 213 | WHERE templateName = ? |
| 214 | AND templateGroupID IS NULL"; |
| 215 | $statement = WCF::getDB()->prepareStatement($sql); |
| 216 | $statement->execute([ |
| 217 | $parameters['data']['templateName'] |
| 218 | ]); |
| 219 | $packageID = $statement->fetchSingleRow() ?: 1; |
| 220 | |
| 221 | $parameters['data']['application'] = Package::getAbbreviation( |
| 222 | PackageCache::getInstance()->getPackage($packageID)->package |
| 223 | ); |
| 224 | } |
| 225 | |
| 226 | return $parameters; |
| 227 | } |
| 228 | ) |
| 229 | ); |
| 230 | } |
| 231 | } |
| 232 | |
| 233 | #[\Override] |
| 234 | public function readParameters() |
| 235 | { |
| 236 | parent::readParameters(); |
| 237 | if (!empty($_REQUEST['copy'])) { |
| 238 | $this->copy = \intval($_REQUEST['copy']); |
| 239 | $this->copiedTemplate = new Template($this->copy); |
| 240 | if (!$this->copiedTemplate->templateID) { |
| 241 | throw new IllegalLinkException(); |
| 242 | } |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | #[\Override] |
| 247 | public function readData() |
| 248 | { |
| 249 | parent::readData(); |
| 250 | |
| 251 | if ($_POST === [] && isset($this->copiedTemplate)) { |
| 252 | $this->form->getNodeById('templateSource')->value($this->copiedTemplate->getSource()); |
| 253 | $this->form->getNodeById('templateName')->value($this->copiedTemplate->templateName); |
| 254 | } |
| 255 | } |
| 256 | } |