/** * Handle form upload of an addon zip archive. * * @return array */ protected function handleAddonUpload() { $Upload = new Gdn_Upload(); $Upload->allowFileExtension(null); $Upload->allowFileExtension('zip'); $AnalyzedAddon = []; try { // Validate the upload. $TmpFile = $Upload->validateUpload('File'); $Extension = pathinfo($Upload->getUploadedFileName(), PATHINFO_EXTENSION); // Generate the target name. $TargetFile = $Upload->generateTargetName('addons', $Extension); $TargetPath = PATH_UPLOADS . '/' . $TargetFile; if (!file_exists(dirname($TargetPath))) { mkdir(dirname($TargetPath), 0777, true); } // Save the file to a temporary location for parsing. if (!move_uploaded_file($TmpFile, $TargetPath)) { throw new Exception("We couldn't save the file you uploaded. Please try again later.", 400); } $AnalyzedAddon = UpdateModel::analyzeAddon($TargetPath, true); // If the long description is blank, load up the readme if it exists $formDescription = $this->Form->getFormValue('Description2', ''); if ($formDescription == '') { $Readme = $this->parseReadme($TargetPath); if ($Readme) { $AnalyzedAddon['Description2'] = $Readme; } } else { $AnalyzedAddon['Description2'] = $formDescription; } // Get an icon if one exists. $Icon = $this->extractIcon($TargetPath, val('Icon', $AnalyzedAddon, '')); if ($Icon) { // Overwrite info array value with the path to the saved file. $AnalyzedAddon['Icon'] = $Icon; } // Set the filename for the CDN. $Upload->EventArguments['OriginalFilename'] = AddonModel::slug($AnalyzedAddon, true) . '.zip'; // Save the uploaded file. After this, we no longer have a local copy to analyze. $Parsed = $Upload->saveAs($TargetPath, $TargetFile); $AnalyzedAddon['File'] = $Parsed['SaveName']; unset($AnalyzedAddon['Path']); trace($AnalyzedAddon, 'Analyzed Addon'); $this->Form->formValues($AnalyzedAddon); } catch (Exception $ex) { $this->Form->addError($ex); // Delete the erroneous file. try { if (isset($AnalyzedAddon) && isset($AnalyzedAddon['File'])) { $Upload->delete($AnalyzedAddon['File']); } } catch (Exception $Ex2) { } } if (isset($TargetPath) && file_exists($TargetPath)) { unlink($TargetPath); } return $AnalyzedAddon; }
/** * Save the addon data. * * @param array $Stub * @param bool|array $Settings Not used; for signature compatibility. * @return bool|Gdn_DataSet|mixed|object|string */ public function save($Stub, $Settings = false) { trace('AddonModel->Save()'); $Session = Gdn::session(); $this->defineSchema(); // Most of the values come from the file itself. if (isset($Stub['Path'])) { $Path = $Stub['Path']; } elseif (val('Checked', $Stub)) { $Addon = $Stub; } elseif (isset($Stub['File'])) { $Path = combinePaths(array(PATH_UPLOADS, $Stub['File'])); } else { if (!$Session->checkPermission('Addons.Addon.Manage') && isset($Stub['Filename'])) { // Only admins can modify plugin attributes without the file. $this->Validation->addValidationResult('Filename', 'ValidateRequired'); return false; } } // Analyze and fix the file. if (!isset($Addon)) { if (isset($Path)) { try { $Addon = UpdateModel::analyzeAddon($Path, false); } catch (Exception $Ex) { $Addon = false; $this->Validation->addValidationResult('File', '@' . $Ex->getMessage()); } if (!is_array($Addon)) { $this->Validation->addValidationResult('File', 'Could not analyze the addon file.'); return false; } $Addon = array_merge($Stub, $Addon); } else { $Addon = $Stub; if (isset($Path)) { $Addon['MD5'] = md5_file($Path); $Addon['FileSize'] = filesize($Path); } } } // Get an existing addon. if (isset($Addon['AddonID'])) { $CurrentAddon = $this->getID($Addon['AddonID'], false, ['GetVersions' => true]); } elseif (isset($Addon['AddonKey']) && isset($Addon['AddonTypeID'])) { $CurrentAddon = $this->getID(array($Addon['AddonKey'], $Addon['AddonTypeID']), false, ['GetVersions' => true]); } else { $CurrentAddon = false; } trace($CurrentAddon, 'CurrentAddon'); $Insert = !$CurrentAddon; if ($Insert) { $this->addInsertFields($Addon); } $this->addUpdateFields($Addon); // always add update fields if (!$this->validate($Addon, $Insert)) { trace('Addon did not validate'); return false; } // Search for the current version. $MaxVersion = false; $CurrentVersion = false; if ($CurrentAddon && isset($Addon['Version'])) { // Search for a current version. foreach ($CurrentAddon['Versions'] as $Index => $Version) { if (isset($Addon['AddonVersionID'])) { if ($Addon['AddonVersionID'] == $Version['AddonVersionID']) { $CurrentVersion = $Version; } } elseif (version_compare($Addon['Version'], $Version['Version']) == 0) { $CurrentVersion = $Version; } // Only check for a current version if the version has been checked. if (!$Version['Checked']) { continue; } if (!$MaxVersion || version_compare($MaxVersion['Version'], $Version['Version'], '<')) { $MaxVersion = $Version; } } } // Save the addon. $Fields = $this->filterSchema($Addon); if ($Insert) { $AddonID = $this->SQL->insert($this->Name, $Fields); // Add the activity. $ActivityModel = new ActivityModel(); $Activity = array('ActivityType' => 'Addon', 'ActivityUserID' => $Fields['InsertUserID'], 'NotifyUserID' => ActivityModel::NOTIFY_PUBLIC, 'HeadlineFormat' => '{ActivityUserID,user} added the <a href="{Url,html}">{Data.Name}</a> addon.', 'Story' => Gdn_Format::html($Fields['Description']), 'Route' => '/addon/' . rawurlencode(self::slug($Fields, false)), 'Data' => array('Name' => $Fields['Name'])); $ActivityModel->save($Activity); } else { $AddonID = val('AddonID', $CurrentAddon); // Only save the addon if it is the current version. if (!$MaxVersion || version_compare($Addon['Version'], $MaxVersion['Version'], '>=')) { trace('Uploaded version is the most recent version.'); $this->SQL->put($this->Name, $Fields, array('AddonID' => $AddonID)); } else { $this->SQL->reset(); } } // Save the version. if ($AddonID && isset($Path) || isset($Addon['File'])) { trace('Saving addon version'); $Addon['AddonID'] = $AddonID; if (isset($Path)) { if (!stringBeginsWith($Path, PATH_UPLOADS . '/addons/')) { // The addon must be copied into the uploads folder. $NewPath = PATH_UPLOADS . '/addons/' . basename($Path); //rename($Path, $NewPath); $Path = $NewPath; $this->_AddonCache = array(); } $File = substr($Path, strlen(PATH_UPLOADS . '/')); $Addon['File'] = $File; } if ($CurrentVersion) { $Addon['AddonVersionID'] = val('AddonVersionID', $CurrentVersion); } // Insert or update the version. $VersionModel = new Gdn_Model('AddonVersion'); $AddonVersionID = $VersionModel->save($Addon); $this->Validation->addValidationResult($VersionModel->validationResults()); if (!$AddonVersionID) { return false; } // Update the current version in the addon. if (!$MaxVersion || version_compare($CurrentAddon['Version'], $Addon['Version'], '<')) { $this->SQL->put($this->Name, array('CurrentAddonVersionID' => $AddonVersionID), array('AddonID' => $AddonID)); } } $this->_AddonCache = array(); return $AddonID; }