/** * Cleanup cache files */ private function cleanupCache() { $files = SpoonFile::getList($this->cachePath); foreach ($files as $file) { $fileinfo = SpoonFile::getInfo($this->cachePath . '/' . $file); // delete file if more than 1 week old if ($fileinfo['modification_date'] < strtotime('-1 week')) { SpoonFile::delete($this->cachePath . '/' . $file); } } }
/** * Cleanup cache files * * @return void */ private function cleanupCache() { // get cache files $files = SpoonFile::getList($this->cachePath); // loop items foreach ($files as $file) { // get info $fileinfo = SpoonFile::getInfo($this->cachePath . '/' . $file); // file is more than one week old if ($fileinfo['modification_date'] < strtotime('-1 week')) { // delete file SpoonFile::delete($this->cachePath . '/' . $file); } } }
/** * Execute the action */ public function execute() { $this->id = $this->getParameter('id', 'int'); // does the item exist if ($this->id !== null && BackendAddressesModel::exists($this->id)) { parent::execute(); $this->record = (array) BackendAddressesModel::get($this->id); BackendAddressesModel::delete($this->id); BackendAddressesModel::deleteGroupsFromAddress($this->id); // delete the image \SpoonFile::delete(FRONTEND_FILES_PATH . '/Addresses/Images/Source/' . $this->record['image']); BackendSearchModel::removeIndex($this->getModule(), $this->id); BackendModel::triggerEvent($this->getModule(), 'after_delete', array('id' => $this->id)); $this->redirect(BackendModel::createURLForAction('index') . '&report=deleted&var=' . urlencode($this->record['id'])); } else { $this->redirect(BackendModel::createURLForAction('index') . '&error=non-existing'); } }
/** * Execute the action */ public function execute() { // get parameters $this->id = $this->getParameter('id', 'int'); // does the item exist if ($this->id !== null && BackendBlogModel::exists($this->id)) { // call parent, this will probably add some general CSS/JS or other required files parent::execute(); // set category id $this->categoryId = SpoonFilter::getGetValue('category', null, null, 'int'); if ($this->categoryId == 0) { $this->categoryId = null; } // get data $this->record = (array) BackendBlogModel::get($this->id); // delete item BackendBlogModel::delete($this->id); // delete the image SpoonFile::delete(FRONTEND_FILES_PATH . '/blog/images/source/' . $this->record['image']); // trigger event BackendModel::triggerEvent($this->getModule(), 'after_delete', array('id' => $this->id)); // delete search indexes if (is_callable(array('BackendSearchModel', 'removeIndex'))) { BackendSearchModel::removeIndex($this->getModule(), $this->id); } // build redirect URL $redirectUrl = BackendModel::createURLForAction('index') . '&report=deleted&var=' . urlencode($this->record['title']); // append to redirect URL if ($this->categoryId != null) { $redirectUrl .= '&category=' . $this->categoryId; } // item was deleted, so redirect $this->redirect($redirectUrl); } else { $this->redirect(BackendModel::createURLForAction('index') . '&error=non-existing'); } }
/** * Add a search index * * @param string $module The module wherin will be searched. * @param int $otherId The id of the record. * @param array $fields A key/value pair of fields to index. * @param string[optional] $language The frontend language for this entry. */ protected function addSearchIndex($module, $otherId, array $fields, $language) { // get db $db = $this->getDB(); // validate cache if (empty(self::$modules)) { // get all modules self::$modules = (array) $db->getColumn('SELECT m.name FROM modules AS m'); } // module exists? if (!in_array('search', self::$modules)) { return; } // no fields? if (empty($fields)) { return; } // insert search index foreach ($fields as $field => $value) { // reformat value $value = strip_tags((string) $value); // insert in db $db->execute('INSERT INTO search_index (module, other_id, language, field, value, active) VALUES (?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE value = ?, active = ?', array((string) $module, (int) $otherId, (string) $language, (string) $field, $value, 'Y', $value, 'Y')); } // invalidate the cache for search foreach (SpoonFile::getList(FRONTEND_CACHE_PATH . '/search/') as $file) { SpoonFile::delete(FRONTEND_CACHE_PATH . '/search/' . $file); } }
/** * Remove all cache files * * @return void */ public static function removeCacheFiles() { // get path $cachePath = BACKEND_CACHE_PATH . '/analytics'; // loop all cache files foreach (SpoonFile::getList($cachePath) as $file) { SpoonFile::delete($cachePath . '/' . $file); } }
/** * Validate the form */ private function validateForm() { // is the form submitted? if ($this->frm->isSubmitted()) { // cleanup the submitted fields, ignore fields that were added by hackers $this->frm->cleanupFields(); $fields = $this->frm->getFields(); // email is present if (!$this->user->isGod()) { if ($fields['email']->isFilled(BL::err('EmailIsRequired'))) { // is this an email-address if ($fields['email']->isEmail(BL::err('EmailIsInvalid'))) { // was this emailaddress deleted before if (BackendUsersModel::emailDeletedBefore($fields['email']->getValue())) { $fields['email']->addError(sprintf(BL::err('EmailWasDeletedBefore'), BackendModel::createURLForAction('undo_delete', null, null, array('email' => $fields['email']->getValue())))); } elseif (BackendUsersModel::existsEmail($fields['email']->getValue(), $this->id)) { $fields['email']->addError(BL::err('EmailAlreadyExists')); } } } } // required fields if ($this->user->isGod() && $fields['email']->getValue() != '' && $this->user->getEmail() != $fields['email']->getValue()) { $fields['email']->addError(BL::err('CantChangeGodsEmail')); } if (!$this->user->isGod()) { $fields['email']->isEmail(BL::err('EmailIsInvalid')); } $fields['nickname']->isFilled(BL::err('NicknameIsRequired')); $fields['name']->isFilled(BL::err('NameIsRequired')); $fields['surname']->isFilled(BL::err('SurnameIsRequired')); $fields['interface_language']->isFilled(BL::err('FieldIsRequired')); $fields['date_format']->isFilled(BL::err('FieldIsRequired')); $fields['time_format']->isFilled(BL::err('FieldIsRequired')); $fields['number_format']->isFilled(BL::err('FieldIsRequired')); $fields['groups']->isFilled(BL::err('FieldIsRequired')); if (isset($fields['new_password']) && $fields['new_password']->isFilled()) { if ($fields['new_password']->getValue() !== $fields['confirm_password']->getValue()) { $fields['confirm_password']->addError(BL::err('ValuesDontMatch')); } } // validate avatar if ($fields['avatar']->isFilled()) { // correct extension if ($fields['avatar']->isAllowedExtension(array('jpg', 'jpeg', 'gif', 'png'), BL::err('JPGGIFAndPNGOnly'))) { // correct mimetype? $fields['avatar']->isAllowedMimeType(array('image/gif', 'image/jpg', 'image/jpeg', 'image/png'), BL::err('JPGGIFAndPNGOnly')); } } // no errors? if ($this->frm->isCorrect()) { // build user-array $user['id'] = $this->id; if (!$this->user->isGod()) { $user['email'] = $fields['email']->getValue(true); } if (BackendAuthentication::getUser()->getUserId() != $this->record['id']) { $user['active'] = $fields['active']->isChecked() ? 'Y' : 'N'; } // update password (only if filled in) if (isset($fields['new_password']) && $fields['new_password']->isFilled()) { $user['password'] = BackendAuthentication::getEncryptedString($fields['new_password']->getValue(), $this->record['settings']['password_key']); } // build settings-array $settings['nickname'] = $fields['nickname']->getValue(); $settings['name'] = $fields['name']->getValue(); $settings['surname'] = $fields['surname']->getValue(); $settings['interface_language'] = $fields['interface_language']->getValue(); $settings['date_format'] = $fields['date_format']->getValue(); $settings['time_format'] = $fields['time_format']->getValue(); $settings['datetime_format'] = $settings['date_format'] . ' ' . $settings['time_format']; $settings['number_format'] = $fields['number_format']->getValue(); $settings['csv_split_character'] = $fields['csv_split_character']->getValue(); $settings['csv_line_ending'] = $fields['csv_line_ending']->getValue(); $settings['api_access'] = (bool) $fields['api_access']->getChecked(); // get selected groups $groups = $fields['groups']->getChecked(); // init var $newSequence = BackendGroupsModel::getSetting($groups[0], 'dashboard_sequence'); // loop through groups and collect all dashboard widget sequences foreach ($groups as $group) { $sequences[] = BackendGroupsModel::getSetting($group, 'dashboard_sequence'); } // loop through sequences foreach ($sequences as $sequence) { // loop through modules inside a sequence foreach ($sequence as $moduleKey => $module) { // loop through widgets inside a module foreach ($module as $widgetKey => $widget) { // if widget present set true if ($widget['present']) { $newSequence[$moduleKey][$widgetKey]['present'] = true; } } } } // add new sequence to settings $settings['dashboard_sequence'] = $newSequence; // has the user submitted an avatar? if ($fields['avatar']->isFilled()) { // delete old avatar if it isn't the default-image if ($this->record['settings']['avatar'] != 'no-avatar.jpg') { SpoonFile::delete(FRONTEND_FILES_PATH . '/backend_users/avatars/source/' . $this->record['settings']['avatar']); SpoonFile::delete(FRONTEND_FILES_PATH . '/backend_users/avatars/128x128/' . $this->record['settings']['avatar']); SpoonFile::delete(FRONTEND_FILES_PATH . '/backend_users/avatars/64x64/' . $this->record['settings']['avatar']); SpoonFile::delete(FRONTEND_FILES_PATH . '/backend_users/avatars/32x32/' . $this->record['settings']['avatar']); } // create new filename $filename = rand(0, 3) . '_' . $user['id'] . '.' . $fields['avatar']->getExtension(); // add into settings to update $settings['avatar'] = $filename; // resize (128x128) $fields['avatar']->createThumbnail(FRONTEND_FILES_PATH . '/backend_users/avatars/128x128/' . $filename, 128, 128, true, false, 100); // resize (64x64) $fields['avatar']->createThumbnail(FRONTEND_FILES_PATH . '/backend_users/avatars/64x64/' . $filename, 64, 64, true, false, 100); // resize (32x32) $fields['avatar']->createThumbnail(FRONTEND_FILES_PATH . '/backend_users/avatars/32x32/' . $filename, 32, 32, true, false, 100); } // save changes BackendUsersModel::update($user, $settings); // save groups BackendGroupsModel::insertMultipleGroups($this->id, $groups); // trigger event BackendModel::triggerEvent($this->getModule(), 'after_edit', array('item' => $user)); // everything is saved, so redirect to the overview $this->redirect(BackendModel::createURLForAction('index') . '&report=edited&var=' . $settings['nickname'] . '&highlight=row-' . $user['id']); } } }
/** * Check if a directory is writable. * The default is_writable function has problems due to Windows ACLs "bug" * * @return bool * @param string $path */ private static function isWritable($path) { // redefine argument $path = (string) $path; // create temporary file $file = tempnam($path, 'isWritable'); // file has been created if ($file !== false) { // remove temporary file SpoonFile::delete($file); // file could not be created = writable return true; } // file could not be created = not writable return false; }
/** * @param array $ids */ public static function deleteFile(array $ids) { if (empty($ids)) { return; } foreach ($ids as $id) { $item = self::getFile($id); $product = self::get($item['product_id']); // delete file reference from db BackendModel::getContainer()->get('database')->delete('catalog_files', 'id = ?', array($id)); // delete file from disk $basePath = FRONTEND_FILES_PATH . '/catalog/' . $item['product_id']; \SpoonFile::delete($basePath . '/source/' . $item['filename']); } }
/** * * Delete image from an album * * @param $id */ public static function delete($id) { //--Get the image $image = self::get((int) $id); if (!empty($image)) { //--Get folders $folders = BackendModel::getThumbnailFolders(FRONTEND_FILES_PATH . '/Galleria/Images', true); //--Loop the folders foreach ($folders as $folder) { //--Delete the image \SpoonFile::delete($folder['url'] . '/' . $folder['dirname'] . '/' . $image['filename']); } //--Delete images from the database BackendModel::getContainer()->get('database')->delete("galleria_images", "id=?", array($id)); } }
/** * Download a file from a public URL. * * @return bool True if the file was downloaded, false if not. * @param string $sourceURL The URL of the file to download. * @param string $destinationPath The path where the file should be downloaded to. * @param bool[optional] $overwrite In case the destinationPath already exists, should we overwrite this file? */ public static function download($sourceURL, $destinationPath, $overwrite = true) { // check if curl is available if (!function_exists('curl_init')) { throw new SpoonFileException('This method requires cURL (http://php.net/curl), it seems like the extension isn\'t installed.'); } // redefine $sourceURL = (string) $sourceURL; $destinationPath = (string) $destinationPath; $overwrite = (bool) $overwrite; // validate if the file already exists if (!$overwrite && self::exists($destinationPath)) { return false; } // open file handler $fileHandle = @fopen($destinationPath, 'w'); // validate filehandle if ($fileHandle === false) { return false; } $options[CURLOPT_URL] = $sourceURL; $options[CURLOPT_FILE] = $fileHandle; $options[CURLOPT_HEADER] = false; if (ini_get('open_basedir') == '' && ini_get('safe_mode' == 'Off')) { $options[CURLOPT_FOLLOWLOCATION] = true; } // init curl $curl = curl_init(); // set options curl_setopt_array($curl, $options); // execute the call curl_exec($curl); // get errornumber $errorNumber = curl_errno($curl); $errorMessage = curl_error($curl); $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); // close curl_close($curl); fclose($fileHandle); // validate the errornumber if ($errorNumber != 0) { throw new SpoonFileException($errorMessage); } if ($httpCode != 200) { // delete the destination path file, which is empty SpoonFile::delete($destinationPath); // throw exception throw new SpoonFileException('The file "' . $sourceURL . '" isn\'t available for download.'); } // return return true; }
/** * Invalidate search cache */ public static function invalidateCache() { foreach (SpoonFile::getList(FRONTEND_CACHE_PATH . '/search/') as $file) { SpoonFile::delete(FRONTEND_CACHE_PATH . '/search/' . $file); } }
/** * Validate the form */ protected function validateForm() { if ($this->frm->isSubmitted()) { $this->frm->cleanupFields(); // validation $fields = $this->frm->getFields(); // $fields['name']->isFilled(BL::err('FieldIsRequired')); $this->meta->validate(); if ($this->frm->isCorrect()) { $item['meta_id'] = $this->meta->save(); $item['company'] = $fields['company']->getValue(); $item['name'] = $fields['name']->getValue(); $item['firstname'] = $fields['firstname']->getValue(); $item['email'] = $fields['email']->getValue(); $item['address'] = $fields['address']->getValue(); $item['zipcode'] = $fields['zipcode']->getValue(); $item['city'] = $fields['city']->getValue(); $item['country'] = $fields['country']->getValue(); $item['phone'] = $fields['phone']->getValue(); $item['fax'] = $fields['fax']->getValue(); $item['website'] = str_replace("http://", "", $fields['website']->getValue()); $item['zipcodes'] = $fields['zipcodes']->getValue(); $item['remark'] = $fields['remark']->getValue(); //$item['text'] = $fields['text']->getValue(); //$item['assort'] = $fields['assort']->getValue(); //$item['open'] = $fields['open']->getValue(); //$item['closed'] = $fields['closed']->getValue(); //$item['visit'] = $fields['visit']->getValue(); //$item['size'] = $fields['size']->getValue(); $item['language'] = BL::getWorkingLanguage(); $item['hidden'] = $fields['hidden']->getValue(); if ($item['country'] == '') { $item['country'] = 'BE'; } //--Create url $url = 'http://maps.googleapis.com/maps/api/geocode/json?address=' . urlencode($item['address'] . ', ' . $item['zipcode'] . ' ' . $item['city'] . ', ' . \SpoonLocale::getCountry($item['country'], BL::getWorkingLanguage())) . '&sensor=false'; //--Get lat $geocode = json_decode(\SpoonHTTP::getContent($url)); //--Sleep between the requests sleep(0.05); //--Check result $item['lat'] = isset($geocode->results[0]->geometry->location->lat) ? $geocode->results[0]->geometry->location->lat : null; $item['lng'] = isset($geocode->results[0]->geometry->location->lng) ? $geocode->results[0]->geometry->location->lng : null; $item['image'] = $this->record['image']; // the image path $imagePath = FRONTEND_FILES_PATH . '/Addresses/Images'; // create folders if needed if (!\SpoonDirectory::exists($imagePath . '/Source')) { \SpoonDirectory::create($imagePath . '/Source'); } if (!\SpoonDirectory::exists($imagePath . '/128x128')) { \SpoonDirectory::create($imagePath . '/128x128'); } if (!\SpoonDirectory::exists($imagePath . '/400x300')) { \SpoonDirectory::create($imagePath . '/400x300'); } if (!\SpoonDirectory::exists($imagePath . '/800x')) { \SpoonDirectory::create($imagePath . '/800x'); } // if the image should be deleted if ($this->frm->getField('delete_image')->isChecked()) { // delete the image \SpoonFile::delete($imagePath . '/Source/' . $item['image']); // reset the name $item['image'] = null; } // new image given? if ($this->frm->getField('image')->isFilled()) { // delete the old image \SpoonFile::delete($imagePath . '/Source/' . $this->record['image']); // build the image name $item['image'] = $this->meta->getURL() . '.' . $this->frm->getField('image')->getExtension(); // upload the image & generate thumbnails $this->frm->getField('image')->generateThumbnails($imagePath, $item['image']); } elseif ($item['image'] != null) { // get the old file extension $imageExtension = \SpoonFile::getExtension($imagePath . '/Source/' . $item['image']); // get the new image name $newName = $this->meta->getURL() . '.' . $imageExtension; // only change the name if there is a difference if ($newName != $item['image']) { // loop folders foreach (BackendModel::getThumbnailFolders($imagePath, true) as $folder) { // move the old file to the new name \SpoonFile::move($folder['path'] . '/' . $item['image'], $folder['path'] . '/' . $newName); } // assign the new name to the database $item['image'] = $newName; } } BackendAddressesModel::update($this->id, $item); $item['id'] = $this->id; //--Add the languages foreach ((array) BackendModel::get('fork.settings')->get('Core', 'languages') as $key => $language) { $itemLanguage = array(); $itemLanguage['id'] = $item['id']; $itemLanguage['language'] = $language; $itemLanguage['text'] = $this->frm->getField('text_' . $language)->getValue(); $itemLanguage['opening_hours'] = $this->frm->getField('opening_hours_' . $language)->getValue(); BackendAddressesModel::updateLanguage($itemLanguage); } if (isset($fields["groups"])) { //--Get all the groups $groups = $fields["groups"]->getValue(); BackendAddressesModel::deleteGroupsFromAddress($item['id']); foreach ($groups as $value) { $groupAddress = array(); $groupAddress["address_id"] = $item['id']; $groupAddress["group_id"] = $value; //--Add user to the group BackendAddressesModel::insertAddressToGroup($groupAddress); } } BackendSearchModel::saveIndex($this->getModule(), $item['id'], array('title' => $item['name'], 'text' => $item['name'])); BackendModel::triggerEvent($this->getModule(), 'after_edit', $item); $this->redirect(BackendModel::createURLForAction('index') . '&report=edited&highlight=row-' . $item['id']); } } }
/** * Execute the action * * @return void */ public function execute() { // call parent, this will probably add some general CSS/JS or other required files parent::execute(); // get parameters $page = trim(SpoonFilter::getPostValue('page', null, '')); $identifier = trim(SpoonFilter::getPostValue('identifier', null, '')); // validate if ($page == '' || $identifier == '') { $this->output(self::BAD_REQUEST, null, 'No page provided.'); } // init vars $filename = BACKEND_CACHE_PATH . '/analytics/' . $page . '_' . $identifier . '.txt'; // does the temporary file still exits? $status = SpoonFile::getContent($filename); // no file - create one if ($status === false) { // create file with initial counter SpoonFile::setContent($filename, 'missing1'); // return status $this->output(self::OK, array('status' => false), 'Temporary file was missing. We created one.'); } // busy status if (strpos($status, 'busy') !== false) { // get counter $counter = (int) substr($status, 4) + 1; // file's been busy for more than hundred cycles - just stop here if ($counter > 100) { // remove file SpoonFile::delete($filename); // return status $this->output(self::ERROR, array('status' => 'timeout'), 'Error while retrieving data - the script took too long to retrieve data.'); } // change file content to increase counter SpoonFile::setContent($filename, 'busy' . $counter); // return status $this->output(self::OK, array('status' => 'busy'), 'Data is being retrieved. (' . $counter . ')'); } // unauthorized status if ($status == 'unauthorized') { // remove file SpoonFile::delete($filename); // remove all parameters from the module settings BackendModel::setModuleSetting($this->getModule(), 'session_token', null); BackendModel::setModuleSetting($this->getModule(), 'account_name', null); BackendModel::setModuleSetting($this->getModule(), 'table_id', null); BackendModel::setModuleSetting($this->getModule(), 'profile_title', null); // remove cache files BackendAnalyticsModel::removeCacheFiles(); // clear tables BackendAnalyticsModel::clearTables(); // return status $this->output(self::OK, array('status' => 'unauthorized'), 'No longer authorized.'); } // done status if ($status == 'done') { // remove file SpoonFile::delete($filename); // return status $this->output(self::OK, array('status' => 'done'), 'Data retrieved.'); } // missing status if (strpos($status, 'missing') !== false) { // get counter $counter = (int) substr($status, 7) + 1; // file's been missing for more than ten cycles - just stop here if ($counter > 10) { // remove file SpoonFile::delete($filename); // return status $this->output(self::ERROR, array('status' => 'missing'), 'Error while retrieving data - file was never created.'); } // change file content to increase counter SpoonFile::setContent($filename, 'missing' . $counter); // return status $this->output(self::OK, array('status' => 'busy'), 'Temporary file was still in status missing. (' . $counter . ')'); } /* FALLBACK - SOMETHING WENT WRONG */ // remove file SpoonFile::delete($filename); // return status $this->output(self::ERROR, array('status' => 'error'), 'Error while retrieving data.'); }
/** * Function to store the actual content for either HTML or plain text. * * @param string $content The body of the e-mail you wish to send. * @param array $variables The variables to parse into the content. * @param string[optional] $type The e-mail type. Either 'html' or 'plain'. */ private function processContent($content, $variables, $type = 'html') { // check for type $type = SpoonFilter::getValue($type, array('html', 'plain'), 'html'); // exploded string $exploded = explode('/', str_replace('\\', '/', $content)); $filename = end($exploded); // check if the string provided is a formatted as a file if (SpoonFilter::isFilename($filename) && preg_match('/^[\\S]+\\.\\w{2,3}[\\S]$/', $filename) && !strstr($filename, ' ')) { // check if template exists if (!SpoonFile::exists($content)) { throw new SpoonEmailException('Template not found. (' . $content . ')'); } // store content $this->content[$type] = (string) $this->getTemplateContent($content, $variables); } else { // set the name for the temporary file $tempFile = $this->compileDirectory . '/' . md5(uniqid()) . '.tpl'; // write temp file SpoonFile::setContent($tempFile, $content); // store content $this->content[$type] = (string) $this->getTemplateContent($tempFile, $variables); // delete the temporary SpoonFile::delete($tempFile); } }
/** * Validate the form */ private function validateForm() { // is the form submitted? if ($this->frm->isSubmitted()) { // get the status $status = SpoonFilter::getPostValue('status', array('active', 'draft'), 'active'); // cleanup the submitted fields, ignore fields that were added by hackers $this->frm->cleanupFields(); // validate fields $this->frm->getField('title')->isFilled(BL::err('TitleIsRequired')); $this->frm->getField('text')->isFilled(BL::err('FieldIsRequired')); $this->frm->getField('publish_on_date')->isValid(BL::err('DateIsInvalid')); $this->frm->getField('publish_on_time')->isValid(BL::err('TimeIsInvalid')); $this->frm->getField('category_id')->isFilled(BL::err('FieldIsRequired')); // validate meta $this->meta->validate(); // no errors? if ($this->frm->isCorrect()) { // build item $item['id'] = $this->id; $item['revision_id'] = $this->record['revision_id']; // this is used to let our model know the status (active, archive, draft) of the edited item $item['meta_id'] = $this->meta->save(); $item['category_id'] = (int) $this->frm->getField('category_id')->getValue(); $item['user_id'] = $this->frm->getField('user_id')->getValue(); $item['language'] = BL::getWorkingLanguage(); $item['title'] = $this->frm->getField('title')->getValue(); $item['introduction'] = $this->frm->getField('introduction')->getValue(); $item['text'] = $this->frm->getField('text')->getValue(); $item['publish_on'] = BackendModel::getUTCDate(null, BackendModel::getUTCTimestamp($this->frm->getField('publish_on_date'), $this->frm->getField('publish_on_time'))); $item['edited_on'] = BackendModel::getUTCDate(); $item['hidden'] = $this->frm->getField('hidden')->getValue(); $item['allow_comments'] = $this->frm->getField('allow_comments')->getChecked() ? 'Y' : 'N'; $item['status'] = $status; if ($this->imageIsAllowed) { $item['image'] = $this->record['image']; // the image path $imagePath = FRONTEND_FILES_PATH . '/blog/images'; // if the image should be deleted if ($this->frm->getField('delete_image')->isChecked()) { // delete the image SpoonFile::delete($imagePath . '/source/' . $item['image']); // reset the name $item['image'] = null; } // new image given? if ($this->frm->getField('image')->isFilled()) { // delete the old image SpoonFile::delete($imagePath . '/source/' . $this->record['image']); // build the image name $item['image'] = $this->meta->getURL() . '.' . $this->frm->getField('image')->getExtension(); // upload the image $this->frm->getField('image')->moveFile($imagePath . '/source/' . $item['image']); } elseif ($item['image'] != null) { // get the old file extension $imageExtension = SpoonFile::getExtension($imagePath . '/source/' . $item['image']); // get the new image name $newName = $this->meta->getURL() . '.' . $imageExtension; // only change the name if there is a difference if ($newName != $item['image']) { // move the old file to the new name SpoonFile::move($imagePath . '/source/' . $item['image'], $imagePath . '/source/' . $newName); // assign the new name to the database $item['image'] = $newName; } } } else { $item['image'] = null; } // update the item $item['revision_id'] = BackendBlogModel::update($item); // trigger event BackendModel::triggerEvent($this->getModule(), 'after_edit', array('item' => $item)); // recalculate comment count so the new revision has the correct count BackendBlogModel::reCalculateCommentCount(array($this->id)); // save the tags BackendTagsModel::saveTags($item['id'], $this->frm->getField('tags')->getValue(), $this->URL->getModule()); // active if ($item['status'] == 'active') { // edit search index BackendSearchModel::saveIndex($this->getModule(), $item['id'], array('title' => $item['title'], 'text' => $item['text'])); // ping if (BackendModel::getModuleSetting($this->URL->getModule(), 'ping_services', false)) { BackendModel::ping(SITE_URL . BackendModel::getURLForBlock($this->URL->getModule(), 'detail') . '/' . $this->meta->getURL()); } // build URL $redirectUrl = BackendModel::createURLForAction('index') . '&report=edited&var=' . urlencode($item['title']) . '&id=' . $this->id . '&highlight=row-' . $item['revision_id']; } elseif ($item['status'] == 'draft') { // everything is saved, so redirect to the edit action $redirectUrl = BackendModel::createURLForAction('edit') . '&report=saved-as-draft&var=' . urlencode($item['title']) . '&id=' . $item['id'] . '&draft=' . $item['revision_id'] . '&highlight=row-' . $item['revision_id']; } // append to redirect URL if ($this->categoryId != null) { $redirectUrl .= '&category=' . $this->categoryId; } // everything is saved, so redirect to the overview $this->redirect($redirectUrl); } } }
/** * Start processing the hooks */ public static function startProcessingHooks() { // is the queue already running? if (SpoonFile::exists(FRONTEND_CACHE_PATH . '/hooks/pid')) { // get the pid $pid = trim(SpoonFile::getContent(FRONTEND_CACHE_PATH . '/hooks/pid')); // running on windows? if (strtolower(substr(php_uname('s'), 0, 3)) == 'win') { // get output $output = @shell_exec('tasklist.exe /FO LIST /FI "PID eq ' . $pid . '"'); // validate output if ($output == '' || $output === false) { // delete the pid file SpoonFile::delete(FRONTEND_CACHE_PATH . '/hooks/pid'); } else { return true; } } elseif (strtolower(substr(php_uname('s'), 0, 6)) == 'darwin') { // get output $output = @posix_getsid($pid); // validate output if ($output === false) { // delete the pid file SpoonFile::delete(FRONTEND_CACHE_PATH . '/hooks/pid'); } else { return true; } } else { // check if the process is still running, by checking the proc folder if (!SpoonFile::exists('/proc/' . $pid)) { // delete the pid file SpoonFile::delete(FRONTEND_CACHE_PATH . '/hooks/pid'); } else { return true; } } } // init var $parts = parse_url(SITE_URL); $errNo = ''; $errStr = ''; $defaultPort = 80; if ($parts['scheme'] == 'https') { $defaultPort = 433; } // open the socket $socket = fsockopen($parts['host'], isset($parts['port']) ? $parts['port'] : $defaultPort, $errNo, $errStr, 1); // build the request $request = 'GET /backend/cronjob.php?module=core&action=process_queued_hooks HTTP/1.1' . "\r\n"; $request .= 'Host: ' . $parts['host'] . "\r\n"; $request .= 'Content-Length: 0' . "\r\n\r\n"; $request .= 'Connection: Close' . "\r\n\r\n"; // send the request fwrite($socket, $request); // close the socket fclose($socket); // return return true; }
/** * Validate the form */ private function validateForm() { // is the form submitted? if ($this->frm->isSubmitted()) { // cleanup the submitted fields, ignore fields that were added by hackers $this->frm->cleanupFields(); // XML provided? if ($this->frm->getField('blogger')->isFilled()) { $this->frm->getField('blogger')->isAllowedExtension(array('xml'), BL::err('XMLFilesOnly')); } else { $this->frm->getField('blogger')->addError(BL::err('FieldIsRequired')); } // no errors? if ($this->frm->isCorrect()) { // move the file $this->frm->getField('blogger')->moveFile(FRONTEND_FILES_PATH . '/blogger.xml'); // process the XML $this->processXML(); // recalculate the comments BackendBlogModel::reCalculateCommentCount($this->newIds); // remove the file SpoonFile::delete(FRONTEND_FILES_PATH . '/blogger.xml'); // everything is saved, so redirect to the overview $this->redirect(BackendModel::createURLForAction('index') . '&report=imported'); } } }
/** * Execute the action * * @return void */ public function execute() { // no timelimit set_time_limit(0); // get database $db = BackendModel::getDB(true); // create log $log = new SpoonLog('custom', BACKEND_CACHE_PATH . '/logs/events'); // get process-id $pid = getmypid(); // store PID SpoonFile::setContent(BACKEND_CACHE_PATH . '/hooks/pid', $pid); // loop forever while (true) { // get 1 item $item = $db->getRecord('SELECT * FROM hooks_queue WHERE status = ? LIMIT 1', array('queued')); // any item? if (!empty($item)) { // init var $processedSuccesfully = true; // set item as busy $db->update('hooks_queue', array('status' => 'busy'), 'id = ?', array($item['id'])); // unserialize data $item['callback'] = unserialize($item['callback']); $item['data'] = unserialize($item['data']); // check if the item is callable if (!is_callable($item['callback'])) { // in debug mode we want to know if there are errors if (SPOON_DEBUG) { throw new BackendException('Invalid callback.'); } // set to error state $db->update('hooks_queue', array('status' => 'error'), 'id = ?', $item['id']); // reset state $processedSuccesfully = false; // logging when we are in debugmode if (SPOON_DEBUG) { $log->write('Callback (' . serialize($item['callback']) . ') failed.'); } } try { // logging when we are in debugmode if (SPOON_DEBUG) { $log->write('Callback (' . serialize($item['callback']) . ') called.'); } // call the callback $return = call_user_func($item['callback'], $item['data']); // failed? if ($return === false) { // set to error state $db->update('hooks_queue', array('status' => 'error'), 'id = ?', $item['id']); // reset state $processedSuccesfully = false; // logging when we are in debugmode if (SPOON_DEBUG) { $log->write('Callback (' . serialize($item['callback']) . ') failed.'); } } } catch (Exception $e) { // set to error state $db->update('hooks_queue', array('status' => 'error'), 'id = ?', $item['id']); // reset state $processedSuccesfully = false; // logging when we are in debugmode if (SPOON_DEBUG) { $log->write('Callback (' . serialize($item['callback']) . ') failed.'); } } // everything went fine so delete the item if ($processedSuccesfully) { $db->delete('hooks_queue', 'id = ?', $item['id']); } // logging when we are in debugmode if (SPOON_DEBUG) { $log->write('Callback (' . serialize($item['callback']) . ') finished.'); } } else { // remove the file SpoonFile::delete(BACKEND_CACHE_PATH . '/hooks/pid'); // stop the script exit; } } }
/** * Converts a CSV-formatted string to an array * * @return array * @param string $string The string you wish to convert to an array. * @param array[optional] $columns The column names you want to use. * @param array[optional] $excludeColumns The columns to exclude. * @param string[optional] $delimiter The field delimiter of the CSV. * @param string[optional] $enclosure The enclosure character of the CSV. */ public static function stringToArray($string, array $columns = array(), array $excludeColumns = null, $delimiter = ',', $enclosure = '"') { // reset variables $string = (string) $string; $filename = dirname(__FILE__) . '/' . uniqid(); // save a tempfile SpoonFile::setContent($filename, $string); // return the file to array $array = self::fileToArray($filename, $columns, $excludeColumns, $delimiter, $enclosure); // remove the created file SpoonFile::delete($filename); // return the array return $array; }
/** * Check if a directory is writable. * The default is_writable function has problems due to Windows ACLs "bug" * * @param string $path The path to check. * @return bool */ public static function isWritable($path) { // redefine argument $path = rtrim((string) $path, '/'); // create random file $file = uniqid() . '.tmp'; $return = @file_put_contents($path . '/' . $file, 'temporary file', FILE_APPEND); if ($return === false) { return false; } // unlink the random file SpoonFile::delete($path . '/' . $file); return true; }
/** * Clear/removed the busy file * * @return void */ protected function clearBusyFile() { // build path $path = BACKEND_CACHE_PATH . '/cronjobs/' . $this->getId() . '.busy'; // remove the file SpoonFile::delete($path); }
/** * Deletes a directory and all of its subdirectories. * * @return bool True if the directory was deleted, false if not. * @param string $directory Full path to the directory. */ public static function delete($directory) { // redfine directory $directory = (string) $directory; // directory exists if (self::exists($directory)) { // get the list $list = self::getList($directory, true); // has subdirectories/files if (count($list) != 0) { // loop directories and execute function foreach ((array) $list as $item) { // delete directory recursive if (is_dir($directory . '/' . $item)) { self::delete($directory . '/' . $item); } else { SpoonFile::delete($directory . '/' . $item); } } } // has no content @rmdir($directory); } else { return false; } }
/** * Delete the cached data */ private function deleteCachedData() { // init some vars $foldersToLoop = array('/backend/cache', '/frontend/cache'); $foldersToIgnore = array('/backend/cache/navigation'); $filesToIgnore = array('.gitignore'); $filesToDelete = array(); // loop folders foreach ($foldersToLoop as $folder) { // get folderlisting $subfolders = (array) SpoonDirectory::getList(PATH_WWW . $folder, false, array('.svn', '.gitignore')); // loop folders foreach ($subfolders as $subfolder) { // not in ignore list? if (!in_array($folder . '/' . $subfolder, $foldersToIgnore)) { // get the filelisting $files = (array) SpoonFile::getList(PATH_WWW . $folder . '/' . $subfolder); // loop the files foreach ($files as $file) { if (!in_array($file, $filesToIgnore)) { $filesToDelete[] = PATH_WWW . $folder . '/' . $subfolder . '/' . $file; } } } } } // delete cached files if (!empty($filesToDelete)) { // loop files and delete them foreach ($filesToDelete as $file) { SpoonFile::delete($file); } } }
/** * Validate the form * * @return void */ private function validateForm() { // is the form submitted? if ($this->frm->isSubmitted()) { // cleanup the submitted fields, ignore fields that were added by hackers $this->frm->cleanupFields(); // XML provided? if ($this->frm->getField('blogger')->isFilled()) { $this->frm->getField('blogger')->isAllowedExtension(array('xml'), BL::err('XMLFilesOnly')); } else { $this->frm->getField('blogger')->addError(BL::err('FieldIsRequired')); } // no errors? if ($this->frm->isCorrect()) { // move the file $this->frm->getField('blogger')->moveFile(FRONTEND_FILES_PATH . '/blogger.xml'); // init object $reader = new XMLReader(); // open the file $reader->open(FRONTEND_FILES_PATH . '/blogger.xml'); // start reading $reader->read(); // loop through the document while (true) { // start tag for entry? if ($reader->name == 'entry') { // end tag? if ($reader->nodeType == XMLReader::END_ELEMENT) { $reader->next(); } // get the raw XML $xmlString = $reader->readOuterXml(); // is it really an entry? if (substr($xmlString, 0, 6) == '<entry') { // read the XML as an SimpleXML-object $xml = @simplexml_load_string($reader->readOuterXml()); // validate if ($xml !== false) { // loop the categories foreach ($xml->category as $category) { // post if ($category['term'] == 'http://schemas.google.com/blogger/2008/kind#post') { // process the post $this->processXMLAsPost($xml); // stop looping break; } // comment if ($category['term'] == 'http://schemas.google.com/blogger/2008/kind#comment') { // process the post $this->processXMLAsComment($xml); // stop looping break; } } } } } // end if (!$reader->read()) { break; } } // close $reader->close(); // recalculate the comments BackendBlogModel::reCalculateCommentCount($this->newIds); // remove the file SpoonFile::delete(FRONTEND_FILES_PATH . '/blogger.xml'); // everything is saved, so redirect to the overview $this->redirect(BackendModel::createURLForAction('index') . '&report=imported'); } } }
/** * Clear the entire compiled directory or a specific template. * * @param string[optional] $template The filename of a specific template to mark for recompiling. */ public function clearCompiled($template = null) { // specific template if ($template !== null) { SpoonFile::delete($this->compileDirectory . '/' . $this->getCompileName($template)); } else { // list of *.tpl.php files from compileDirectory $files = SpoonFile::getList($this->compileDirectory, '|.*\\.tpl\\.php|'); // delete foreach ($files as $file) { SpoonFile::delete($this->compileDirectory . '/' . $file); } } }
/** * Clear all applications cache. * * Note: we do not need to rebuild anything, the core will do this when noticing the cache files are missing. */ public static function clearCache() { // list of cache files to be deleted $filesToDelete = array(); // backend navigation $filesToDelete[] = BACKEND_CACHE_PATH . '/navigation/navigation.php'; // backend locale foreach (SpoonFile::getList(BACKEND_CACHE_PATH . '/locale', '/\\.php$/') as $file) { $filesToDelete[] = BACKEND_CACHE_PATH . '/locale/' . $file; } // frontend navigation foreach (SpoonFile::getList(FRONTEND_CACHE_PATH . '/navigation', '/\\.(php|js)$/') as $file) { $filesToDelete[] = FRONTEND_CACHE_PATH . '/navigation/' . $file; } // frontend locale foreach (SpoonFile::getList(FRONTEND_CACHE_PATH . '/locale', '/\\.php$/') as $file) { $filesToDelete[] = FRONTEND_CACHE_PATH . '/locale/' . $file; } // delete the files foreach ($filesToDelete as $file) { SpoonFile::delete($file); } }