mirror of
https://github.com/getgrav/grav.git
synced 2026-03-05 20:11:50 +01:00
Merge branch 'release/0.9.36'
This commit is contained in:
21
CHANGELOG.md
21
CHANGELOG.md
@@ -1,3 +1,19 @@
|
||||
# v0.9.36
|
||||
## 08/11/2015
|
||||
|
||||
1. [](#new)
|
||||
* Added a new `newuser` CLI command to create user accounts
|
||||
* Added `default` blueprint for all templates
|
||||
* Support `user` and `system` language translation merging
|
||||
1. [](#improved)
|
||||
* Added isSymlink method in GPM to determine if Grav is symbolically linked or not
|
||||
* Refactored page recursing
|
||||
* Updated blueprints to use new toggles
|
||||
* Updated blueprints to use current date for date format fields
|
||||
* Updated composer.phar
|
||||
* Use sessions for admin even when disabled for site
|
||||
* Use `GRAV_ROOT` in session identifier
|
||||
|
||||
# v0.9.35
|
||||
## 08/06/2015
|
||||
|
||||
@@ -5,13 +21,12 @@
|
||||
* Added `body_classes` field
|
||||
* Added `visiblity` toggle and help tooltips on new page form
|
||||
* Added new `Page.unsetRoute()` method to allow admin to regenerate the route
|
||||
1. [](#improved)
|
||||
2. [](#improved)
|
||||
* User save no longer stores username each time
|
||||
* Page list form field now shows all pages except root
|
||||
* Removed required option from page title
|
||||
* Added configuration settings for running Nginx in sub directory
|
||||
1. [](#bugfix)
|
||||
* Fixed issue with GPM and cURL throwing `Undefined offset: 1` error
|
||||
3. [](#bugfix)
|
||||
* Fixed deep translation merging
|
||||
* Fixed broken **metadata** merging with site defaults
|
||||
* Fixed broken **summary** field
|
||||
|
||||
Binary file not shown.
1
bin/grav
1
bin/grav
@@ -41,5 +41,6 @@ $app->addCommands(array(
|
||||
new Grav\Console\Cli\ClearCacheCommand(),
|
||||
new Grav\Console\Cli\BackupCommand(),
|
||||
new Grav\Console\Cli\NewProjectCommand(),
|
||||
new Grav\Console\Cli\NewUserCommand(),
|
||||
));
|
||||
$app->run();
|
||||
|
||||
@@ -50,31 +50,31 @@ form:
|
||||
'': 'Default (Server Timezone)'
|
||||
|
||||
pages.dateformat.short:
|
||||
type: select
|
||||
type: dateformat
|
||||
size: medium
|
||||
classes: fancy
|
||||
label: Short date format
|
||||
help: "Set the short date format that can be used by themes"
|
||||
default: "jS M Y"
|
||||
options:
|
||||
"F jS \\a\\t g:ia": "January 1st at 11:59pm"
|
||||
"l jS of F g:i A": "Monday 1st of January at 11:59 PM"
|
||||
"D, m M Y G:i:s": "Mon, 01 Jan 2014 23:59:00"
|
||||
"d-m-y G:i": "01-01-14 23:59"
|
||||
"jS M Y": "10th Feb 2014"
|
||||
"F jS \\a\\t g:ia": Date1
|
||||
"l jS of F g:i A": Date2
|
||||
"D, m M Y G:i:s": Date3
|
||||
"d-m-y G:i": Date4
|
||||
"jS M Y": Date5
|
||||
|
||||
pages.dateformat.long:
|
||||
type: select
|
||||
type: dateformat
|
||||
size: medium
|
||||
classes: fancy
|
||||
label: Long date format
|
||||
help: "Set the long date format that can be used by themes"
|
||||
options:
|
||||
"F jS \\a\\t g:ia": "January 1st at 11:59pm"
|
||||
"l jS of F g:i A": "Monday 1st of January at 11:59 PM"
|
||||
"D, m M Y G:i:s": "Mon, 01 Jan 2014 23:59:00"
|
||||
"d-m-y G:i": "01-01-14 23:59"
|
||||
"jS M Y": "10th Feb 2014"
|
||||
"F jS \\a\\t g:ia": Date1
|
||||
"l jS of F g:i A": Date2
|
||||
"D, m M Y G:i:s": Date3
|
||||
"d-m-y G:i": Date4
|
||||
"jS M Y": Date5
|
||||
|
||||
pages.order.by:
|
||||
type: select
|
||||
@@ -180,7 +180,7 @@ form:
|
||||
languages.session_store_active:
|
||||
type: toggle
|
||||
label: Active language in session
|
||||
help: "Support translations in Grav, plugins and extensions"
|
||||
help: "Store the active language in the session"
|
||||
highlight: 0
|
||||
options:
|
||||
1: Yes
|
||||
|
||||
@@ -50,12 +50,12 @@ form:
|
||||
fields:
|
||||
header.published:
|
||||
type: toggle
|
||||
toggleable: true
|
||||
label: Published
|
||||
help: "By default, a page is published unless you explicitly set published: false or via a publish_date being in the future, or unpublish_date in the past"
|
||||
highlight: 1
|
||||
size: medium
|
||||
options:
|
||||
'': Global
|
||||
1: Yes
|
||||
0: No
|
||||
validate:
|
||||
@@ -232,11 +232,11 @@ form:
|
||||
|
||||
header.visible:
|
||||
type: toggle
|
||||
toggleable: true
|
||||
label: Visible
|
||||
help: "Determines if a page is visible in the navigation."
|
||||
highlight: 1
|
||||
options:
|
||||
'': Global
|
||||
1: Enabled
|
||||
0: Disabled
|
||||
validate:
|
||||
@@ -244,12 +244,11 @@ form:
|
||||
|
||||
header.routable:
|
||||
type: toggle
|
||||
toggleable: true
|
||||
label: Routable
|
||||
help: If this page is reachable by a URL
|
||||
highlight: 1
|
||||
default: ''
|
||||
options:
|
||||
'': Global
|
||||
1: Enabled
|
||||
0: Disabled
|
||||
validate:
|
||||
@@ -257,10 +256,10 @@ form:
|
||||
|
||||
header.cache_enable:
|
||||
type: toggle
|
||||
toggleable: true
|
||||
label: Caching
|
||||
highlight: 1
|
||||
options:
|
||||
'': Global
|
||||
1: Enabled
|
||||
0: Disabled
|
||||
validate:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
// Some standard defines
|
||||
define('GRAV', true);
|
||||
define('GRAV_VERSION', '0.9.35');
|
||||
define('GRAV_VERSION', '0.9.36');
|
||||
define('DS', '/');
|
||||
|
||||
// Directories and Paths
|
||||
|
||||
@@ -373,10 +373,11 @@ class Config extends Data
|
||||
$content = $lang_file->content();
|
||||
$this->languages->mergeRecursive($content);
|
||||
}
|
||||
unset($languageFiles['user/plugins']);
|
||||
}
|
||||
|
||||
if (isset($languageFiles['system/languages'])) {
|
||||
foreach ((array) $languageFiles['system/languages'] as $lang => $item) {
|
||||
foreach ($languageFiles as $location) {
|
||||
foreach ($location as $lang => $item) {
|
||||
$lang_file = CompiledYamlFile::instance($item['file']);
|
||||
$content = $lang_file->content();
|
||||
$this->languages->join($lang, $content, '/');
|
||||
|
||||
@@ -287,4 +287,14 @@ class Installer
|
||||
{
|
||||
return self::$error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to manually set an error
|
||||
* @param $error the Error code
|
||||
*/
|
||||
|
||||
public static function setError($error)
|
||||
{
|
||||
self::$error = $error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,4 +87,9 @@ class Grav extends AbstractPackageCollection
|
||||
{
|
||||
return version_compare(GRAV_VERSION, $this->getVersion(), '<');
|
||||
}
|
||||
|
||||
public function isSymlink()
|
||||
{
|
||||
return is_link(GRAV_ROOT . DS . 'index.php');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,4 +80,14 @@ class Upgrader
|
||||
{
|
||||
return version_compare($this->getLocalVersion(), $this->getRemoteVersion(), "<");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if Grav is currently symbolically linked
|
||||
* @return boolean True if Grav is symlinked, False otherwise.
|
||||
*/
|
||||
|
||||
public function isSymlink()
|
||||
{
|
||||
return $this->remote->isSymlink();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,7 +257,7 @@ class Grav extends Container
|
||||
$this['session']->close();
|
||||
}
|
||||
|
||||
if ($this['uri']->isExternal($route)) {
|
||||
if ($uri->isExternal($route)) {
|
||||
$url = $route;
|
||||
} else {
|
||||
$url = rtrim($uri->rootUrl(), '/') .'/'. trim($route, '/');
|
||||
@@ -277,7 +277,6 @@ class Grav extends Container
|
||||
{
|
||||
/** @var Language $language */
|
||||
$language = $this['language'];
|
||||
$config = $this['config'];
|
||||
|
||||
if ($language->enabled()) {
|
||||
return $this->redirect($language->getLanguage() . $route, $code);
|
||||
@@ -413,7 +412,7 @@ class Grav extends Container
|
||||
}
|
||||
|
||||
/**
|
||||
* This attempts to fine media, other files, and download them
|
||||
* This attempts to find media, other files, and download them
|
||||
* @param $page
|
||||
* @param $path
|
||||
*/
|
||||
|
||||
@@ -705,16 +705,16 @@ class Page
|
||||
$pages = self::getGrav()['pages'];
|
||||
|
||||
$blueprint = $pages->blueprints($this->blueprintName());
|
||||
|
||||
$fields = $blueprint->fields();
|
||||
$edit_mode = self::getGrav()['admin'] ? self::getGrav()['config']->get('plugins.admin.edit_mode') : null;
|
||||
|
||||
// override if you only want 'normal' mode
|
||||
if (empty($fields) && self::getGrav()['admin'] && self::getGrav()['config']->get('plugins.admin.edit_mode', 'auto') == 'normal') {
|
||||
if (empty($fields) && ($edit_mode == 'auto' || $edit_mode == 'normal')) {
|
||||
$blueprint = $pages->blueprints('default');
|
||||
}
|
||||
|
||||
// override if you only want 'expert' mode
|
||||
if (!empty($fields) && self::getGrav()['admin'] && self::getGrav()['config']->get('plugins.admin.edit_mode', 'auto') == 'expert') {
|
||||
if (!empty($fields) && $edit_mode == 'expert') {
|
||||
$blueprint = $pages->blueprints('');
|
||||
}
|
||||
|
||||
|
||||
@@ -514,7 +514,7 @@ class Pages
|
||||
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $this->grav['locator'];
|
||||
$pagesDir = $locator->findResource('page://');
|
||||
$pages_dir = $locator->findResource('page://');
|
||||
|
||||
if ($config->get('system.cache.enabled')) {
|
||||
/** @var Cache $cache */
|
||||
@@ -529,10 +529,10 @@ class Pages
|
||||
$last_modified = 0;
|
||||
break;
|
||||
case 'folder':
|
||||
$last_modified = Folder::lastModifiedFolder($pagesDir);
|
||||
$last_modified = Folder::lastModifiedFolder($pages_dir);
|
||||
break;
|
||||
default:
|
||||
$last_modified = Folder::lastModifiedFile($pagesDir);
|
||||
$last_modified = Folder::lastModifiedFile($pages_dir);
|
||||
}
|
||||
|
||||
$page_cache_id = md5(USER_DIR.$last_modified.$language->getActive().$config->checksum());
|
||||
@@ -541,25 +541,46 @@ class Pages
|
||||
if (!$this->instances) {
|
||||
$this->grav['debugger']->addMessage('Page cache missed, rebuilding pages..');
|
||||
|
||||
$this->recurse($pagesDir);
|
||||
$this->buildRoutes();
|
||||
// recurse pages and cache result
|
||||
$this->resetPages($pages_dir, $page_cache_id);
|
||||
|
||||
// save pages, routes, taxonomy, and sort to cache
|
||||
$cache->save(
|
||||
$page_cache_id,
|
||||
array($this->instances, $this->routes, $this->children, $taxonomy->taxonomy(), $this->sort)
|
||||
);
|
||||
} else {
|
||||
// If pages was found in cache, set the taxonomy
|
||||
$this->grav['debugger']->addMessage('Page cache hit.');
|
||||
$taxonomy->taxonomy($taxonomy_map);
|
||||
}
|
||||
} else {
|
||||
$this->recurse($pagesDir);
|
||||
$this->recurse($pages_dir);
|
||||
$this->buildRoutes();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessible method to manually reset the pages cache
|
||||
*
|
||||
* @param $pages_dir
|
||||
* @param $page_cache_id
|
||||
*/
|
||||
public function resetPages($pages_dir, $page_cache_id)
|
||||
{
|
||||
$this->recurse($pages_dir);
|
||||
$this->buildRoutes();
|
||||
|
||||
// cache if needed
|
||||
if ($this->grav['config']->get('system.cache.enabled')) {
|
||||
/** @var Cache $cache */
|
||||
$cache = $this->grav['cache'];
|
||||
/** @var Taxonomy $taxonomy */
|
||||
$taxonomy = $this->grav['taxonomy'];
|
||||
|
||||
// save pages, routes, taxonomy, and sort to cache
|
||||
$cache->save(
|
||||
$page_cache_id,
|
||||
array($this->instances, $this->routes, $this->children, $taxonomy->taxonomy(), $this->sort)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive function to load & build page relationships.
|
||||
*
|
||||
|
||||
@@ -49,6 +49,9 @@ class Types implements \ArrayAccess, \Iterator, \Countable
|
||||
$this->systemBlueprints = $this->findBlueprints('blueprints://pages');
|
||||
}
|
||||
|
||||
// register default by default
|
||||
$this->register('default');
|
||||
|
||||
foreach (Folder::all($path, $options) as $type) {
|
||||
$this->register($type);
|
||||
}
|
||||
|
||||
@@ -20,17 +20,23 @@ class Session extends \RocketTheme\Toolbox\Session\Session
|
||||
$uri = $this->grav['uri'];
|
||||
$config = $this->grav['config'];
|
||||
|
||||
if ($config->get('system.session.enabled')) {
|
||||
// Only activate admin if we're inside the admin path.
|
||||
$is_admin = false;
|
||||
$is_admin = false;
|
||||
|
||||
$session_timeout = $config->get('system.session.timeout', 1800);
|
||||
$session_path = $config->get('system.session.path', '/' . ltrim($uri->rootUrl(false), '/'));
|
||||
|
||||
// Activate admin if we're inside the admin path.
|
||||
if ($config->get('plugins.admin.enabled')) {
|
||||
$route = $config->get('plugins.admin.route');
|
||||
$base = '/' . trim($route, '/');
|
||||
if (substr($uri->route(), 0, strlen($base)) == $base) {
|
||||
$session_timeout = $config->get('plugins.admin.session.timeout', 1800);
|
||||
$is_admin = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($config->get('system.session.enabled') || $is_admin) {
|
||||
|
||||
$session_timeout = $config->get('system.session.timeout', 1800);
|
||||
$session_path = $config->get('system.session.path', '/' . ltrim($uri->rootUrl(false), '/'));
|
||||
|
||||
// Define session service.
|
||||
parent::__construct(
|
||||
@@ -38,8 +44,8 @@ class Session extends \RocketTheme\Toolbox\Session\Session
|
||||
$session_path
|
||||
);
|
||||
|
||||
$site_identifier = $config->get('site.title', 'unknown');
|
||||
$this->setName($config->get('system.session.name', 'grav_site') . '_' . substr(md5($site_identifier), 0, 7) . ($is_admin ? '_admin' : ''));
|
||||
$unique_identifier = GRAV_ROOT;
|
||||
$this->setName($config->get('system.session.name', 'grav_site') . '_' . substr(md5($unique_identifier), 0, 7) . ($is_admin ? '_admin' : ''));
|
||||
$this->start();
|
||||
setcookie(session_name(), session_id(), time() + $session_timeout, $session_path);
|
||||
}
|
||||
|
||||
136
system/src/Grav/Console/Cli/NewUserCommand.php
Normal file
136
system/src/Grav/Console/Cli/NewUserCommand.php
Normal file
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
namespace Grav\Console\Cli;
|
||||
|
||||
use Grav\Common\Data\Blueprints;
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\User\User;
|
||||
use Grav\Console\ConsoleTrait;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\ChoiceQuestion;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
|
||||
/**
|
||||
* Class CleanCommand
|
||||
* @package Grav\Console\Cli
|
||||
*/
|
||||
class NewUserCommand extends Command
|
||||
{
|
||||
use ConsoleTrait;
|
||||
|
||||
/**
|
||||
* Configure the command
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName("newuser")
|
||||
->setDescription("Creates a new user")
|
||||
->setHelp('The <info>newuser</info> creates a new user file in user/accounts/ folder');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
*
|
||||
* @return int|null|void
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->setupConsole($input, $output);
|
||||
$helper = $this->getHelper('question');
|
||||
$data = [];
|
||||
|
||||
$this->output->writeln('<green>Create new user</green>');
|
||||
$this->output->writeln('');
|
||||
|
||||
// Get username and validate
|
||||
$question = new Question('Enter a <yellow>username</yellow>: ', 'admin');
|
||||
$question->setValidator(function ($value) {
|
||||
if (!preg_match('/^[a-z0-9_-]{3,16}$/', $value)) {
|
||||
throw new RuntimeException(
|
||||
'Username should be between 3 and 16 comprised of lowercase letters, numbers, underscores and hyphens'
|
||||
);
|
||||
}
|
||||
if (file_exists(self::getGrav()['locator']->findResource('user://accounts/' . $value . YAML_EXT))) {
|
||||
throw new RuntimeException(
|
||||
'Username "'.$value.'" already exists, please pick another username'
|
||||
);
|
||||
}
|
||||
return $value;
|
||||
});
|
||||
$username = $helper->ask($this->input, $this->output, $question);
|
||||
|
||||
// Get password and validate
|
||||
$question = new Question('Enter a <yellow>password</yellow>: ');
|
||||
$question->setValidator(function ($value) {
|
||||
if (!preg_match('/(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}/', $value)) {
|
||||
throw new RuntimeException('Password must contain at least one number and one uppercase and lowercase letter, and at least 8 or more characters');
|
||||
}
|
||||
return $value;
|
||||
});
|
||||
$data['password'] = $helper->ask($this->input, $this->output, $question);
|
||||
|
||||
// Get email and validate
|
||||
$question = new Question('Enter an <yellow>email</yellow>: ');
|
||||
$question->setValidator(function ($value) {
|
||||
if (!preg_match('/^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/', $value)) {
|
||||
throw new RuntimeException(
|
||||
'Not a valid email address'
|
||||
);
|
||||
}
|
||||
return $value;
|
||||
});
|
||||
$data['email'] = $helper->ask($this->input, $this->output, $question);
|
||||
|
||||
// Choose permissions
|
||||
$question = new ChoiceQuestion(
|
||||
'Please choose a set of <yellow>permissions</yellow>:',
|
||||
array('a'=>'admin access', 's'=>'site access', 'b'=>'admin and site access'),
|
||||
'a'
|
||||
);
|
||||
$question->setErrorMessage('permissions %s is invalid.');
|
||||
$permissions_choice = $helper->ask($this->input, $this->output, $question);
|
||||
|
||||
switch ($permissions_choice) {
|
||||
case 'a':
|
||||
$data['access']['admin'] = ['login' => true, 'super' => true];
|
||||
break;
|
||||
case 's':
|
||||
$data['access']['site'] = ['login' => true];
|
||||
break;
|
||||
case 'b':
|
||||
$data['access']['admin'] = ['login' => true, 'super' => true];
|
||||
$data['access']['site'] = ['login' => true];
|
||||
}
|
||||
|
||||
// Get fullname
|
||||
$question = new Question('Enter a <yellow>fullname</yellow>: ');
|
||||
$question->setValidator(function ($value) {
|
||||
if ($value === null or trim($value) == '') {
|
||||
throw new RuntimeException(
|
||||
'Fullname is required'
|
||||
);
|
||||
}
|
||||
return $value;
|
||||
});
|
||||
$data['fullname'] = $helper->ask($this->input, $this->output, $question);
|
||||
|
||||
// Get title
|
||||
$question = new Question('Enter a <yellow>title</yellow>: ');
|
||||
$data['title'] = $helper->ask($this->input, $this->output, $question);
|
||||
|
||||
// Create user object and save it
|
||||
$user = new User($data);
|
||||
$file = CompiledYamlFile::instance(self::getGrav()['locator']->findResource('user://accounts/' . $username . YAML_EXT, true, true));
|
||||
$user->file($file);
|
||||
$user->save();
|
||||
|
||||
$this->output->writeln('');
|
||||
$this->output->writeln('<green>Success!</green> User <cyan>'. $username .'</cyan> created.');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user