970 lines
41 KiB
PHP
970 lines
41 KiB
PHP
|
<?php
|
|||
|
namespace Grav\Plugin;
|
|||
|
|
|||
|
use Grav\Common\Filesystem\Folder;
|
|||
|
use Grav\Common\Form\FormFlash;
|
|||
|
use Grav\Common\Grav;
|
|||
|
use Grav\Common\Page\Page;
|
|||
|
use Grav\Common\Plugin;
|
|||
|
use RocketTheme\Toolbox\Event\Event;
|
|||
|
use RocketTheme\Toolbox\File\File;
|
|||
|
use RocketTheme\Toolbox\File\YamlFile;
|
|||
|
use Symfony\Component\Yaml\Yaml;
|
|||
|
|
|||
|
/**
|
|||
|
* Class AddPageByFormPlugin
|
|||
|
* @package Grav\Plugin
|
|||
|
*/
|
|||
|
class AddPageByFormPlugin extends Plugin
|
|||
|
{
|
|||
|
|
|||
|
private $new_page_route = '';
|
|||
|
private $move_self_files = false;
|
|||
|
private $say_my_name = ['addpage', 'add_page'];
|
|||
|
private $uploads = array();
|
|||
|
private $page_frontmatter = array();
|
|||
|
|
|||
|
protected $post = array();
|
|||
|
|
|||
|
/**
|
|||
|
* Extends a path
|
|||
|
*
|
|||
|
* @param string $path
|
|||
|
* @param string $page
|
|||
|
* @param string $slug
|
|||
|
*
|
|||
|
* @return string $path
|
|||
|
*/
|
|||
|
public function buildPath($path, $page, $slug)
|
|||
|
{
|
|||
|
if (!is_null($page)) {
|
|||
|
return $path . DS . $page->folder();
|
|||
|
} else {
|
|||
|
return $path . DS . $slug;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Extends a route
|
|||
|
*
|
|||
|
* @param string $route
|
|||
|
* @param string $page
|
|||
|
* @param string $slug
|
|||
|
*
|
|||
|
* @return string $route
|
|||
|
*/
|
|||
|
public function buildRoute($route, $page, $slug)
|
|||
|
{
|
|||
|
if (!is_null($page)) {
|
|||
|
return $route . DS . $page->slug();
|
|||
|
} else {
|
|||
|
return $route . DS . $slug;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Copy uploaded files and return list of properties
|
|||
|
*
|
|||
|
* @param string $form
|
|||
|
* @param string $new_page
|
|||
|
*
|
|||
|
* @return array $file_fields
|
|||
|
*/
|
|||
|
public function copyFiles($form, $new_page)
|
|||
|
{
|
|||
|
// Copy uploaded files to the new page folder
|
|||
|
// and prepare uploaded file properties
|
|||
|
|
|||
|
$new_page_path = $new_page->relativePagePath();
|
|||
|
|
|||
|
$file_fields = array();
|
|||
|
$deleted_files = array();
|
|||
|
|
|||
|
$flash = $form->getFlash();
|
|||
|
$fields = $flash->getFilesByFields();
|
|||
|
|
|||
|
foreach ($fields as $file_field => $uploads) {
|
|||
|
$file_fields[$file_field] = array();
|
|||
|
foreach ($uploads as $file_name => $upload) {
|
|||
|
|
|||
|
// When upload field has been emptied $upload == null
|
|||
|
if ($upload) {
|
|||
|
$file_name = $upload->getClientFilename();
|
|||
|
$destination = $upload->getDestination();
|
|||
|
|
|||
|
// Do the actual file copy unless destination is in tmp/forms
|
|||
|
if (strpos($destination, 'tmp/forms') === false) {
|
|||
|
$full_name = $new_page_path . DS . $file_name;
|
|||
|
$upload->moveTo($full_name);
|
|||
|
} else {
|
|||
|
$full_name = $destination;
|
|||
|
// Leave the copy to the Form plugin
|
|||
|
}
|
|||
|
|
|||
|
$file_fields[$file_field][$file_name] = [
|
|||
|
'name' => $file_name,
|
|||
|
'type' => $upload->getClientMediaType(),
|
|||
|
'size' => $upload->getSize(),
|
|||
|
'path' => $full_name
|
|||
|
];
|
|||
|
}
|
|||
|
else {
|
|||
|
// Uploaded file was removed by the user
|
|||
|
$to_be_deleted = $new_page_path . DS . $file_name;
|
|||
|
array_push($deleted_files, array('file_field' => $file_field, 'file_path' => $to_be_deleted));
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return array('uploaded' => $file_fields, 'deleted' => $deleted_files);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
*
|
|||
|
* @param string $route
|
|||
|
* @param boolean $create
|
|||
|
*
|
|||
|
* @return array|null [route, path]
|
|||
|
*
|
|||
|
* Crawl a route, using modular page folder names as a fallback and
|
|||
|
* optionally creating non existing folders along the way
|
|||
|
*/
|
|||
|
public function crawlRoute($route, $create = false)
|
|||
|
{
|
|||
|
$path = USER_DIR . 'pages';
|
|||
|
if (!is_null($route) && isset($route)) {
|
|||
|
if ($route != DS) {
|
|||
|
$slugs = explode(DS, $route);
|
|||
|
$route = '';
|
|||
|
for ($i = 1; $i < count($slugs); $i++) {
|
|||
|
$page = $this->pageExists($route, $slugs[$i]);
|
|||
|
if (is_null($page)) {
|
|||
|
if ($create) {
|
|||
|
// Create folder if it doesn't exist
|
|||
|
$slugs[$i] = $this->createFolder($path . DS . $slugs[$i]);
|
|||
|
} else {
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
$route = $this->buildRoute($route, $page, $slugs[$i]);
|
|||
|
$path = $this->buildPath($path, $page, $slugs[$i]);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return ['route' => $route, 'path' => $path];
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Create a folder if it does not exist
|
|||
|
*
|
|||
|
* @param string $path
|
|||
|
*
|
|||
|
* @return string $folder_name
|
|||
|
*/
|
|||
|
public function createFolder($path)
|
|||
|
{
|
|||
|
$folder_names = explode(DS, $path);
|
|||
|
$folder_name = $folder_names[count($folder_names) - 1];
|
|||
|
if (!file_exists($path)) {
|
|||
|
// split and sanitize leaf as a slug (disallowing periods)
|
|||
|
$folder_name = $this->sanitize($folder_name, 'slug');
|
|||
|
unset($folder_names[count($folder_names) - 1]);
|
|||
|
$path = implode(DS, $folder_names);
|
|||
|
Folder::create($path . DS . $folder_name);
|
|||
|
}
|
|||
|
return $folder_name;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Custom filter_var
|
|||
|
*
|
|||
|
* Based upon: https://gist.github.com/chtombleson/10002134
|
|||
|
* Get a value from $_POST and sanitize it
|
|||
|
*
|
|||
|
* @param string $var Variable to check
|
|||
|
* @param string $type What type is the value (string, email, int, float, encoded, url, email)
|
|||
|
* @param array $option Options for filter_var
|
|||
|
* @return mixed will return false on failure
|
|||
|
*/
|
|||
|
public function filter_post_var($var, $type = 'string', $options = array()) {
|
|||
|
if (!isset($this->post[$var])) {
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
return filter_var($this->post[$var], $this->get_filter($type), $options);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Source: https://gist.github.com/chtombleson/10002134
|
|||
|
*
|
|||
|
*/
|
|||
|
private function get_filter($type) {
|
|||
|
switch (strtolower($type)) {
|
|||
|
case 'string':
|
|||
|
$filter = FILTER_SANITIZE_STRING;
|
|||
|
break;
|
|||
|
|
|||
|
case 'int':
|
|||
|
$filter = FILTER_SANITIZE_NUMBER_INT;
|
|||
|
break;
|
|||
|
|
|||
|
case 'float' || 'decimal':
|
|||
|
$filter = FILTER_SANITIZE_NUMBER_FLOAT;
|
|||
|
break;
|
|||
|
|
|||
|
case 'encoded':
|
|||
|
$filter = FILTER_SANITIZE_ENCODED;
|
|||
|
break;
|
|||
|
|
|||
|
case 'url':
|
|||
|
$filter = FILTER_SANITIZE_URL;
|
|||
|
break;
|
|||
|
|
|||
|
case 'email':
|
|||
|
$filter = FILTER_SANITIZE_EMAIL;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
$filter = FILTER_SANITIZE_STRING;
|
|||
|
}
|
|||
|
|
|||
|
return $filter;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Get parent page
|
|||
|
*
|
|||
|
* @param string $parent
|
|||
|
* @param object $page
|
|||
|
*
|
|||
|
* @return object $parent_page
|
|||
|
*/
|
|||
|
public function getParentPage($parent, $page)
|
|||
|
{
|
|||
|
if ($parent != '') {
|
|||
|
// Check for a relative parent
|
|||
|
if ($parent[0] != DS) {
|
|||
|
// Make an absolute route starting from this form page
|
|||
|
$parent = $page->route() . DS . $parent;
|
|||
|
}
|
|||
|
|
|||
|
// Check whether the parent page exists, allowing for modular pages
|
|||
|
// but disallowing empty folders in the route
|
|||
|
if (!is_null($this->crawlRoute($parent, false))) {
|
|||
|
$parent_page = $this->grav['page']->find($parent);
|
|||
|
} else {
|
|||
|
// Gracefully continue and falback to adding the
|
|||
|
// new page as a child page of the form page
|
|||
|
$parent_page = $page;
|
|||
|
}
|
|||
|
} else {
|
|||
|
$parent_page = $page;
|
|||
|
}
|
|||
|
|
|||
|
return $parent_page; // can be a page or null if there is no page.
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @return array
|
|||
|
*
|
|||
|
* The getSubscribedEvents() gives the core a list of events
|
|||
|
* that the plugin wants to listen to. The key of each
|
|||
|
* array section is the event that the plugin listens to
|
|||
|
* and the value (in the form of an array) contains the
|
|||
|
* callable (or function) as well as the priority. The
|
|||
|
* higher the number the higher the priority.
|
|||
|
*/
|
|||
|
public static function getSubscribedEvents()
|
|||
|
{
|
|||
|
|
|||
|
return [
|
|||
|
'onPluginsInitialized' => ['onPluginsInitialized', 0],
|
|||
|
'onFormProcessed' => ['onFormProcessed', 0]
|
|||
|
];
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @return string
|
|||
|
*
|
|||
|
* Return the state of a tristate configuration variable
|
|||
|
*
|
|||
|
*/
|
|||
|
public function getTriStateConfig($value, $state) {
|
|||
|
|
|||
|
if(!isset($value)) {
|
|||
|
return '';
|
|||
|
}
|
|||
|
|
|||
|
if($value === $state) {
|
|||
|
return $state;
|
|||
|
}
|
|||
|
|
|||
|
if(gettype($value) === "boolean") {
|
|||
|
if($value) {
|
|||
|
return "true";
|
|||
|
}
|
|||
|
else {
|
|||
|
return "false";
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if(gettype($value) === "string") {
|
|||
|
$value = trim(strtolower($value));
|
|||
|
};
|
|||
|
|
|||
|
if(in_array($value, [1, '1', 'on', 'true'], true)) {
|
|||
|
return 'true';
|
|||
|
}
|
|||
|
else {
|
|||
|
return 'false';
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Handle form action
|
|||
|
*
|
|||
|
* @param $event
|
|||
|
*
|
|||
|
*/
|
|||
|
public function onFormProcessed(Event $event)
|
|||
|
{
|
|||
|
$form = $event['form'];
|
|||
|
$action = $event['action'];
|
|||
|
$params = $event['params'];
|
|||
|
|
|||
|
switch (true) {
|
|||
|
case (in_array($action, $this->say_my_name)):
|
|||
|
|
|||
|
if (isset($_POST)) {
|
|||
|
|
|||
|
$pages = $this->grav['pages'];
|
|||
|
$page = $this->grav['page'];
|
|||
|
$header = $page->header();
|
|||
|
$form_page_relative_page_path = $page->relativePagePath();
|
|||
|
|
|||
|
// Get default settings form plugin config
|
|||
|
$include_username = $this->config->get('plugins.add-page-by-form.include_username');
|
|||
|
|
|||
|
$overwrite_mode = $this->getTriStateConfig($this->config->get('plugins.add-page-by-form.overwrite_mode'), 'edit');
|
|||
|
|
|||
|
$date_format = $this->config->get('plugins.add-page-by-form.date_display_format');
|
|||
|
$auto_taxonomy_types = $this->config->get('plugins.add-page-by-form.auto_taxonomy_types');
|
|||
|
$physical_template_name = $this->config->get('plugins.add-page-by-form.physical_template_name');
|
|||
|
|
|||
|
$slug_field = '';
|
|||
|
|
|||
|
// For next plugin version
|
|||
|
$include_timestamp = false;
|
|||
|
|
|||
|
// Initialize default subroute
|
|||
|
$sub_route = '';
|
|||
|
|
|||
|
// Get settings from pageconfig block and override values via form fields
|
|||
|
if (isset($header->pageconfig) && is_array($header->pageconfig)) {
|
|||
|
|
|||
|
$positives = ['1', 'on', 'true'];
|
|||
|
$pageconfig = $header->pageconfig;
|
|||
|
|
|||
|
if (isset($pageconfig['parent'])) {
|
|||
|
$parent = strtolower(trim($pageconfig['parent']));
|
|||
|
}
|
|||
|
if (isset($pageconfig['subroute'])) {
|
|||
|
$sub_route = strtolower(trim($pageconfig['subroute']));
|
|||
|
}
|
|||
|
if (isset($pageconfig['include_username'])) {
|
|||
|
$include_username = in_array(strtolower(trim($pageconfig['include_username'])), $positives);
|
|||
|
}
|
|||
|
if (isset($pageconfig['overwrite_mode'])) {
|
|||
|
$overwrite_mode = $this->getTriStateConfig($pageconfig['overwrite_mode'], 'edit');
|
|||
|
}
|
|||
|
if (isset($pageconfig['slug_field'])) {
|
|||
|
$slug_field = strtolower(trim($pageconfig['slug_field']));
|
|||
|
}
|
|||
|
if (isset($pageconfig['physical_template_name'])) {
|
|||
|
$physical_template_name = in_array(strtolower(trim($pageconfig['physical_template_name'])), $positives);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Assemble the new page frontmatter from the page_frontmatter block
|
|||
|
// as set in the form page
|
|||
|
if (isset($header->pagefrontmatter) && is_array($header->pagefrontmatter)) {
|
|||
|
$page_frontmatter = $header->pagefrontmatter;
|
|||
|
} else {
|
|||
|
$page_frontmatter = array();
|
|||
|
}
|
|||
|
|
|||
|
// Add username (or not)
|
|||
|
if ($include_username) {
|
|||
|
$username = null;
|
|||
|
if (!is_null($this->grav['session']->user)) {
|
|||
|
$username = $this->grav['session']->user->username;
|
|||
|
}
|
|||
|
if (is_null($username)) {
|
|||
|
$username = '';
|
|||
|
}
|
|||
|
$page_frontmatter['username'] = $username;
|
|||
|
}
|
|||
|
|
|||
|
// Get all form field values
|
|||
|
$form_data = $form->value()->toArray();
|
|||
|
|
|||
|
if (isset($form_data)) {
|
|||
|
|
|||
|
// Append taxonomy
|
|||
|
if (isset($form_data['taxonomy']) && is_array($form_data['taxonomy'])) {
|
|||
|
// Convert comma separated list into array assuming double quoted items
|
|||
|
foreach ($form_data['taxonomy'] as $key => $value) {
|
|||
|
$values = str_getcsv($value, ',', '"');
|
|||
|
foreach ($values as $k => $v) {
|
|||
|
$values[$k] = trim($v);
|
|||
|
}
|
|||
|
$form_data['taxonomy'][$key] = $values;
|
|||
|
}
|
|||
|
if (isset($page_frontmatter['taxonomy'])) {
|
|||
|
// Append type/values
|
|||
|
$page_frontmatter['taxonomy'] = array_merge_recursive($page_frontmatter['taxonomy'], $form_data['taxonomy']);
|
|||
|
// Remove duplicate values
|
|||
|
foreach ($page_frontmatter['taxonomy'] as $key => $value) {
|
|||
|
if (is_array($page_frontmatter['taxonomy'][$key])) {
|
|||
|
$page_frontmatter['taxonomy'][$key] = array_keys(array_flip($page_frontmatter['taxonomy'][$key]));
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
// Add taxonomy, types and values
|
|||
|
$page_frontmatter['taxonomy'] = $form_data['taxonomy'];
|
|||
|
}
|
|||
|
// Remove taxonomy from form data (to prevent merging raw data)
|
|||
|
unset($form_data['taxonomy']);
|
|||
|
}
|
|||
|
|
|||
|
// Merge variables from pagefrontmatter block and form fields;
|
|||
|
// Values that have been through a Twig Processor are in the
|
|||
|
// page_frontmatter and take precedence over the form values
|
|||
|
$page_frontmatter = array_merge($page_frontmatter, $form_data);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
// Here you can insert anything else into the new page frontmatter
|
|||
|
|
|||
|
/*
|
|||
|
$result = 'Hello World';
|
|||
|
$page_frontmatter['result'] = $result;
|
|||
|
*/
|
|||
|
|
|||
|
// If content is not included as a form value then fallback to config default
|
|||
|
if (isset($page_frontmatter['content'])) {
|
|||
|
$content = $page_frontmatter['content'];
|
|||
|
} else {
|
|||
|
$content = $this->config->get('plugins.add-page-by-form.default_content');
|
|||
|
}
|
|||
|
|
|||
|
if ($physical_template_name && isset($page_frontmatter['template'])) {
|
|||
|
$page_template = $page_frontmatter['template'];
|
|||
|
// Remove the frontmatter variable template as which template
|
|||
|
// must be used will be determined by the new page filename
|
|||
|
unset($page_frontmatter['template']);
|
|||
|
} else {
|
|||
|
$page_template = 'default';
|
|||
|
}
|
|||
|
|
|||
|
// Remove unwanted items from new page frontmatter
|
|||
|
unset($page_frontmatter['_json']);
|
|||
|
unset($page_frontmatter['content']);
|
|||
|
unset($page_frontmatter['parent']);
|
|||
|
unset($page_frontmatter['subroute']);
|
|||
|
|
|||
|
// Initialize default page parent
|
|||
|
if (isset($header->parent)) {
|
|||
|
// For backwards compatibility
|
|||
|
$parent = $header->parent;
|
|||
|
} else {
|
|||
|
$parent = '';
|
|||
|
}
|
|||
|
|
|||
|
// Override parent if set in pageconfig block
|
|||
|
if (isset($pageconfig['parent'])) {
|
|||
|
$parent = strtolower(trim($pageconfig['parent']));
|
|||
|
}
|
|||
|
// Override parent if set in the form
|
|||
|
if (isset($form_data['parent'])) {
|
|||
|
$parent = strtolower(trim($form_data['parent']));
|
|||
|
}
|
|||
|
|
|||
|
// Removes multiple concatenated slashes plus a trailing slash if present
|
|||
|
if (strlen($parent) > 1) {
|
|||
|
$parent = preg_replace('/[\/]+/', DS, $parent);
|
|||
|
$parent = rtrim($parent, DS);
|
|||
|
}
|
|||
|
|
|||
|
// Get the "parent to be"
|
|||
|
$parent_page = $this->getParentPage($parent, $page);
|
|||
|
|
|||
|
$parent_page_path = $parent_page->path();
|
|||
|
$parent_page_route = $parent_page->route();
|
|||
|
|
|||
|
if ($overwrite_mode === 'edit') {
|
|||
|
// Get slug of exisiting page
|
|||
|
// Normal method
|
|||
|
if(isset($form_data['edit_path'])) {
|
|||
|
$slug = basename(dirname($form_data['edit_path']));
|
|||
|
}
|
|||
|
else {
|
|||
|
// Alternative method
|
|||
|
if(isset($form_data['file_path'])) {
|
|||
|
$slug = basename(dirname($form_data['file_path']));
|
|||
|
}
|
|||
|
else {
|
|||
|
$slug = '';
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else {
|
|||
|
// Create the slug for the new page
|
|||
|
// Override subroute
|
|||
|
if (isset($form_data['subroute'])) {
|
|||
|
$sub_route = mb_strtolower(trim($form_data['subroute']));
|
|||
|
}
|
|||
|
if ($sub_route != '') {
|
|||
|
// Remove any multiple concatenated slashes
|
|||
|
$sub_route = preg_replace('/[\/]+/', DS, $sub_route);
|
|||
|
// Remove preceding and trailing slashes if present
|
|||
|
$sub_route = trim($sub_route, DS);
|
|||
|
// Prepare for crawling
|
|||
|
$parent_page_route = $parent_page->route() . DS . $sub_route;
|
|||
|
// Create subroute path if it doesn't exist
|
|||
|
$parent_destination = $this->crawlRoute($parent_page_route, true);
|
|||
|
$parent_page_route = $parent_destination['route'];
|
|||
|
$parent_page_path = $parent_destination['path'];
|
|||
|
}
|
|||
|
// Create a slug to be used as the page name (used publicly in URLs etc.)
|
|||
|
if ($slug_field != '') {
|
|||
|
if (isset($page_frontmatter[$slug_field])) {
|
|||
|
$slug = self::slug($page_frontmatter[$slug_field]);
|
|||
|
}
|
|||
|
}
|
|||
|
if (!isset($slug)) {
|
|||
|
if (isset($page_frontmatter['title'])) {
|
|||
|
$slug = self::slug($page_frontmatter['title']);
|
|||
|
} else {
|
|||
|
$slug = $this->config->get('plugins.add-page-by-form.default_title');
|
|||
|
$slug = self::slug($slug);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (empty($slug)) {
|
|||
|
$this->grav->fireEvent('onFormValidationError', new Event([
|
|||
|
'form' => $form,
|
|||
|
'message' => '<strong>ERROR</strong> in Add Page by Form Plugin: Variable \'slug\' is empty']));
|
|||
|
$event->stopPropagation();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
$new_page_folder = $parent_page_path . DS . $slug;
|
|||
|
|
|||
|
// Check overwrite mode
|
|||
|
// When overwrite mode == 'true' replace page including media
|
|||
|
// When overwrite mode == 'false' create a sequential named page
|
|||
|
// When overwrite mode == 'edit' edit page and page media
|
|||
|
if ($overwrite_mode !== 'false') {
|
|||
|
if (file_exists($new_page_folder)) {
|
|||
|
if ($overwrite_mode === 'edit') {
|
|||
|
$original_frontmatter = (array)$pages->get($new_page_folder)->header();
|
|||
|
}
|
|||
|
else {
|
|||
|
Folder::delete($new_page_folder);
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
// Scan for the next available sequential suffix
|
|||
|
$version = 0;
|
|||
|
// Keep incrementing the page slug suffix to keep earlier versions / duplicates
|
|||
|
while (file_exists($new_page_folder)) {
|
|||
|
$version += 1;
|
|||
|
$new_page_folder = $parent_page_path . DS . $slug . '-' . $version;
|
|||
|
}
|
|||
|
if ($version > 0) {
|
|||
|
$slug = $slug . '-' . $version;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Create and add the page to Grav
|
|||
|
try {
|
|||
|
|
|||
|
// Create new page
|
|||
|
$new_page = new Page;
|
|||
|
|
|||
|
// Get active or default language for page filename
|
|||
|
// (e.g. 'nl' -> 'default.nl.md')
|
|||
|
$language = Grav::instance()['language']->getLanguage() ?: null;
|
|||
|
|
|||
|
$extension = '.md';
|
|||
|
|
|||
|
if ($language != '') {
|
|||
|
$new_page_name = $page_template . '.' . $language . $extension;
|
|||
|
} else {
|
|||
|
$new_page_name = $page_template . $extension;
|
|||
|
}
|
|||
|
|
|||
|
$path = $parent_page_path . DS . $slug . DS . $new_page_name;
|
|||
|
$route = $parent_page_route . DS . $slug;
|
|||
|
|
|||
|
// Set page location vars
|
|||
|
$new_page->name($new_page_name);
|
|||
|
$new_page->folder($slug);
|
|||
|
//$new_page->path($path);
|
|||
|
$new_page->extension($extension);
|
|||
|
$new_page->parent($parent_page);
|
|||
|
$new_page->filePath($path);
|
|||
|
$new_page->routable(true);
|
|||
|
|
|||
|
// Add frontmatter vars
|
|||
|
$new_page->header((object) $page_frontmatter);
|
|||
|
$new_page->frontmatter(Yaml::dump((array)$new_page->header(), 20));
|
|||
|
|
|||
|
// Set page markdown content vars
|
|||
|
$new_page->rawMarkdown((string) $content);
|
|||
|
$new_page->file()->markdown($new_page->rawMarkdown());
|
|||
|
|
|||
|
// Add routing information
|
|||
|
$pages->addPage($new_page, $this->new_page_route);
|
|||
|
|
|||
|
|
|||
|
// Fire BeforePageSave event
|
|||
|
$this->grav->fireEvent('onAddPageByFormPluginBeforePageSave', new Event(['page' => &$new_page]));
|
|||
|
|
|||
|
// Set time vars
|
|||
|
$new_page->modified(time());
|
|||
|
|
|||
|
// First page save (required to have an existing new page folder
|
|||
|
// to store any files with destination '@self' in)
|
|||
|
$new_page->save();
|
|||
|
|
|||
|
// Copy uploaded files to the new page folder
|
|||
|
$copy_files = $this->copyFiles($form, $new_page);
|
|||
|
// Get uploaded files plus properties
|
|||
|
$file_fields = $copy_files['uploaded'];
|
|||
|
|
|||
|
// If original frontmatter exists update it to keep
|
|||
|
// already present page media
|
|||
|
if (isset($original_frontmatter)) {
|
|||
|
|
|||
|
$file_fields_updated = array();
|
|||
|
foreach ($file_fields as $file_field => $uploads) {
|
|||
|
$file_fields_updated[$file_field] = array_merge($original_frontmatter[$file_field], $uploads);
|
|||
|
|
|||
|
// Get any (uploaded and then) deleted files
|
|||
|
foreach ($copy_files['deleted'] as $file_to_delete) {
|
|||
|
if (file_exists($file_to_delete['file_path'])) {
|
|||
|
unlink($file_to_delete['file_path']);
|
|||
|
if (in_array($file_to_delete['file_path'], $file_fields_updated[$file_field][basename($file_to_delete['file_path'])])) {
|
|||
|
unset($file_fields_updated[$file_field][basename($file_to_delete['file_path'])]);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
$file_fields = $file_fields_updated;
|
|||
|
}
|
|||
|
|
|||
|
// Add uploaded file properties to frontmatter
|
|||
|
if (isset($file_fields) && isset($page_frontmatter)) {
|
|||
|
$page_frontmatter = array_merge($page_frontmatter, $file_fields);
|
|||
|
}
|
|||
|
|
|||
|
// Add modified frontmatter to the page header
|
|||
|
$new_page->header((object) $page_frontmatter);
|
|||
|
$new_page->frontmatter(Yaml::dump((array)$new_page->header(), 20));
|
|||
|
|
|||
|
// Set time vars
|
|||
|
$new_page->modified(time());
|
|||
|
|
|||
|
// Update the new page
|
|||
|
$new_page->save();
|
|||
|
|
|||
|
// Store the route so it can be used to redirect to if needed and to be saved as a Twig var
|
|||
|
$this->new_page_route = $route;
|
|||
|
$this->page_frontmatter = $page_frontmatter;
|
|||
|
|
|||
|
// Process any new taxonomy types
|
|||
|
if ($auto_taxonomy_types && isset($page_frontmatter['taxonomy'])) {
|
|||
|
|
|||
|
// Read site configuration
|
|||
|
$grav = Grav::instance();
|
|||
|
$locator = $grav['locator'];
|
|||
|
$filename = 'config://site.yaml';
|
|||
|
$file = YamlFile::instance($locator->findResource($filename, true, true));
|
|||
|
$site_config = Yaml::parse($file->load());
|
|||
|
|
|||
|
// Merge taxonomy types
|
|||
|
$taxonomies = (array) $this->config->get('site.taxonomies');
|
|||
|
foreach (array_keys($page_frontmatter['taxonomy']) as $type) {
|
|||
|
$taxonomies = array_merge($taxonomies, (array) $type);
|
|||
|
}
|
|||
|
|
|||
|
// Don't bother if there are no new taxonomy types
|
|||
|
if (count(array_unique($taxonomies)) > count($site_config['taxonomies'])) {
|
|||
|
$this->config->set('site.taxonomies', $taxonomies);
|
|||
|
$taxonomies_merged = array();
|
|||
|
$taxonomies_merged['taxonomies'] = array_values(array_unique($taxonomies));
|
|||
|
$site_config = array_merge($site_config, $taxonomies_merged);
|
|||
|
|
|||
|
// Update taxonomy types in site.yaml
|
|||
|
$file->save($site_config);
|
|||
|
$file->free();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Fire AfterPageSave event
|
|||
|
$this->grav->fireEvent('onAddPageByFormPluginAfterPageSave', new Event(['page' => $new_page]));
|
|||
|
|
|||
|
} catch (\Exception $e) {
|
|||
|
$this->grav['debugger']->addMessage($e->getMessage());
|
|||
|
$this->grav->fireEvent('onFormValidationError', new Event([
|
|||
|
'form' => $form,
|
|||
|
'message' => '<strong>ERROR:</strong> ' . $e->getMessage()]));
|
|||
|
$event->stopPropagation();
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
case ($action == 'redirect'):
|
|||
|
|
|||
|
// The Form plugin does not know how to handle '@self' as a redirect
|
|||
|
// or display parameter, so prepare the redirect to the new page
|
|||
|
switch (strtolower((string) $params)) {
|
|||
|
case '@self':
|
|||
|
$route = $this->new_page_route;
|
|||
|
break;
|
|||
|
case '@self-admin':
|
|||
|
$admin_route = $this->config->get('plugins.admin.route');
|
|||
|
if ($admin_route && $this->config->get('plugins.admin.enabled')) {
|
|||
|
$base = DS . trim($admin_route, DS);
|
|||
|
$route = $base . DS . 'pages' . $this->new_page_route;
|
|||
|
} else {
|
|||
|
// Admin not installed or inactive
|
|||
|
// Fall back to @self
|
|||
|
$route = $this->new_page_route;
|
|||
|
}
|
|||
|
break;
|
|||
|
default:
|
|||
|
// No valid redirect to self parameter
|
|||
|
$route = '';
|
|||
|
}
|
|||
|
|
|||
|
// Do the redirect
|
|||
|
// BTW if there is no route the redirect is handed over to the Form plugin
|
|||
|
if ($route) {
|
|||
|
|
|||
|
/** @var Twig $twig */
|
|||
|
$twig = $this->grav['twig'];
|
|||
|
$twig->twig_vars['form'] = $form;
|
|||
|
$twig->twig_vars['pagefrontmatter'] = $this->page_frontmatter;
|
|||
|
|
|||
|
/** @var Pages $pages */
|
|||
|
$pages = $this->grav['pages'];
|
|||
|
$page = $pages->dispatch($route, false);
|
|||
|
|
|||
|
// Redirect to the new page
|
|||
|
unset($this->grav['page']);
|
|||
|
$this->grav['page'] = $page;
|
|||
|
$this->grav->redirect($route);
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Add assets
|
|||
|
*
|
|||
|
*/
|
|||
|
public function onPageInitialized()
|
|||
|
{
|
|||
|
// Some forms (like forgot_password) do not have frontmatter
|
|||
|
if (null !== ($this->grav['page']->frontmatter())) {
|
|||
|
$data = Yaml::parse($this->grav['page']->frontmatter());
|
|||
|
// Only act upon forms which are intended to be processed by this plugin
|
|||
|
if (isset($data['form']) && isset($data['form']['name']) &&
|
|||
|
in_array(strtolower(substr($data['form']['name'], 0, 7)), $this->say_my_name)) {
|
|||
|
|
|||
|
if ($this->config->get('plugins.add-page-by-form.use_editor_class',true)) {
|
|||
|
$assets = $this->grav['assets'];
|
|||
|
// Add jQuery library (no harm done when already present)
|
|||
|
$assets->add('jquery', 101);
|
|||
|
// Add SimpleMDE Markdown Editor
|
|||
|
$assets->addCss('//cdn.jsdelivr.net/simplemde/latest/simplemde.min.css', 100);
|
|||
|
$assets->addJs('//cdn.jsdelivr.net/simplemde/latest/simplemde.min.js', 100);
|
|||
|
// Add custom styles
|
|||
|
$assets->addCss('plugin://add-page-by-form/assets/css/customstyles.css', 110);
|
|||
|
// Load inline Javascript code from configuration file
|
|||
|
$assets->addInlineJs(file_get_contents('plugin://add-page-by-form/assets/js/simplemde_config.js'), 110);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Initialize the plugin
|
|||
|
*/
|
|||
|
public function onPluginsInitialized()
|
|||
|
{
|
|||
|
// Don't proceed if we are in the admin plugin
|
|||
|
if ($this->isAdmin()) {
|
|||
|
return;
|
|||
|
}
|
|||
|
// Enable the events we are interested in
|
|||
|
$this->enable([
|
|||
|
'onPageInitialized' => ['onPageInitialized', 0],
|
|||
|
]);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Check whether a page exists at the specified route irrespective of page type
|
|||
|
*
|
|||
|
* @param string $route
|
|||
|
* @param string $slug
|
|||
|
*
|
|||
|
* @return object $page
|
|||
|
*/
|
|||
|
public function pageExists($route, $slug)
|
|||
|
{
|
|||
|
$page = $this->grav['page']->find($route . DS . $slug);
|
|||
|
if (is_null($page) && !empty($slug) && $slug[0] != '_') {
|
|||
|
$page = $this->grav['page']->find($route . DS . '_' . $slug);
|
|||
|
}
|
|||
|
return $page;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Generates a slug of the given string
|
|||
|
* Source: Laravel str_slug()
|
|||
|
*
|
|||
|
* @param string $str
|
|||
|
* @param string $separator (optional)
|
|||
|
* @return string
|
|||
|
*/
|
|||
|
public static function slug(string $str, string $separator = '-')
|
|||
|
{
|
|||
|
// Sanitize non-latin characters
|
|||
|
$str = self::sanitize($str, 'slug');
|
|||
|
|
|||
|
// Convert all dashes/underscores into separator
|
|||
|
$flip = $separator === '-' ? '_' : '-';
|
|||
|
$str = preg_replace('!['.preg_quote($flip).']+!u', $separator, $str);
|
|||
|
|
|||
|
// Replace @ with the word 'at'
|
|||
|
$str = str_replace('@', $separator.'at'.$separator, $str);
|
|||
|
|
|||
|
// Remove all characters that are not the separator, letters, numbers, or whitespace.
|
|||
|
$str = preg_replace('![^'.preg_quote($separator).'\pL\pN\s]+!u', '', strtolower($str));
|
|||
|
|
|||
|
// Replace all separator characters and whitespace by a single separator
|
|||
|
$str = preg_replace('!['.preg_quote($separator).'\s]+!u', $separator, $str);
|
|||
|
|
|||
|
return trim($str, $separator);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Sanitize a string into a safe filename or slug
|
|||
|
*
|
|||
|
* @param string $f
|
|||
|
*
|
|||
|
* @return string
|
|||
|
*/
|
|||
|
public function sanitize($f, $type = 'file')
|
|||
|
{
|
|||
|
/* A combination of various methods to sanitize a string while retaining
|
|||
|
the "essence" of the original file name as much as possible.
|
|||
|
Note: unsuitable for file paths as '/' and '\' are filtered out.
|
|||
|
Sources:
|
|||
|
http://www.house6.com/blog/?p=83
|
|||
|
and
|
|||
|
http://stackoverflow.com/a/24984010
|
|||
|
*/
|
|||
|
$replace_chars = array(
|
|||
|
'&' => '-and-', '@' => '-at-', '©' => 'c', '®' => 'r', 'À' => 'a',
|
|||
|
'Á' => 'a', 'Â' => 'a', 'Ä' => 'a', 'Å' => 'a', 'Æ' => 'ae', 'Ç' => 'c',
|
|||
|
'È' => 'e', 'É' => 'e', 'Ë' => 'e', 'Ì' => 'i', 'Í' => 'i', 'Î' => 'i',
|
|||
|
'Ï' => 'i', 'Ò' => 'o', 'Ó' => 'o', 'Ô' => 'o', 'Õ' => 'o', 'Ö' => 'o',
|
|||
|
'Ø' => 'o', 'Ù' => 'u', 'Ú' => 'u', 'Û' => 'u', 'Ü' => 'u', 'Ý' => 'y',
|
|||
|
'ß' => 'ss', 'à' => 'a', 'á' => 'a', 'â' => 'a', 'ä' => 'a', 'å' => 'a',
|
|||
|
'æ' => 'ae', 'ç' => 'c', 'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e',
|
|||
|
'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i', 'ò' => 'o', 'ó' => 'o',
|
|||
|
'ô' => 'o', 'õ' => 'o', 'ö' => 'o', 'ø' => 'o', 'ù' => 'u', 'ú' => 'u',
|
|||
|
'û' => 'u', 'ü' => 'u', 'ý' => 'y', 'þ' => 'p', 'ÿ' => 'y', 'Ā' => 'a',
|
|||
|
'ā' => 'a', 'Ă' => 'a', 'ă' => 'a', 'Ą' => 'a', 'ą' => 'a', 'Ć' => 'c',
|
|||
|
'ć' => 'c', 'Ĉ' => 'c', 'ĉ' => 'c', 'Ċ' => 'c', 'ċ' => 'c', 'Č' => 'c',
|
|||
|
'č' => 'c', 'Ď' => 'd', 'ď' => 'd', 'Đ' => 'd', 'đ' => 'd', 'Ē' => 'e',
|
|||
|
'ē' => 'e', 'Ĕ' => 'e', 'ĕ' => 'e', 'Ė' => 'e', 'ė' => 'e', 'Ę' => 'e',
|
|||
|
'ę' => 'e', 'Ě' => 'e', 'ě' => 'e', 'Ĝ' => 'g', 'ĝ' => 'g', 'Ğ' => 'g',
|
|||
|
'ğ' => 'g', 'Ġ' => 'g', 'ġ' => 'g', 'Ģ' => 'g', 'ģ' => 'g', 'Ĥ' => 'h',
|
|||
|
'ĥ' => 'h', 'Ħ' => 'h', 'ħ' => 'h', 'Ĩ' => 'i', 'ĩ' => 'i', 'Ī' => 'i',
|
|||
|
'ī' => 'i', 'Ĭ' => 'i', 'ĭ' => 'i', 'Į' => 'i', 'į' => 'i', 'İ' => 'i',
|
|||
|
'ı' => 'i', 'IJ' => 'ij', 'ij' => 'ij', 'Ĵ' => 'j', 'ĵ' => 'j', 'Ķ' => 'k',
|
|||
|
'ķ' => 'k', 'ĸ' => 'k', 'Ĺ' => 'l', 'ĺ' => 'l', 'Ļ' => 'l', 'ļ' => 'l',
|
|||
|
'Ľ' => 'l', 'ľ' => 'l', 'Ŀ' => 'l', 'ŀ' => 'l', 'Ł' => 'l', 'ł' => 'l',
|
|||
|
'Ń' => 'n', 'ń' => 'n', 'Ņ' => 'n', 'ņ' => 'n', 'Ň' => 'n', 'ň' => 'n',
|
|||
|
'ʼn' => 'n', 'Ŋ' => 'n', 'ŋ' => 'n', 'Ō' => 'o', 'ō' => 'o', 'Ŏ' => 'o',
|
|||
|
'ŏ' => 'o', 'Ő' => 'o', 'ő' => 'o', 'Œ' => 'oe', 'œ' => 'oe', 'Ŕ' => 'r',
|
|||
|
'ŕ' => 'r', 'Ŗ' => 'r', 'ŗ' => 'r', 'Ř' => 'r', 'ř' => 'r', 'Ś' => 's',
|
|||
|
'ś' => 's', 'Ŝ' => 's', 'ŝ' => 's', 'Ş' => 's', 'ş' => 's', 'Š' => 's',
|
|||
|
'š' => 's', 'Ţ' => 't', 'ţ' => 't', 'Ť' => 't', 'ť' => 't', 'Ŧ' => 't',
|
|||
|
'ŧ' => 't', 'Ũ' => 'u', 'ũ' => 'u', 'Ū' => 'u', 'ū' => 'u', 'Ŭ' => 'u',
|
|||
|
'ŭ' => 'u', 'Ů' => 'u', 'ů' => 'u', 'Ű' => 'u', 'ű' => 'u', 'Ų' => 'u',
|
|||
|
'ų' => 'u', 'Ŵ' => 'w', 'ŵ' => 'w', 'Ŷ' => 'y', 'ŷ' => 'y', 'Ÿ' => 'y',
|
|||
|
'Ź' => 'z', 'ź' => 'z', 'Ż' => 'z', 'ż' => 'z', 'Ž' => 'z', 'ž' => 'z',
|
|||
|
'ſ' => 'z', 'Ə' => 'e', 'ƒ' => 'f', 'Ơ' => 'o', 'ơ' => 'o', 'Ư' => 'u',
|
|||
|
'ư' => 'u', 'Ǎ' => 'a', 'ǎ' => 'a', 'Ǐ' => 'i', 'ǐ' => 'i', 'Ǒ' => 'o',
|
|||
|
'ǒ' => 'o', 'Ǔ' => 'u', 'ǔ' => 'u', 'Ǖ' => 'u', 'ǖ' => 'u', 'Ǘ' => 'u',
|
|||
|
'ǘ' => 'u', 'Ǚ' => 'u', 'ǚ' => 'u', 'Ǜ' => 'u', 'ǜ' => 'u', 'Ǻ' => 'a',
|
|||
|
'ǻ' => 'a', 'Ǽ' => 'ae', 'ǽ' => 'ae', 'Ǿ' => 'o', 'ǿ' => 'o', 'ə' => 'e',
|
|||
|
'Ё' => 'jo', 'Є' => 'e', 'І' => 'i', 'Ї' => 'i', 'А' => 'a', 'Б' => 'b',
|
|||
|
'В' => 'v', 'Г' => 'g', 'Д' => 'd', 'Е' => 'e', 'Ж' => 'zh', 'З' => 'z',
|
|||
|
'И' => 'i', 'Й' => 'j', 'К' => 'k', 'Л' => 'l', 'М' => 'm', 'Н' => 'n',
|
|||
|
'О' => 'o', 'П' => 'p', 'Р' => 'r', 'С' => 's', 'Т' => 't', 'У' => 'u',
|
|||
|
'Ф' => 'f', 'Х' => 'h', 'Ц' => 'c', 'Ч' => 'ch', 'Ш' => 'sh', 'Щ' => 'sch',
|
|||
|
'Ъ' => '-', 'Ы' => 'y', 'Ь' => '-', 'Э' => 'je', 'Ю' => 'ju', 'Я' => 'ja',
|
|||
|
'а' => 'a', 'б' => 'b', 'в' => 'v', 'г' => 'g', 'д' => 'd', 'е' => 'e',
|
|||
|
'ж' => 'zh', 'з' => 'z', 'и' => 'i', 'й' => 'j', 'к' => 'k', 'л' => 'l',
|
|||
|
'м' => 'm', 'н' => 'n', 'о' => 'o', 'п' => 'p', 'р' => 'r', 'с' => 's',
|
|||
|
'т' => 't', 'у' => 'u', 'ф' => 'f', 'х' => 'h', 'ц' => 'c', 'ч' => 'ch',
|
|||
|
'ш' => 'sh', 'щ' => 'sch', 'ъ' => '-', 'ы' => 'y', 'ь' => '-', 'э' => 'je',
|
|||
|
'ю' => 'ju', 'я' => 'ja', 'ё' => 'jo', 'є' => 'e', 'і' => 'i', 'ї' => 'i',
|
|||
|
'Ґ' => 'g', 'ґ' => 'g', 'א' => 'a', 'ב' => 'b', 'ג' => 'g', 'ד' => 'd',
|
|||
|
'ה' => 'h', 'ו' => 'v', 'ז' => 'z', 'ח' => 'h', 'ט' => 't', 'י' => 'i',
|
|||
|
'ך' => 'k', 'כ' => 'k', 'ל' => 'l', 'ם' => 'm', 'מ' => 'm', 'ן' => 'n',
|
|||
|
'נ' => 'n', 'ס' => 's', 'ע' => 'e', 'ף' => 'p', 'פ' => 'p', 'ץ' => 'C',
|
|||
|
'צ' => 'c', 'ק' => 'q', 'ר' => 'r', 'ש' => 'w', 'ת' => 't', '™' => 'tm',
|
|||
|
'Ã' => 'A', 'Ð' => 'Dj', 'Ê' => 'E', 'Ñ' => 'N', 'Þ' => 'B', 'ã' => 'a',
|
|||
|
'ð' => 'o', 'ñ' => 'n', '#' => '-nr-');
|
|||
|
// "Translate" multi byte characters to 'corresponding' ASCII characters
|
|||
|
$f = strtr($f, $replace_chars);
|
|||
|
// Convert special characters to a hyphen
|
|||
|
$f = str_replace(array(
|
|||
|
' ', '!', '\\', '/', '\'', '`', '"', '~', '%', '|',
|
|||
|
'*', '$', '^', '(', ')', '[', ']', '{', '}',
|
|||
|
'+', ',', ':', ';', '<', '=', '>', '?', '|'), '-', $f);
|
|||
|
// Remove any non ASCII characters
|
|||
|
$f = preg_replace('/[^(\x20-\x7F)]*/', '', $f);
|
|||
|
if ($type == 'file') {
|
|||
|
// Remove non-word chars (leaving hyphens and periods)
|
|||
|
$f = preg_replace('/[^\w\-\.]+/', '', $f);
|
|||
|
// Convert multiple adjacent dots into a single one
|
|||
|
$f = preg_replace('/[\.]+/', '.', $f);
|
|||
|
} else { // Do not allow periods, for instance for a Grav slug
|
|||
|
// Convert period to hyphen
|
|||
|
$f = str_replace('.', '-', $f);
|
|||
|
// Remove non-word chars (leaving hyphens)
|
|||
|
$f = preg_replace('/[^\w\-]+/', '', $f);
|
|||
|
}
|
|||
|
// Convert multiple adjacent hyphens into a single one
|
|||
|
$f = preg_replace('/[\-]+/', '-', $f);
|
|||
|
// Change into a lowercase string; BTW no need to use mb_strtolower() here ;)
|
|||
|
$f = strtolower($f);
|
|||
|
return $f;
|
|||
|
}
|
|||
|
|
|||
|
}
|