This commit is contained in:
2018-09-19 21:43:25 +02:00
parent 1c407a4328
commit f9d3d02e74
482 changed files with 31736 additions and 9897 deletions
+61 -1
View File
@@ -1,3 +1,63 @@
# v1.5.1
## 08/23/2018
1. [](#new)
* Added static `Grav\Common\Yaml` class which should be used instead of `Symfony\Component\Yaml\Yaml`
1. [](#improved)
* Updated deprecated Twig code so it works in both in Twig 1.34+ and Twig 2.4+
* Switched to new Grav Yaml class to support Native + Fallback YAML libraries
1. [](#bugfix)
* Broken handling of user folder in Grav URI object [#2151](https://github.com/getgrav/grav/issues/2151)
# v1.5.0
## 08/17/2018
1. [](#new)
* Set minimum requirements to [PHP 5.6.4](https://getgrav.org/blog/raising-php-requirements-2018)
* Updated Doctrine Collections to 1.4
* Updated Symfony Components to 3.4 (with compatibility mode to fall back to Symfony YAML 2.8)
* Added `Uri::method()` to get current HTTP method (GET/POST etc)
* `FormatterInterface`: Added `getSupportedFileExtensions()` and `getDefaultFileExtension()` methods
* Added option to disable `SimpleCache` key validation
* Added support for multiple repo locations for `bin/grav install` command
* Added twig filters for casting values: `|string`, `|int`, `|bool`, `|float`, `|array`
* Made `ObjectCollection::matching()` criteria expressions to behave more like in Twig
* Criteria: Added support for `LENGTH()`, `LOWER()`, `UPPER()`, `LTRIM()`, `RTRIM()` and `TRIM()`
* Added `Grav\Framework\File\Formatter` classes for encoding/decoding YAML, Markdown, JSON, INI and PHP serialized strings
* Added `Grav\Framework\Session` class to replace `RocketTheme\Toolbox\Session\Session`
* Added `Grav\Common\Media` interfaces and trait; use those in `Page` and `Media` classes
* Added `Grav\Common\Page` interface to allow custom page types in the future
* Added setting to disable sessions from the site [#2013](https://github.com/getgrav/grav/issues/2013)
* Added new `strict_mode` settings in `system.yaml` for compatibility
1. [](#improved)
* Improved `Utils::url()` to support query strings
* Display better exception message if Grav fails to initialize
* Added `muted` and `playsinline` support to videos [#2124](https://github.com/getgrav/grav/pull/2124)
* Added `MediaTrait::clearMediaCache()` to allow cache to be cleared
* Added `MediaTrait::getMediaCache()` to allow custom caching
* Improved session handling, allow all session configuration options in `system.session.options`
1. [](#bugfix)
* Fix broken form nonce logic [#2121](https://github.com/getgrav/grav/pull/2121)
* Fixed issue with uppercase extensions and fallback media URLs [#2133](https://github.com/getgrav/grav/issues/2133)
* Fixed theme inheritance issue with `camel-case` that includes numbers [#2134](https://github.com/getgrav/grav/issues/2134)
* Typo in demo typography page [#2136](https://github.com/getgrav/grav/pull/2136)
* Fix for incorrect plugin order in debugger panel
* Made `|markdown` filter HTML safe
* Fixed bug in `ContentBlock` serialization
* Fixed `Route::withQueryParam()` to accept array values
* Fixed typo in truncate function [#1943](https://github.com/getgrav/grav/issues/1943)
* Fixed blueprint field validation: Allow numeric inputs in text fields
# v1.4.8
## 07/31/2018
1. [](#improved)
* Add Grav version to debug bar messages tab [#2106](https://github.com/getgrav/grav/pull/2106)
* Add Nginx config for ddev project to `webserver-configs` [#2117](https://github.com/getgrav/grav/pull/2117)
* Vendor library updates
1. [](#bugfix)
* Don't allow `null` to be set as Page content
# v1.4.7 # v1.4.7
## 07/13/2018 ## 07/13/2018
@@ -6,7 +66,7 @@
1. [](#bugfix) 1. [](#bugfix)
* Fix for modular page preview [#2066](https://github.com/getgrav/grav/issues/2066) * Fix for modular page preview [#2066](https://github.com/getgrav/grav/issues/2066)
* `Page::routeCanonical()` should be string not array [#2069](https://github.com/getgrav/grav/issues/2069) * `Page::routeCanonical()` should be string not array [#2069](https://github.com/getgrav/grav/issues/2069)
# v1.4.6 # v1.4.6
## 06/20/2018 ## 06/20/2018
+1 -1
View File
@@ -18,7 +18,7 @@ The underlying architecture of Grav is designed to use well-established and _bes
# Requirements # Requirements
- PHP 5.5.9 or higher. Check the [required modules list](https://learn.getgrav.org/basics/requirements#php-requirements) - PHP 5.6.4 or higher. Check the [required modules list](https://learn.getgrav.org/basics/requirements#php-requirements)
- Check the [Apache](https://learn.getgrav.org/basics/requirements#apache-requirements) or [IIS](https://learn.getgrav.org/basics/requirements#iis-requirements) requirements - Check the [Apache](https://learn.getgrav.org/basics/requirements#apache-requirements) or [IIS](https://learn.getgrav.org/basics/requirements#iis-requirements) requirements
# QuickStart # QuickStart
+8 -8
View File
@@ -6,17 +6,17 @@
"homepage": "http://getgrav.org", "homepage": "http://getgrav.org",
"license": "MIT", "license": "MIT",
"require": { "require": {
"php": ">=5.5.9", "php": ">=5.6.4",
"twig/twig": "~1.24", "twig/twig": "~1.24",
"erusev/parsedown": "1.6.4", "erusev/parsedown": "1.6.4",
"erusev/parsedown-extra": "~0.7", "erusev/parsedown-extra": "~0.7",
"symfony/yaml": "~2.8", "symfony/yaml": "~3.4",
"symfony/console": "~2.8", "symfony/console": "~3.4",
"symfony/event-dispatcher": "~2.8", "symfony/event-dispatcher": "~3.4",
"symfony/var-dumper": "~2.8", "symfony/var-dumper": "~3.4",
"symfony/polyfill-iconv": "~1.0", "symfony/polyfill-iconv": "~1.0",
"doctrine/cache": "^1.6", "doctrine/cache": "^1.6",
"doctrine/collections": "1.3", "doctrine/collections": "^1.4",
"psr/simple-cache": "^1.0", "psr/simple-cache": "^1.0",
"psr/http-message": "^1.0", "psr/http-message": "^1.0",
"guzzlehttp/psr7": "^1.4", "guzzlehttp/psr7": "^1.4",
@@ -26,7 +26,7 @@
"gregwar/image": "2.*", "gregwar/image": "2.*",
"donatj/phpuseragentparser": "~0.3", "donatj/phpuseragentparser": "~0.3",
"pimple/pimple": "~3.2", "pimple/pimple": "~3.2",
"rockettheme/toolbox": "~1.3.9", "rockettheme/toolbox": "~1.4",
"maximebf/debugbar": "~1.10", "maximebf/debugbar": "~1.10",
"ext-mbstring": "*", "ext-mbstring": "*",
"ext-openssl": "*", "ext-openssl": "*",
@@ -45,7 +45,7 @@
}, },
"config": { "config": {
"platform": { "platform": {
"php": "5.5.9" "php": "5.6.4"
} }
}, },
"repositories": [ "repositories": [
Generated
+228 -187
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -7,7 +7,7 @@
*/ */
namespace Grav; namespace Grav;
define('GRAV_PHP_MIN', '5.5.9'); define('GRAV_PHP_MIN', '5.6.4');
// Ensure vendor libraries exist // Ensure vendor libraries exist
$autoload = __DIR__ . '/vendor/autoload.php'; $autoload = __DIR__ . '/vendor/autoload.php';
@@ -15,7 +15,7 @@ if (!is_file($autoload)) {
die("Please run: <i>bin/grav install</i>"); die("Please run: <i>bin/grav install</i>");
} }
if (PHP_SAPI == 'cli-server') { if (PHP_SAPI === 'cli-server') {
if (!isset($_SERVER['PHP_CLI_ROUTER'])) { if (!isset($_SERVER['PHP_CLI_ROUTER'])) {
die("PHP webserver requires a router to run Grav, please use: <pre>php -S {$_SERVER['SERVER_NAME']}:{$_SERVER['SERVER_PORT']} system/router.php</pre>"); die("PHP webserver requires a router to run Grav, please use: <pre>php -S {$_SERVER['SERVER_NAME']}:{$_SERVER['SERVER_PORT']} system/router.php</pre>");
} }
+36
View File
@@ -996,6 +996,18 @@ form:
validate: validate:
type: bool type: bool
session.initialize:
type: toggle
label: PLUGIN_ADMIN.SESSION_INITIALIZE
help: PLUGIN_ADMIN.SESSION_INITIALIZE_HELP
highlight: 1
options:
1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO
default: true
validate:
type: bool
session.timeout: session.timeout:
type: text type: text
size: small size: small
@@ -1206,3 +1218,27 @@ form:
placeholder: "e.g. http://yoursite.com/yourpath" placeholder: "e.g. http://yoursite.com/yourpath"
label: PLUGIN_ADMIN.CUSTOM_BASE_URL label: PLUGIN_ADMIN.CUSTOM_BASE_URL
help: PLUGIN_ADMIN.CUSTOM_BASE_URL_HELP help: PLUGIN_ADMIN.CUSTOM_BASE_URL_HELP
strict_mode.yaml_compat:
type: toggle
label: PLUGIN_ADMIN.STRICT_YAML_COMPAT
highlight: 1
default: 1
help: PLUGIN_ADMIN.STRICT_YAML_COMPAT_HELP
options:
1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO
validate:
type: bool
strict_mode.twig_compat:
type: toggle
label: PLUGIN_ADMIN.STRICT_TWIG_COMPAT
highlight: 1
default: 1
help: PLUGIN_ADMIN.STRICT_TWIG_COMPAT_HELP
options:
1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO
validate:
type: bool
-2
View File
@@ -39,7 +39,6 @@ form:
options: options:
type: tab type: tab
title: PLUGIN_ADMIN.OPTIONS title: PLUGIN_ADMIN.OPTIONS
multiple: true
fields: fields:
@@ -49,7 +48,6 @@ form:
underline: true underline: true
fields: fields:
header.published: header.published:
type: toggle type: toggle
toggleable: true toggleable: true
+1
View File
@@ -94,6 +94,7 @@ form:
twofa_secret: twofa_secret:
type: 2fa_secret type: 2fa_secret
outerclasses: 'twofa-secret' outerclasses: 'twofa-secret'
markdown: true
label: PLUGIN_ADMIN.2FA_SECRET label: PLUGIN_ADMIN.2FA_SECRET
sublabel: PLUGIN_ADMIN.2FA_SECRET_HELP sublabel: PLUGIN_ADMIN.2FA_SECRET_HELP
+5 -1
View File
@@ -88,7 +88,7 @@ twig:
cache: true # Set to true to enable Twig caching cache: true # Set to true to enable Twig caching
debug: true # Enable Twig debug debug: true # Enable Twig debug
auto_reload: true # Refresh cache on changes auto_reload: true # Refresh cache on changes
autoescape: false # Autoescape Twig vars autoescape: false # Autoescape Twig vars (DEPRECATED, always enabled in strict mode)
undefined_functions: true # Allow undefined functions undefined_functions: true # Allow undefined functions
undefined_filters: true # Allow undefined filters undefined_filters: true # Allow undefined filters
umask_fix: false # By default Twig creates cached files as 755, fix switches this to 775 umask_fix: false # By default Twig creates cached files as 755, fix switches this to 775
@@ -146,3 +146,7 @@ gpm:
method: 'auto' # Either 'curl', 'fopen' or 'auto'. 'auto' will try fopen first and if not available cURL method: 'auto' # Either 'curl', 'fopen' or 'auto'. 'auto' will try fopen first and if not available cURL
verify_peer: true # Sometimes on some systems (Windows most commonly) GPM is unable to connect because the SSL certificate cannot be verified. Disabling this setting might help. verify_peer: true # Sometimes on some systems (Windows most commonly) GPM is unable to connect because the SSL certificate cannot be verified. Disabling this setting might help.
official_gpm_only: true # By default GPM direct-install will only allow URLs via the official GPM proxy to ensure security official_gpm_only: true # By default GPM direct-install will only allow URLs via the official GPM proxy to ensure security
strict_mode:
yaml_compat: true # Grav 1.5+: Enables YAML backwards compatibility
twig_compat: true # Grav 1.5+: Enables deprecated Twig autoescape setting (autoescape: false)
+2 -2
View File
@@ -8,12 +8,12 @@
// Some standard defines // Some standard defines
define('GRAV', true); define('GRAV', true);
define('GRAV_VERSION', '1.4.7'); define('GRAV_VERSION', '1.5.1');
define('GRAV_TESTING', false); define('GRAV_TESTING', false);
define('DS', '/'); define('DS', '/');
if (!defined('GRAV_PHP_MIN')) { if (!defined('GRAV_PHP_MIN')) {
define('GRAV_PHP_MIN', '5.5.9'); define('GRAV_PHP_MIN', '5.6.4');
} }
// Directories and Paths // Directories and Paths
+15 -11
View File
@@ -262,18 +262,22 @@ class Setup extends Data
); );
} }
if (!$locator->findResource('environment://config', true)) { try {
// If environment does not have its own directory, remove it from the lookup. if (!$locator->findResource('environment://config', true)) {
$this->set('streams.schemes.environment.prefixes', ['config' => []]); // If environment does not have its own directory, remove it from the lookup.
$this->initializeLocator($locator); $this->set('streams.schemes.environment.prefixes', ['config' => []]);
} $this->initializeLocator($locator);
}
// Create security.yaml if it doesn't exist. // Create security.yaml if it doesn't exist.
$filename = $locator->findResource('config://security.yaml', true, true); $filename = $locator->findResource('config://security.yaml', true, true);
$file = YamlFile::instance($filename); $file = YamlFile::instance($filename);
if (!$file->exists()) { if (!$file->exists()) {
$file->save(['salt' => Utils::generateRandomString(14)]); $file->save(['salt' => Utils::generateRandomString(14)]);
$file->free(); $file->free();
}
} catch (\RuntimeException $e) {
throw new \RuntimeException(sprintf('Grav failed to initialize: %s', $e->getMessage()), 500, $e);
} }
} }
} }
+10 -12
View File
@@ -10,9 +10,8 @@ namespace Grav\Common\Data;
use Grav\Common\Grav; use Grav\Common\Grav;
use Grav\Common\Utils; use Grav\Common\Utils;
use Symfony\Component\Yaml\Exception\ParseException; use Grav\Common\Yaml;
use Symfony\Component\Yaml\Parser; use RocketTheme\Toolbox\Compat\Yaml\Yaml as FallbackYaml;
use Symfony\Component\Yaml\Yaml;
class Validation class Validation
{ {
@@ -107,7 +106,7 @@ class Validation
$method = 'filter' . ucfirst(strtr($type, '-', '_')); $method = 'filter' . ucfirst(strtr($type, '-', '_'));
// If this is a YAML field validate/filter as such // If this is a YAML field validate/filter as such
if ($type != 'yaml' && isset($field['yaml']) && $field['yaml'] === true) { if ($type !== 'yaml' && isset($field['yaml']) && $field['yaml'] === true) {
$method = 'filterYaml'; $method = 'filterYaml';
} }
@@ -128,10 +127,12 @@ class Validation
*/ */
public static function typeText($value, array $params, array $field) public static function typeText($value, array $params, array $field)
{ {
if (!is_string($value)) { if (!is_string($value) && !is_numeric($value)) {
return false; return false;
} }
$value = (string)$value;
if (isset($params['min']) && strlen($value) < $params['min']) { if (isset($params['min']) && strlen($value) < $params['min']) {
return false; return false;
} }
@@ -643,15 +644,12 @@ class Validation
public static function filterYaml($value, $params) public static function filterYaml($value, $params)
{ {
try { if (!is_string($value)) {
if (is_string($value)) {
return (array) Yaml::parse($value);
} else {
return $value;
}
} catch (ParseException $e) {
return $value; return $value;
} }
return (array) Yaml::parse($value);
} }
/** /**
+8 -1
View File
@@ -58,8 +58,15 @@ class Debugger
$this->enabled = $this->config->get('system.debugger.enabled'); $this->enabled = $this->config->get('system.debugger.enabled');
if ($this->enabled()) { if ($this->enabled()) {
$plugins_config = (array)$this->config->get('plugins');
ksort($plugins_config);
$this->debugbar->addCollector(new ConfigCollector((array)$this->config->get('system'), 'Config')); $this->debugbar->addCollector(new ConfigCollector((array)$this->config->get('system'), 'Config'));
$this->debugbar->addCollector(new ConfigCollector((array)$this->config->get('plugins'), 'Plugins')); $this->debugbar->addCollector(new ConfigCollector($plugins_config, 'Plugins'));
$this->addMessage('Grav v' . GRAV_VERSION);
} }
return $this; return $this;
@@ -20,9 +20,6 @@ trait CompiledFile
*/ */
public function content($var = null) public function content($var = null)
{ {
// Set some options
$this->settings(['native' => true, 'compat' => true]);
try { try {
// If nothing has been loaded, attempt to get pre-compiled version of the file first. // If nothing has been loaded, attempt to get pre-compiled version of the file first.
if ($var === null && $this->raw === null && $this->content === null) { if ($var === null && $this->raw === null && $this->content === null) {
+8 -3
View File
@@ -13,7 +13,7 @@ use Grav\Common\Filesystem\Folder;
use Grav\Common\Inflector; use Grav\Common\Inflector;
use Grav\Common\Iterator; use Grav\Common\Iterator;
use Grav\Common\Utils; use Grav\Common\Utils;
use Symfony\Component\Yaml\Yaml; use RocketTheme\Toolbox\File\YamlFile;
class GPM extends Iterator class GPM extends Iterator
{ {
@@ -624,7 +624,10 @@ class GPM extends Iterator
return false; return false;
} }
$blueprint = (array)Yaml::parse(file_get_contents($blueprint_file)); $file = YamlFile::instance($blueprint_file);
$blueprint = (array)$file->content();
$file->free();
return $blueprint; return $blueprint;
} }
@@ -873,7 +876,9 @@ class GPM extends Iterator
// get currently installed version // get currently installed version
$locator = Grav::instance()['locator']; $locator = Grav::instance()['locator'];
$blueprints_path = $locator->findResource('plugins://' . $dependency_slug . DS . 'blueprints.yaml'); $blueprints_path = $locator->findResource('plugins://' . $dependency_slug . DS . 'blueprints.yaml');
$package_yaml = Yaml::parse(file_get_contents($blueprints_path)); $file = YamlFile::instance($blueprints_path);
$package_yaml = $file->content();
$file->free();
$currentlyInstalledVersion = $package_yaml['version']; $currentlyInstalledVersion = $package_yaml['version'];
// if requirement is next significant release, check is compatible with currently installed version, might not be // if requirement is next significant release, check is compatible with currently installed version, might not be
+1 -1
View File
@@ -114,7 +114,7 @@ class Licenses
{ {
if (!isset(self::$file)) { if (!isset(self::$file)) {
$path = Grav::instance()['locator']->findResource('user://data') . '/licenses.yaml';; $path = Grav::instance()['locator']->findResource('user://data') . '/licenses.yaml';
if (!file_exists($path)) { if (!file_exists($path)) {
touch($path); touch($path);
} }
+3 -7
View File
@@ -9,7 +9,6 @@
namespace Grav\Common; namespace Grav\Common;
use Grav\Common\Config\Config; use Grav\Common\Config\Config;
use Grav\Common\Language\Language;
use Grav\Common\Page\Medium\ImageMedium; use Grav\Common\Page\Medium\ImageMedium;
use Grav\Common\Page\Medium\Medium; use Grav\Common\Page\Medium\Medium;
use Grav\Common\Page\Page; use Grav\Common\Page\Page;
@@ -205,11 +204,8 @@ class Grav extends Container
*/ */
public function redirectLangSafe($route, $code = null) public function redirectLangSafe($route, $code = null)
{ {
/** @var Language $language */ if (!$this['uri']->isExternal($route)) {
$language = $this['language']; $this->redirect($this['pages']->route($route), $code);
if (!$this['uri']->isExternal($route) && $language->enabled() && $language->isIncludeDefaultLanguage()) {
$this->redirect($language->getLanguage() . $route, $code);
} else { } else {
$this->redirect($route, $code); $this->redirect($route, $code);
} }
@@ -443,7 +439,7 @@ class Grav extends Container
/** @var Config $config */ /** @var Config $config */
$config = $this['config']; $config = $this['config'];
$uri_extension = $uri->extension(); $uri_extension = strtolower($uri->extension());
$fallback_types = $config->get('system.media.allowed_fallback_types', null); $fallback_types = $config->get('system.media.allowed_fallback_types', null);
$supported_types = $config->get('media.types'); $supported_types = $config->get('media.types');
+4 -3
View File
@@ -190,10 +190,11 @@ class Inflector
public function hyphenize($word) public function hyphenize($word)
{ {
$regex1 = preg_replace('/([A-Z]+)([A-Z][a-z])/', '\1-\2', $word); $regex1 = preg_replace('/([A-Z]+)([A-Z][a-z])/', '\1-\2', $word);
$regex2 = preg_replace('/([a-zd])([A-Z])/', '\1-\2', $regex1); $regex2 = preg_replace('/([a-z])([A-Z])/', '\1-\2', $regex1);
$regex3 = preg_replace('/[^A-Z^a-z^0-9]+/', '-', $regex2); $regex3 = preg_replace('/([0-9])([A-Z])/', '\1-\2', $regex2);
$regex4 = preg_replace('/[^A-Z^a-z^0-9]+/', '-', $regex3);
return strtolower($regex3); return strtolower($regex4);
} }
/** /**
@@ -0,0 +1,9 @@
<?php
namespace Grav\Common\Media\Interfaces;
/**
* Class implements media collection interface.
*/
interface MediaCollectionInterface
{
}
@@ -0,0 +1,29 @@
<?php
namespace Grav\Common\Media\Interfaces;
/**
* Class implements media interface.
*/
interface MediaInterface
{
/**
* Gets the associated media collection.
*
* @return MediaCollectionInterface Collection of associated media.
*/
public function getMedia();
/**
* Get filesystem path to the associated media.
*
* @return string|null Media path or null if the object doesn't have media folder.
*/
public function getMediaFolder();
/**
* Get display order for the associated media.
*
* @return array Empty array means default ordering.
*/
public function getMediaOrder();
}
@@ -0,0 +1,9 @@
<?php
namespace Grav\Common\Media\Interfaces;
/**
* Class implements media object interface.
*/
interface MediaObjectInterface
{
}
@@ -0,0 +1,112 @@
<?php
namespace Grav\Common\Media\Traits;
use Grav\Common\Cache;
use Grav\Common\Grav;
use Grav\Common\Media\Interfaces\MediaCollectionInterface;
use Grav\Common\Page\Media;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
trait MediaTrait
{
protected $media;
/**
* Get filesystem path to the associated media.
*
* @return string|null
*/
abstract public function getMediaFolder();
/**
* Get display order for the associated media.
*
* @return array Empty array means default ordering.
*/
abstract public function getMediaOrder();
/**
* Get URI ot the associated media. Method will return null if path isn't URI.
*
* @return null|string
*/
public function getMediaUri()
{
$folder = $this->getMediaFolder();
if (strpos($folder, '://')) {
return $folder;
}
/** @var UniformResourceLocator $locator */
$locator = Grav::instance()['locator'];
$user = $locator->findResource('user://');
if (strpos($folder, $user) === 0) {
return 'user://' . substr($folder, strlen($user)+1);
}
return null;
}
/**
* Gets the associated media collection.
*
* @return MediaCollectionInterface Representation of associated media.
*/
public function getMedia()
{
$cache = $this->getMediaCache();
if ($this->media === null) {
// Use cached media if possible.
$cacheKey = md5('media' . $this->getCacheKey());
if (!$media = $cache->fetch($cacheKey)) {
$media = new Media($this->getMediaFolder(), $this->getMediaOrder());
$cache->save($cacheKey, $media);
}
$this->media = $media;
}
return $this->media;
}
/**
* Sets the associated media collection.
*
* @param MediaCollectionInterface $media Representation of associated media.
* @return $this
*/
protected function setMedia(MediaCollectionInterface $media)
{
$cache = $this->getMediaCache();
$cacheKey = md5('media' . $this->getCacheKey());
$cache->save($cacheKey, $media);
$this->media = $media;
return $this;
}
/**
* Clear media cache.
*/
protected function clearMediaCache()
{
$cache = $this->getMediaCache();
$cacheKey = md5('media' . $this->getCacheKey());
$cache->delete($cacheKey);
}
/**
* @return Cache
*/
protected function getMediaCache()
{
return Grav::instance()['cache'];
}
/**
* @return string
*/
abstract protected function getCacheKey();
}
@@ -0,0 +1,9 @@
<?php
namespace Grav\Common\Page\Interfaces;
/**
* Class implements page interface.
*/
interface PageInterface
{
}
+5 -3
View File
@@ -9,11 +9,11 @@
namespace Grav\Common\Page; namespace Grav\Common\Page;
use Grav\Common\Grav; use Grav\Common\Grav;
use Grav\Common\Yaml;
use Grav\Common\Page\Medium\AbstractMedia; use Grav\Common\Page\Medium\AbstractMedia;
use Grav\Common\Page\Medium\GlobalMedia; use Grav\Common\Page\Medium\GlobalMedia;
use Grav\Common\Page\Medium\MediumFactory; use Grav\Common\Page\Medium\MediumFactory;
use RocketTheme\Toolbox\File\File; use RocketTheme\Toolbox\File\File;
use Symfony\Component\Yaml\Yaml;
class Media extends AbstractMedia class Media extends AbstractMedia
{ {
@@ -24,11 +24,13 @@ class Media extends AbstractMedia
protected $standard_exif = ['FileSize', 'MimeType', 'height', 'width']; protected $standard_exif = ['FileSize', 'MimeType', 'height', 'width'];
/** /**
* @param $path * @param string $path
* @param array $media_order
*/ */
public function __construct($path) public function __construct($path, array $media_order = null)
{ {
$this->path = $path; $this->path = $path;
$this->media_order = $media_order;
$this->__wakeup(); $this->__wakeup();
$this->init(); $this->init();
@@ -10,9 +10,11 @@ namespace Grav\Common\Page\Medium;
use Grav\Common\Getters; use Grav\Common\Getters;
use Grav\Common\Grav; use Grav\Common\Grav;
use Grav\Common\Media\Interfaces\MediaCollectionInterface;
use Grav\Common\Media\Interfaces\MediaObjectInterface;
use Grav\Common\Utils; use Grav\Common\Utils;
abstract class AbstractMedia extends Getters abstract class AbstractMedia extends Getters implements MediaCollectionInterface
{ {
protected $gettersVariable = 'instances'; protected $gettersVariable = 'instances';
@@ -21,6 +23,7 @@ abstract class AbstractMedia extends Getters
protected $videos = []; protected $videos = [];
protected $audios = []; protected $audios = [];
protected $files = []; protected $files = [];
protected $media_order;
/** /**
* Get medium by filename. * Get medium by filename.
@@ -62,7 +65,7 @@ abstract class AbstractMedia extends Getters
/** /**
* Get a list of all media. * Get a list of all media.
* *
* @return array|Medium[] * @return array|MediaObjectInterface[]
*/ */
public function all() public function all()
{ {
@@ -74,7 +77,7 @@ abstract class AbstractMedia extends Getters
/** /**
* Get a list of all image media. * Get a list of all image media.
* *
* @return array|Medium[] * @return array|MediaObjectInterface[]
*/ */
public function images() public function images()
{ {
@@ -85,7 +88,7 @@ abstract class AbstractMedia extends Getters
/** /**
* Get a list of all video media. * Get a list of all video media.
* *
* @return array|Medium[] * @return array|MediaObjectInterface[]
*/ */
public function videos() public function videos()
{ {
@@ -96,7 +99,7 @@ abstract class AbstractMedia extends Getters
/** /**
* Get a list of all audio media. * Get a list of all audio media.
* *
* @return array|Medium[] * @return array|MediaObjectInterface[]
*/ */
public function audios() public function audios()
{ {
@@ -107,7 +110,7 @@ abstract class AbstractMedia extends Getters
/** /**
* Get a list of all file media. * Get a list of all file media.
* *
* @return array|Medium[] * @return array|MediaObjectInterface[]
*/ */
public function files() public function files()
{ {
@@ -117,7 +120,7 @@ abstract class AbstractMedia extends Getters
/** /**
* @param string $name * @param string $name
* @param Medium $file * @param MediaObjectInterface $file
*/ */
protected function add($name, $file) protected function add($name, $file)
{ {
@@ -145,14 +148,20 @@ abstract class AbstractMedia extends Getters
*/ */
protected function orderMedia($media) protected function orderMedia($media)
{ {
$page = Grav::instance()['pages']->get($this->path); if (null === $this->media_order) {
$page = Grav::instance()['pages']->get($this->path);
if ($page && isset($page->header()->media_order)) { if ($page && isset($page->header()->media_order)) {
$media_order = array_map('trim', explode(',', $page->header()->media_order)); $this->media_order = array_map('trim', explode(',', $page->header()->media_order));
$media = Utils::sortArrayByArray($media, $media_order); }
}
if (!empty($this->media_order) && is_array($this->media_order)) {
$media = Utils::sortArrayByArray($media, $this->media_order);
} else { } else {
ksort($media, SORT_NATURAL | SORT_FLAG_CASE); ksort($media, SORT_NATURAL | SORT_FLAG_CASE);
} }
return $media; return $media;
} }
@@ -11,6 +11,7 @@ namespace Grav\Common\Page\Medium;
use Grav\Common\Data\Blueprint; use Grav\Common\Data\Blueprint;
use Grav\Common\Grav; use Grav\Common\Grav;
use Grav\Common\Utils; use Grav\Common\Utils;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class ImageMedium extends Medium class ImageMedium extends Medium
{ {
@@ -164,12 +165,18 @@ class ImageMedium extends Medium
*/ */
public function url($reset = true) public function url($reset = true)
{ {
$image_path = Grav::instance()['locator']->findResource('cache://images', true); /** @var UniformResourceLocator $locator */
$image_dir = Grav::instance()['locator']->findResource('cache://images', false); $locator = Grav::instance()['locator'];
$image_path = $locator->findResource('cache://images', true);
$image_dir = $locator->findResource('cache://images', false);
$saved_image_path = $this->saveImage(); $saved_image_path = $this->saveImage();
$output = preg_replace('|^' . preg_quote(GRAV_ROOT, '|') . '|', '', $saved_image_path); $output = preg_replace('|^' . preg_quote(GRAV_ROOT, '|') . '|', '', $saved_image_path);
if ($locator->isStream($output)) {
$output = $locator->findResource($output, false);
}
if (Utils::startsWith($output, $image_path)) { if (Utils::startsWith($output, $image_path)) {
$output = '/' . $image_dir . preg_replace('|^' . preg_quote($image_path, '|') . '|', '', $output); $output = '/' . $image_dir . preg_replace('|^' . preg_quote($image_path, '|') . '|', '', $output);
} }
@@ -12,9 +12,9 @@ use Grav\Common\File\CompiledYamlFile;
use Grav\Common\Grav; use Grav\Common\Grav;
use Grav\Common\Data\Data; use Grav\Common\Data\Data;
use Grav\Common\Data\Blueprint; use Grav\Common\Data\Blueprint;
use Grav\Common\Utils; use Grav\Common\Media\Interfaces\MediaObjectInterface;
class Medium extends Data implements RenderableInterface class Medium extends Data implements RenderableInterface, MediaObjectInterface
{ {
use ParsedownHtmlTrait; use ParsedownHtmlTrait;
@@ -199,7 +199,12 @@ class Medium extends Data implements RenderableInterface
*/ */
public function url($reset = true) public function url($reset = true)
{ {
$output = preg_replace('|^' . preg_quote(GRAV_ROOT) . '|', '', $this->get('filepath')); $output = preg_replace('|^' . preg_quote(GRAV_ROOT, '|') . '|', '', $this->get('filepath'));
$locator = Grav::instance()['locator'];
if ($locator->isStream($output)) {
$output = $locator->findResource($output, false);
}
if ($reset) { if ($reset) {
$this->reset(); $this->reset();
@@ -94,6 +94,40 @@ class VideoMedium extends Medium
return $this; return $this;
} }
/**
* Allows to set the playsinline attribute
*
* @param bool $status
* @return $this
*/
public function playsinline($status = false)
{
if($status) {
$this->attributes['playsinline'] = true;
} else {
unset($this->attributes['playsinline']);
}
return $this;
}
/**
* Allows to set the muted attribute
*
* @param bool $status
* @return $this
*/
public function muted($status = false)
{
if($status) {
$this->attributes['muted'] = true;
} else {
unset($this->attributes['muted']);
}
return $this;
}
/** /**
* Reset medium. * Reset medium.
* *
+62 -52
View File
@@ -12,23 +12,26 @@ use Exception;
use Grav\Common\Cache; use Grav\Common\Cache;
use Grav\Common\Config\Config; use Grav\Common\Config\Config;
use Grav\Common\Data\Blueprint; use Grav\Common\Data\Blueprint;
use Grav\Common\File\CompiledYamlFile;
use Grav\Common\Filesystem\Folder; use Grav\Common\Filesystem\Folder;
use Grav\Common\Grav; use Grav\Common\Grav;
use Grav\Common\Language\Language;
use Grav\Common\Markdown\Parsedown; use Grav\Common\Markdown\Parsedown;
use Grav\Common\Markdown\ParsedownExtra; use Grav\Common\Markdown\ParsedownExtra;
use Grav\Common\Page\Interfaces\PageInterface;
use Grav\Common\Media\Traits\MediaTrait;
use Grav\Common\Taxonomy; use Grav\Common\Taxonomy;
use Grav\Common\Uri; use Grav\Common\Uri;
use Grav\Common\Utils; use Grav\Common\Utils;
use Grav\Common\Yaml;
use RocketTheme\Toolbox\Event\Event; use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\File\MarkdownFile; use RocketTheme\Toolbox\File\MarkdownFile;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Yaml;
define('PAGE_ORDER_PREFIX_REGEX', '/^[0-9]+\./u'); define('PAGE_ORDER_PREFIX_REGEX', '/^[0-9]+\./u');
class Page class Page implements PageInterface
{ {
use MediaTrait;
/** /**
* @var string Filename. Leave as null if page is folder. * @var string Filename. Leave as null if page is folder.
*/ */
@@ -65,7 +68,6 @@ class Page
protected $summary; protected $summary;
protected $raw_content; protected $raw_content;
protected $pagination; protected $pagination;
protected $media;
protected $metadata; protected $metadata;
protected $title; protected $title;
protected $max_count; protected $max_count;
@@ -318,8 +320,6 @@ class Page
if (!$this->header) { if (!$this->header) {
$file = $this->file(); $file = $this->file();
if ($file) { if ($file) {
// Set some options
$file->settings(['native' => true, 'compat' => true]);
try { try {
$this->raw_content = $file->markdown(); $this->raw_content = $file->markdown();
$this->frontmatter = $file->frontmatter(); $this->frontmatter = $file->frontmatter();
@@ -328,11 +328,12 @@ class Page
if (!Utils::isAdminPlugin()) { if (!Utils::isAdminPlugin()) {
// If there's a `frontmatter.yaml` file merge that in with the page header // If there's a `frontmatter.yaml` file merge that in with the page header
// note page's own frontmatter has precedence and will overwrite any defaults // note page's own frontmatter has precedence and will overwrite any defaults
$frontmatter_file = $this->path . '/' . $this->folder . '/frontmatter.yaml'; $frontmatterFile = CompiledYamlFile::instance($this->path . '/' . $this->folder . '/frontmatter.yaml');
if (file_exists($frontmatter_file)) { if ($frontmatterFile->exists()) {
$frontmatter_data = (array)Yaml::parse(file_get_contents($frontmatter_file)); $frontmatter_data = (array)$frontmatterFile->content();
$this->header = (object)array_replace_recursive($frontmatter_data, $this->header = (object)array_replace_recursive($frontmatter_data,
(array)$this->header); (array)$this->header);
$frontmatterFile->free();
} }
// Process frontmatter with Twig if enabled // Process frontmatter with Twig if enabled
if (Grav::instance()['config']->get('system.pages.frontmatter.process_twig') === true) { if (Grav::instance()['config']->get('system.pages.frontmatter.process_twig') === true) {
@@ -813,6 +814,8 @@ class Page
*/ */
public function setRawContent($content) public function setRawContent($content)
{ {
$content = $content === null ? '': $content;
$this->content = $content; $this->content = $content;
} }
@@ -1122,6 +1125,14 @@ class Page
return json_encode($this->toArray()); return json_encode($this->toArray());
} }
/**
* @return string
*/
protected function getCacheKey()
{
return $this->id();
}
/** /**
* Gets and sets the associated media as found in the page folder. * Gets and sets the associated media as found in the page folder.
* *
@@ -1131,23 +1142,33 @@ class Page
*/ */
public function media($var = null) public function media($var = null)
{ {
/** @var Cache $cache */
$cache = Grav::instance()['cache'];
if ($var) { if ($var) {
$this->media = $var; $this->setMedia($var);
}
if ($this->media === null) {
// Use cached media if possible.
$media_cache_id = md5('media' . $this->id());
if (!$media = $cache->fetch($media_cache_id)) {
$media = new Media($this->path());
$cache->save($media_cache_id, $media);
}
$this->media = $media;
} }
return $this->media; return $this->getMedia();
}
/**
* Get filesystem path to the associated media.
*
* @return string|null
*/
public function getMediaFolder()
{
return $this->path();
}
/**
* Get display order for the associated media.
*
* @return array Empty array means default ordering.
*/
public function getMediaOrder()
{
$header = $this->header();
return isset($header->media_order) ? array_map('trim', explode(',', (string)$header->media_order)) : [];
} }
/** /**
@@ -1626,14 +1647,19 @@ class Page
* Gets the url for the Page. * Gets the url for the Page.
* *
* @param bool $include_host Defaults false, but true would include http://yourhost.com * @param bool $include_host Defaults false, but true would include http://yourhost.com
* @param bool $canonical true to return the canonical URL * @param bool $canonical True to return the canonical URL
* @param bool $include_lang * @param bool $include_base Include base url on multisite as well as language code
* @param bool $raw_route * @param bool $raw_route
* *
* @return string The url. * @return string The url.
*/ */
public function url($include_host = false, $canonical = false, $include_lang = true, $raw_route = false) public function url($include_host = false, $canonical = false, $include_base = true, $raw_route = false)
{ {
// Override any URL when external_url is set
if (isset($this->external_url)) {
return $this->external_url;
}
$grav = Grav::instance(); $grav = Grav::instance();
/** @var Pages $pages */ /** @var Pages $pages */
@@ -1642,41 +1668,25 @@ class Page
/** @var Config $config */ /** @var Config $config */
$config = $grav['config']; $config = $grav['config'];
/** @var Language $language */ // get base route (multisite base and language)
$language = $grav['language']; $route = $include_base ? $pages->baseRoute() : '';
/** @var Uri $uri */
$uri = $grav['uri'];
// Override any URL when external_url is set
if (isset($this->external_url)) {
return $this->external_url;
}
// get pre-route
if ($include_lang && $language->enabled()) {
$pre_route = $language->getLanguageURLPrefix();
} else {
$pre_route = '';
}
// add full route if configured to do so // add full route if configured to do so
if ($config->get('system.absolute_urls', false)) { if (!$include_host && $config->get('system.absolute_urls', false)) {
$include_host = true; $include_host = true;
} }
// get canonical route if requested
if ($canonical) { if ($canonical) {
$route = $pre_route . $this->routeCanonical(); $route .= $this->routeCanonical();
} elseif ($raw_route) { } elseif ($raw_route) {
$route = $pre_route . $this->rawRoute(); $route .= $this->rawRoute();
} else { } else {
$route = $pre_route . $this->route(); $route .= $this->route();
} }
$rootUrl = $uri->rootUrl($include_host) . $pages->base(); /** @var Uri $uri */
$uri = $grav['uri'];
$url = $rootUrl . '/' . trim($route, '/') . $this->urlExtension(); $url = $uri->rootUrl($include_host) . '/' . trim($route, '/') . $this->urlExtension();
// trim trailing / if not root // trim trailing / if not root
if ($url !== '/') { if ($url !== '/') {
+50 -28
View File
@@ -49,7 +49,7 @@ class Pages
/** /**
* @var array|string[] * @var array|string[]
*/ */
protected $baseUrl = []; protected $baseRoute = [];
/** /**
* @var array|string[] * @var array|string[]
@@ -120,47 +120,69 @@ class Pages
if ($path !== null) { if ($path !== null) {
$path = trim($path, '/'); $path = trim($path, '/');
$this->base = $path ? '/' . $path : null; $this->base = $path ? '/' . $path : null;
$this->baseUrl = []; $this->baseRoute = [];
} }
return $this->base; return $this->base;
} }
/**
*
* Get base route for Grav pages.
*
* @param string $lang Optional language code for multilingual routes.
*
* @return string
*/
public function baseRoute($lang = null)
{
$key = $lang ?: 'default';
if (!isset($this->baseRoute[$key])) {
/** @var Language $language */
$language = $this->grav['language'];
$path_base = rtrim($this->base(), '/');
$path_lang = $language->enabled() ? $language->getLanguageURLPrefix($lang) : '';
$this->baseRoute[$key] = $path_base . $path_lang;
}
return $this->baseRoute[$key];
}
/**
*
* Get route for Grav site.
*
* @param string $route Optional route to the page.
* @param string $lang Optional language code for multilingual links.
*
* @return string
*/
public function route($route = '/', $lang = null)
{
if (!$route || $route === '/') {
return $this->baseRoute($lang) ?: '/';
}
return $this->baseRoute($lang) . $route;
}
/** /**
* *
* Get base URL for Grav pages. * Get base URL for Grav pages.
* *
* @param string $lang Optional language code for multilingual links. * @param string $lang Optional language code for multilingual links.
* @param bool $absolute If true, return absolute url, if false, return relative url. Otherwise return default. * @param bool|null $absolute If true, return absolute url, if false, return relative url. Otherwise return default.
* *
* @return string * @return string
*/ */
public function baseUrl($lang = null, $absolute = null) public function baseUrl($lang = null, $absolute = null)
{ {
$lang = (string) $lang;
$type = $absolute === null ? 'base_url' : ($absolute ? 'base_url_absolute' : 'base_url_relative'); $type = $absolute === null ? 'base_url' : ($absolute ? 'base_url_absolute' : 'base_url_relative');
$key = "{$lang} {$type}";
if (!isset($this->baseUrl[$key])) { return $this->grav[$type] . $this->baseRoute($lang);
/** @var Config $config */
$config = $this->grav['config'];
/** @var Language $language */
$language = $this->grav['language'];
if (!$lang) {
$lang = $language->getActive();
}
$path_append = rtrim($this->grav['pages']->base(), '/');
if ($language->getDefault() !== $lang || $config->get('system.languages.include_default_lang') === true) {
$path_append .= $lang ? '/' . $lang : '';
}
$this->baseUrl[$key] = $this->grav[$type] . $path_append;
}
return $this->baseUrl[$key];
} }
/** /**
@@ -179,7 +201,7 @@ class Pages
/** /**
* *
* Get home URL for Grav site. * Get URL for Grav site.
* *
* @param string $route Optional route to the page. * @param string $route Optional route to the page.
* @param string $lang Optional language code for multilingual links. * @param string $lang Optional language code for multilingual links.
@@ -189,7 +211,7 @@ class Pages
*/ */
public function url($route = '/', $lang = null, $absolute = null) public function url($route = '/', $lang = null, $absolute = null)
{ {
if ($route === '/') { if (!$route || $route === '/') {
return $this->homeUrl($lang, $absolute); return $this->homeUrl($lang, $absolute);
} }
@@ -8,6 +8,10 @@
namespace Grav\Common\Processors; namespace Grav\Common\Processors;
use Grav\Common\Config\Config;
use Grav\Common\Uri;
use Grav\Common\Utils;
class InitializeProcessor extends ProcessorBase implements ProcessorInterface class InitializeProcessor extends ProcessorBase implements ProcessorInterface
{ {
public $id = 'init'; public $id = 'init';
@@ -15,29 +19,36 @@ class InitializeProcessor extends ProcessorBase implements ProcessorInterface
public function process() public function process()
{ {
$this->container['config']->debug(); /** @var Config $config */
$config = $this->container['config'];
$config->debug();
// Use output buffering to prevent headers from being sent too early. // Use output buffering to prevent headers from being sent too early.
ob_start(); ob_start();
if ($this->container['config']->get('system.cache.gzip')) { if ($config->get('system.cache.gzip') && !@ob_start('ob_gzhandler')) {
// Enable zip/deflate with a fallback in case of if browser does not support compressing. // Enable zip/deflate with a fallback in case of if browser does not support compressing.
if (!@ob_start("ob_gzhandler")) { ob_start();
ob_start();
}
} }
// Initialize the timezone. // Initialize the timezone.
if ($this->container['config']->get('system.timezone')) { if ($config->get('system.timezone')) {
date_default_timezone_set($this->container['config']->get('system.timezone')); date_default_timezone_set($this->container['config']->get('system.timezone'));
} }
// FIXME: Initialize session should happen later after plugins have been loaded. This is a workaround to fix session issues in AWS. // FIXME: Initialize session should happen later after plugins have been loaded. This is a workaround to fix session issues in AWS.
if ($this->container['config']->get('system.session.initialize', 1) && isset($this->container['session'])) { if (isset($this->container['session']) && $config->get('system.session.initialize', true)) {
$this->container['session']->init(); $this->container['session']->init();
} }
// Initialize uri. /** @var Uri $uri */
$this->container['uri']->init(); $uri = $this->container['uri'];
$uri->init();
// Redirect pages with trailing slash if configured to do so.
$path = $uri->path() ?: '/';
if ($path !== '/' && $config->get('system.pages.redirect_trailing_slash', false) && Utils::endsWith($path, '/')) {
$this->container->redirect(rtrim($path, '/'), 302);
}
$this->container->setLocale(); $this->container->setLocale();
} }
@@ -16,6 +16,7 @@ use Grav\Common\Config\ConfigFileFinder;
use Grav\Common\Config\Setup; use Grav\Common\Config\Setup;
use Pimple\Container; use Pimple\Container;
use Pimple\ServiceProviderInterface; use Pimple\ServiceProviderInterface;
use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator; use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class ConfigServiceProvider implements ServiceProviderInterface class ConfigServiceProvider implements ServiceProviderInterface
@@ -31,7 +32,14 @@ class ConfigServiceProvider implements ServiceProviderInterface
}; };
$container['config'] = function ($c) { $container['config'] = function ($c) {
return static::load($c); $config = static::load($c);
// After configuration has been loaded, we can disable YAML compatibility if strict mode has been enabled.
if (!$config->get('system.strict_mode.yaml_compat', true)) {
YamlFile::globalSettings(['compat' => false, 'native' => true]);
}
return $config;
}; };
$container['languages'] = function ($c) { $container['languages'] = function ($c) {
@@ -65,6 +73,10 @@ class ConfigServiceProvider implements ServiceProviderInterface
return $blueprints->name("master-{$setup->environment}")->load(); return $blueprints->name("master-{$setup->environment}")->load();
} }
/**
* @param Container $container
* @return Config
*/
public static function load(Container $container) public static function load(Container $container)
{ {
/** Setup $setup */ /** Setup $setup */
@@ -8,6 +8,7 @@
namespace Grav\Common\Service; namespace Grav\Common\Service;
use Grav\Common\Config\Config;
use Grav\Common\Grav; use Grav\Common\Grav;
use Grav\Common\Language\Language; use Grav\Common\Language\Language;
use Grav\Common\Page\Page; use Grav\Common\Page\Page;
@@ -26,35 +27,33 @@ class PageServiceProvider implements ServiceProviderInterface
/** @var Pages $pages */ /** @var Pages $pages */
$pages = $c['pages']; $pages = $c['pages'];
/** @var Config $config */
$config = $c['config'];
/** @var Uri $uri */ /** @var Uri $uri */
$uri = $c['uri']; $uri = $c['uri'];
$path = $uri->path(); // Don't trim to support trailing slash default routes $path = $uri->path() ?: '/'; // Don't trim to support trailing slash default routes
$path = $path ?: '/';
$page = $pages->dispatch($path); $page = $pages->dispatch($path);
// Redirection tests // Redirection tests
if ($page) { if ($page) {
/** @var Language $language */
$language = $c['language'];
// some debugger override logic // some debugger override logic
if ($page->debugger() === false) { if ($page->debugger() === false) {
$c['debugger']->enabled(false); $c['debugger']->enabled(false);
} }
if ($c['config']->get('system.force_ssl')) { if ($config->get('system.force_ssl')) {
if (!isset($_SERVER['HTTPS']) || $_SERVER["HTTPS"] != "on") { if (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] !== 'on') {
$url = "https://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"]; $url = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
$c->redirect($url); $c->redirect($url);
} }
} }
$url = $page->route(); $url = $pages->route($page->route());
if ($uri->params()) { if ($uri->params()) {
if ($url == '/') { //Avoid double slash if ($url === '/') { //Avoid double slash
$url = $uri->params(); $url = $uri->params();
} else { } else {
$url .= $uri->params(); $url .= $uri->params();
@@ -67,18 +66,16 @@ class PageServiceProvider implements ServiceProviderInterface
$url .= '#' . $uri->fragment(); $url .= '#' . $uri->fragment();
} }
/** @var Language $language */
$language = $c['language'];
// Language-specific redirection scenarios // Language-specific redirection scenarios
if ($language->enabled()) { if ($language->enabled() && ($language->isLanguageInUrl() xor $language->isIncludeDefaultLanguage())) {
if ($language->isLanguageInUrl() && !$language->isIncludeDefaultLanguage()) { $c->redirect($url);
$c->redirect($url);
}
if (!$language->isLanguageInUrl() && $language->isIncludeDefaultLanguage()) {
$c->redirectLangSafe($url);
}
} }
// Default route test and redirect // Default route test and redirect
if ($c['config']->get('system.pages.redirect_default_route') && $page->route() != $path) { if ($config->get('system.pages.redirect_default_route') && $page->route() !== $path) {
$c->redirectLangSafe($url); $c->redirect($url);
} }
} }
@@ -29,21 +29,22 @@ class SessionServiceProvider implements ServiceProviderInterface
/** @var Uri $uri */ /** @var Uri $uri */
$uri = $c['uri']; $uri = $c['uri'];
// Get session parameters.
$session_timeout = (int)$config->get('system.session.timeout', 1800);
$session_path = $config->get('system.session.path');
if (null === $session_path) {
$session_path = '/' . ltrim(Uri::filterPath($uri->rootUrl(false)), '/');
}
$domain = $uri->host();
if ($domain === 'localhost') {
$domain = '';
}
// Get session options. // Get session options.
$secure = (bool)$config->get('system.session.secure', false);
$httponly = (bool)$config->get('system.session.httponly', true);
$enabled = (bool)$config->get('system.session.enabled', false); $enabled = (bool)$config->get('system.session.enabled', false);
$cookie_secure = (bool)$config->get('system.session.secure', false);
$cookie_httponly = (bool)$config->get('system.session.httponly', true);
$cookie_lifetime = (int)$config->get('system.session.timeout', 1800);
$cookie_path = $config->get('system.session.path');
if (null === $cookie_path) {
$cookie_path = '/' . trim(Uri::filterPath($uri->rootUrl(false)), '/');
}
// Session cookie path requires trailing slash.
$cookie_path = rtrim($cookie_path, '/') . '/';
$cookie_domain = $uri->host();
if ($cookie_domain === 'localhost') {
$cookie_domain = '';
}
// Activate admin if we're inside the admin path. // Activate admin if we're inside the admin path.
$is_admin = false; $is_admin = false;
@@ -56,14 +57,14 @@ class SessionServiceProvider implements ServiceProviderInterface
// Check no language, simple language prefix (en) and region specific language prefix (en-US). // Check no language, simple language prefix (en) and region specific language prefix (en-US).
$pos = strpos($current_route, $base); $pos = strpos($current_route, $base);
if ($pos === 0 || $pos === 3 || $pos === 6) { if ($pos === 0 || $pos === 3 || $pos === 6) {
$session_timeout = $config->get('plugins.admin.session.timeout', 1800); $cookie_lifetime = $config->get('plugins.admin.session.timeout', 1800);
$enabled = $is_admin = true; $enabled = $is_admin = true;
} }
} }
// Fix for HUGE session timeouts. // Fix for HUGE session timeouts.
if ($session_timeout > 99999999999) { if ($cookie_lifetime > 99999999999) {
$session_timeout = 9999999999; $cookie_lifetime = 9999999999;
} }
$inflector = new Inflector(); $inflector = new Inflector();
@@ -73,10 +74,16 @@ class SessionServiceProvider implements ServiceProviderInterface
} }
// Define session service. // Define session service.
$session = new Session($session_timeout, $session_path, $domain); $options = [
$session->setName($session_name); 'name' => $session_name,
$session->setSecure($secure); 'cookie_lifetime' => $cookie_lifetime,
$session->setHttpOnly($httponly); 'cookie_path' => $cookie_path,
'cookie_domain' => $cookie_domain,
'cookie_secure' => $cookie_secure,
'cookie_httponly' => $cookie_httponly
] + (array) $config->get('system.session.options');
$session = new Session($options);
$session->setAutoStart($enabled); $session->setAutoStart($enabled);
return $session; return $session;
+17 -38
View File
@@ -8,34 +8,18 @@
namespace Grav\Common; namespace Grav\Common;
use RocketTheme\Toolbox\Session\Session as BaseSession; class Session extends \Grav\Framework\Session\Session
class Session extends BaseSession
{ {
/** @var bool */ /** @var bool */
protected $autoStart = false; protected $autoStart = false;
protected $lifetime;
protected $path;
protected $domain;
protected $secure;
protected $httpOnly;
/** /**
* @param int $lifetime Defaults to 1800 seconds. * @return \Grav\Framework\Session\Session
* @param string $path Cookie path. * @deprecated 1.5
* @param string $domain Optional, domain for the session
* @throws \RuntimeException
*/ */
public function __construct($lifetime, $path, $domain = null) public static function instance()
{ {
$this->lifetime = $lifetime; return static::getInstance();
$this->path = $path;
$this->domain = $domain;
if (php_sapi_name() !== 'cli') {
parent::__construct($lifetime, $path, $domain);
}
} }
/** /**
@@ -48,9 +32,6 @@ class Session extends BaseSession
if ($this->autoStart) { if ($this->autoStart) {
$this->start(); $this->start();
// TODO: This setcookie shouldn't be here, session should by itself be able to update its cookie.
setcookie(session_name(), session_id(), $this->lifetime ? time() + $this->lifetime : 0, $this->path, $this->domain, $this->secure, $this->httpOnly);
$this->autoStart = false; $this->autoStart = false;
} }
} }
@@ -67,27 +48,25 @@ class Session extends BaseSession
} }
/** /**
* @param bool $secure * Returns attributes.
* @return $this *
* @return array Attributes
* @deprecated 1.5
*/ */
public function setSecure($secure) public function all()
{ {
$this->secure = $secure; return $this->getAll();
ini_set('session.cookie_secure', (bool)$secure);
return $this;
} }
/** /**
* @param bool $httpOnly * Checks if the session was started.
* @return $this *
* @return Boolean
* @deprecated 1.5
*/ */
public function setHttpOnly($httpOnly) public function started()
{ {
$this->httpOnly = $httpOnly; return $this->isStarted();
ini_set('session.cookie_httponly', (bool)$httpOnly);
return $this;
} }
/** /**
@@ -12,7 +12,7 @@ class TwigNodeMarkdown extends \Twig_Node implements \Twig_NodeOutputInterface
{ {
public function __construct(\Twig_Node $body, $lineno, $tag = 'markdown') public function __construct(\Twig_Node $body, $lineno, $tag = 'markdown')
{ {
parent::__construct(array('body' => $body), array(), $lineno, $tag); parent::__construct(['body' => $body], [], $lineno, $tag);
} }
/** /**
* Compiles the node to PHP. * Compiles the node to PHP.
@@ -14,7 +14,7 @@ class TwigNodeScript extends \Twig_Node implements \Twig_NodeOutputInterface
/** /**
* TwigNodeScript constructor. * TwigNodeScript constructor.
* @param \Twig_NodeInterface|null $body * @param \Twig_Node|null $body
* @param \Twig_Node_Expression|null $file * @param \Twig_Node_Expression|null $file
* @param \Twig_Node_Expression|null $group * @param \Twig_Node_Expression|null $group
* @param \Twig_Node_Expression|null $priority * @param \Twig_Node_Expression|null $priority
@@ -23,12 +23,12 @@ class TwigNodeScript extends \Twig_Node implements \Twig_NodeOutputInterface
* @param string|null $tag * @param string|null $tag
*/ */
public function __construct( public function __construct(
\Twig_NodeInterface $body = null, \Twig_Node $body = null,
\Twig_Node_Expression $file = null, \Twig_Node_Expression $file = null,
\Twig_Node_Expression $group = null, \Twig_Node_Expression $group = null,
\Twig_Node_Expression $priority = null, \Twig_Node_Expression $priority = null,
\Twig_Node_Expression $attributes = null, \Twig_Node_Expression $attributes = null,
$lineno, $lineno = 0,
$tag = null $tag = null
) )
{ {
@@ -14,18 +14,18 @@ class TwigNodeStyle extends \Twig_Node implements \Twig_NodeOutputInterface
/** /**
* TwigNodeAssets constructor. * TwigNodeAssets constructor.
* @param \Twig_NodeInterface|null $body * @param \Twig_Node|null $body
* @param \Twig_Node_Expression|null $attributes * @param \Twig_Node_Expression|null $attributes
* @param int $lineno * @param int $lineno
* @param null $tag * @param null $tag
*/ */
public function __construct( public function __construct(
\Twig_NodeInterface $body = null, \Twig_Node $body = null,
\Twig_Node_Expression $file = null, \Twig_Node_Expression $file = null,
\Twig_Node_Expression $group = null, \Twig_Node_Expression $group = null,
\Twig_Node_Expression $priority = null, \Twig_Node_Expression $priority = null,
\Twig_Node_Expression $attributes = null, \Twig_Node_Expression $attributes = null,
$lineno, $lineno = 0,
$tag = null $tag = null
) )
{ {
@@ -10,7 +10,13 @@ namespace Grav\Common\Twig\Node;
class TwigNodeSwitch extends \Twig_Node implements \Twig_NodeOutputInterface class TwigNodeSwitch extends \Twig_Node implements \Twig_NodeOutputInterface
{ {
public function __construct(\Twig_NodeInterface $value, \Twig_NodeInterface $cases, \Twig_NodeInterface $default = null, $lineno, $tag = null) public function __construct(
\Twig_Node $value,
\Twig_Node $cases,
\Twig_Node $default = null,
$lineno = 0,
$tag = null
)
{ {
parent::__construct(array('value' => $value, 'cases' => $cases, 'default' => $default), array(), $lineno, $tag); parent::__construct(array('value' => $value, 'cases' => $cases, 'default' => $default), array(), $lineno, $tag);
} }
@@ -24,20 +30,17 @@ class TwigNodeSwitch extends \Twig_Node implements \Twig_NodeOutputInterface
{ {
$compiler $compiler
->addDebugInfo($this) ->addDebugInfo($this)
->write("switch (") ->write('switch (')
->subcompile($this->getNode('value')) ->subcompile($this->getNode('value'))
->raw(") {\n") ->raw(") {\n")
->indent(); ->indent();
foreach ($this->getNode('cases') as $case) foreach ($this->getNode('cases') as $case) {
{ if (!$case->hasNode('body')) {
if (!$case->hasNode('body'))
{
continue; continue;
} }
foreach ($case->getNode('values') as $value) foreach ($case->getNode('values') as $value) {
{
$compiler $compiler
->write('case ') ->write('case ')
->subcompile($value) ->subcompile($value)
@@ -53,8 +56,7 @@ class TwigNodeSwitch extends \Twig_Node implements \Twig_NodeOutputInterface
->write("}\n"); ->write("}\n");
} }
if ($this->hasNode('default') && $this->getNode('default') !== null) if ($this->hasNode('default') && $this->getNode('default') !== null) {
{
$compiler $compiler
->write("default:\n") ->write("default:\n")
->write("{\n") ->write("{\n")
@@ -10,7 +10,12 @@ namespace Grav\Common\Twig\Node;
class TwigNodeTryCatch extends \Twig_Node class TwigNodeTryCatch extends \Twig_Node
{ {
public function __construct(\Twig_NodeInterface $try, \Twig_NodeInterface $catch = null, $lineno, $tag = null) public function __construct(
\Twig_Node $try,
\Twig_Node $catch = null,
$lineno = 0,
$tag = null
)
{ {
parent::__construct(array('try' => $try, 'catch' => $catch), array(), $lineno, $tag); parent::__construct(array('try' => $try, 'catch' => $catch), array(), $lineno, $tag);
} }
@@ -27,7 +27,7 @@ class TwigTokenParserScript extends \Twig_TokenParser
* *
* @param \Twig_Token $token A Twig_Token instance * @param \Twig_Token $token A Twig_Token instance
* *
* @return \Twig_NodeInterface A Twig_NodeInterface instance * @return \Twig_Node A Twig_Node instance
*/ */
public function parse(\Twig_Token $token) public function parse(\Twig_Token $token)
{ {
@@ -26,7 +26,7 @@ class TwigTokenParserStyle extends \Twig_TokenParser
* *
* @param \Twig_Token $token A Twig_Token instance * @param \Twig_Token $token A Twig_Token instance
* *
* @return \Twig_NodeInterface A Twig_NodeInterface instance * @return \Twig_Node A Twig_Node instance
*/ */
public function parse(\Twig_Token $token) public function parse(\Twig_Token $token)
{ {
@@ -37,8 +37,7 @@ class TwigTokenParserSwitch extends \Twig_TokenParser
$stream->expect(\Twig_Token::BLOCK_END_TYPE); $stream->expect(\Twig_Token::BLOCK_END_TYPE);
// There can be some whitespace between the {% switch %} and first {% case %} tag. // There can be some whitespace between the {% switch %} and first {% case %} tag.
while ($stream->getCurrent()->getType() == \Twig_Token::TEXT_TYPE && trim($stream->getCurrent()->getValue()) == '') while ($stream->getCurrent()->getType() === \Twig_Token::TEXT_TYPE && trim($stream->getCurrent()->getValue()) === '') {
{
$stream->next(); $stream->next();
} }
@@ -47,56 +46,45 @@ class TwigTokenParserSwitch extends \Twig_TokenParser
$expressionParser = $this->parser->getExpressionParser(); $expressionParser = $this->parser->getExpressionParser();
$default = null; $default = null;
$cases = array(); $cases = [];
$end = false; $end = false;
while (!$end) while (!$end) {
{
$next = $stream->next(); $next = $stream->next();
switch ($next->getValue()) switch ($next->getValue()) {
{
case 'case': case 'case':
{ $values = [];
$values = array();
while (true) while (true) {
{ $values[] = $expressionParser->parsePrimaryExpression();
$values[] = $expressionParser->parsePrimaryExpression(); // Multiple allowed values?
// Multiple allowed values? if ($stream->test(\Twig_Token::OPERATOR_TYPE, 'or')) {
if ($stream->test(\Twig_Token::OPERATOR_TYPE, 'or')) $stream->next();
{ } else {
$stream->next(); break;
}
else
{
break;
}
} }
}
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
$body = $this->parser->subparse(array($this, 'decideIfFork'));
$cases[] = new \Twig_Node([
'values' => new \Twig_Node($values),
'body' => $body
]);
break;
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
$body = $this->parser->subparse(array($this, 'decideIfFork'));
$cases[] = new \Twig_Node(array(
'values' => new \Twig_Node($values),
'body' => $body
));
break;
}
case 'default': case 'default':
{ $stream->expect(\Twig_Token::BLOCK_END_TYPE);
$stream->expect(\Twig_Token::BLOCK_END_TYPE); $default = $this->parser->subparse(array($this, 'decideIfEnd'));
$default = $this->parser->subparse(array($this, 'decideIfEnd')); break;
break;
}
case 'endswitch': case 'endswitch':
{ $end = true;
$end = true; break;
break;
}
default: default:
{ throw new \Twig_Error_Syntax(sprintf('Unexpected end of template. Twig was looking for the following tags "case", "default", or "endswitch" to close the "switch" block started at line %d)', $lineno), -1);
throw new \Twig_Error_Syntax(sprintf('Unexpected end of template. Twig was looking for the following tags "case", "default", or "endswitch" to close the "switch" block started at line %d)', $lineno), -1);
}
} }
} }
@@ -127,7 +115,6 @@ class TwigTokenParserSwitch extends \Twig_TokenParser
return $token->test(array('endswitch')); return $token->test(array('endswitch'));
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
@@ -28,7 +28,7 @@ class TwigTokenParserTryCatch extends \Twig_TokenParser
* *
* @param \Twig_Token $token A Twig_Token instance * @param \Twig_Token $token A Twig_Token instance
* *
* @return \Twig_NodeInterface A Twig_NodeInterface instance * @return \Twig_Node A Twig_Node instance
*/ */
public function parse(\Twig_Token $token) public function parse(\Twig_Token $token)
{ {
+10 -7
View File
@@ -113,7 +113,10 @@ class Twig
$params['cache'] = new \Twig_Cache_Filesystem($cachePath, \Twig_Cache_Filesystem::FORCE_BYTECODE_INVALIDATION); $params['cache'] = new \Twig_Cache_Filesystem($cachePath, \Twig_Cache_Filesystem::FORCE_BYTECODE_INVALIDATION);
} }
if (!empty($this->autoescape)) { if (!$config->get('system.strict_mode.twig_compat', true)) {
// Force autoescape on for all files if in strict mode.
$params['autoescape'] = true;
} elseif (!empty($this->autoescape)) {
$params['autoescape'] = $this->autoescape; $params['autoescape'] = $this->autoescape;
} }
@@ -122,10 +125,10 @@ class Twig
if ($config->get('system.twig.undefined_functions')) { if ($config->get('system.twig.undefined_functions')) {
$this->twig->registerUndefinedFunctionCallback(function ($name) { $this->twig->registerUndefinedFunctionCallback(function ($name) {
if (function_exists($name)) { if (function_exists($name)) {
return new \Twig_Function_Function($name); return new \Twig_SimpleFunction($name, $name);
} }
return new \Twig_Function_Function(function () { return new \Twig_SimpleFunction($name, function () {
}); });
}); });
} }
@@ -133,10 +136,10 @@ class Twig
if ($config->get('system.twig.undefined_filters')) { if ($config->get('system.twig.undefined_filters')) {
$this->twig->registerUndefinedFilterCallback(function ($name) { $this->twig->registerUndefinedFilterCallback(function ($name) {
if (function_exists($name)) { if (function_exists($name)) {
return new \Twig_Filter_Function($name); return new \Twig_SimpleFilter($name, $name);
} }
return new \Twig_Filter_Function(function () { return new \Twig_SimpleFilter($name, function () {
}); });
}); });
} }
@@ -145,7 +148,7 @@ class Twig
// set default date format if set in config // set default date format if set in config
if ($config->get('system.pages.dateformat.long')) { if ($config->get('system.pages.dateformat.long')) {
$this->twig->getExtension('core')->setDateFormat($config->get('system.pages.dateformat.long')); $this->twig->getExtension('Twig_Extension_Core')->setDateFormat($config->get('system.pages.dateformat.long'));
} }
// enable the debug extension if required // enable the debug extension if required
if ($config->get('system.twig.debug')) { if ($config->get('system.twig.debug')) {
@@ -159,7 +162,7 @@ class Twig
$pages = $this->grav['pages']; $pages = $this->grav['pages'];
// Set some standard variables for twig // Set some standard variables for twig
$this->twig_vars = $this->twig_vars + [ $this->twig_vars += [
'config' => $config, 'config' => $config,
'system' => $config->get('system'), 'system' => $config->get('system'),
'theme' => $config->get('theme'), 'theme' => $config->get('theme'),
+81 -28
View File
@@ -18,11 +18,11 @@ use Grav\Common\Twig\TokenParser\TwigTokenParserTryCatch;
use Grav\Common\Twig\TokenParser\TwigTokenParserMarkdown; use Grav\Common\Twig\TokenParser\TwigTokenParserMarkdown;
use Grav\Common\User\User; use Grav\Common\User\User;
use Grav\Common\Utils; use Grav\Common\Utils;
use Grav\Common\Yaml;
use Grav\Common\Markdown\Parsedown; use Grav\Common\Markdown\Parsedown;
use Grav\Common\Markdown\ParsedownExtra; use Grav\Common\Markdown\ParsedownExtra;
use Grav\Common\Helpers\Base32; use Grav\Common\Helpers\Base32;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator; use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use Symfony\Component\Yaml\Yaml;
class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsInterface class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsInterface
{ {
@@ -72,7 +72,7 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
new \Twig_SimpleFilter('fieldName', [$this, 'fieldNameFilter']), new \Twig_SimpleFilter('fieldName', [$this, 'fieldNameFilter']),
new \Twig_SimpleFilter('ksort', [$this, 'ksortFilter']), new \Twig_SimpleFilter('ksort', [$this, 'ksortFilter']),
new \Twig_SimpleFilter('ltrim', [$this, 'ltrimFilter']), new \Twig_SimpleFilter('ltrim', [$this, 'ltrimFilter']),
new \Twig_SimpleFilter('markdown', [$this, 'markdownFunction']), new \Twig_SimpleFilter('markdown', [$this, 'markdownFunction'], ['is_safe' => ['html']]),
new \Twig_SimpleFilter('md5', [$this, 'md5Filter']), new \Twig_SimpleFilter('md5', [$this, 'md5Filter']),
new \Twig_SimpleFilter('base32_encode', [$this, 'base32EncodeFilter']), new \Twig_SimpleFilter('base32_encode', [$this, 'base32EncodeFilter']),
new \Twig_SimpleFilter('base32_decode', [$this, 'base32DecodeFilter']), new \Twig_SimpleFilter('base32_decode', [$this, 'base32DecodeFilter']),
@@ -88,9 +88,6 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
new \Twig_SimpleFilter('safe_truncate_html', ['\Grav\Common\Utils', 'safeTruncateHTML']), new \Twig_SimpleFilter('safe_truncate_html', ['\Grav\Common\Utils', 'safeTruncateHTML']),
new \Twig_SimpleFilter('sort_by_key', [$this, 'sortByKeyFilter']), new \Twig_SimpleFilter('sort_by_key', [$this, 'sortByKeyFilter']),
new \Twig_SimpleFilter('starts_with', [$this, 'startsWithFilter']), new \Twig_SimpleFilter('starts_with', [$this, 'startsWithFilter']),
new \Twig_SimpleFilter('t', [$this, 'translate']),
new \Twig_SimpleFilter('tl', [$this, 'translateLanguage']),
new \Twig_SimpleFilter('ta', [$this, 'translateArray']),
new \Twig_SimpleFilter('truncate', ['\Grav\Common\Utils', 'truncate']), new \Twig_SimpleFilter('truncate', ['\Grav\Common\Utils', 'truncate']),
new \Twig_SimpleFilter('truncate_html', ['\Grav\Common\Utils', 'truncateHTML']), new \Twig_SimpleFilter('truncate_html', ['\Grav\Common\Utils', 'truncateHTML']),
new \Twig_SimpleFilter('json_decode', [$this, 'jsonDecodeFilter']), new \Twig_SimpleFilter('json_decode', [$this, 'jsonDecodeFilter']),
@@ -100,6 +97,18 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
new \Twig_SimpleFilter('print_r', 'print_r'), new \Twig_SimpleFilter('print_r', 'print_r'),
new \Twig_SimpleFilter('yaml_encode', [$this, 'yamlEncodeFilter']), new \Twig_SimpleFilter('yaml_encode', [$this, 'yamlEncodeFilter']),
new \Twig_SimpleFilter('yaml_decode', [$this, 'yamlDecodeFilter']), new \Twig_SimpleFilter('yaml_decode', [$this, 'yamlDecodeFilter']),
// Translations
new \Twig_SimpleFilter('t', [$this, 'translate']),
new \Twig_SimpleFilter('tl', [$this, 'translateLanguage']),
new \Twig_SimpleFilter('ta', [$this, 'translateArray']),
// Casting values
new \Twig_SimpleFilter('string', [$this, 'stringFilter']),
new \Twig_SimpleFilter('int', [$this, 'intFilter'], ['is_safe' => true]),
new \Twig_SimpleFilter('bool', [$this, 'boolFilter']),
new \Twig_SimpleFilter('float', [$this, 'floatFilter'], ['is_safe' => true]),
new \Twig_SimpleFilter('array', [$this, 'arrayFilter']),
]; ];
} }
@@ -111,7 +120,7 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
public function getFunctions() public function getFunctions()
{ {
return [ return [
new \Twig_SimpleFunction('array', [$this, 'arrayFunc']), new \Twig_SimpleFunction('array', [$this, 'arrayFilter']),
new \Twig_SimpleFunction('array_key_value', [$this, 'arrayKeyValueFunc']), new \Twig_SimpleFunction('array_key_value', [$this, 'arrayKeyValueFunc']),
new \Twig_SimpleFunction('array_key_exists', 'array_key_exists'), new \Twig_SimpleFunction('array_key_exists', 'array_key_exists'),
new \Twig_SimpleFunction('array_unique', 'array_unique'), new \Twig_SimpleFunction('array_unique', 'array_unique'),
@@ -132,9 +141,6 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
new \Twig_SimpleFunction('regex_replace', [$this, 'regexReplace']), new \Twig_SimpleFunction('regex_replace', [$this, 'regexReplace']),
new \Twig_SimpleFunction('regex_filter', [$this, 'regexFilter']), new \Twig_SimpleFunction('regex_filter', [$this, 'regexFilter']),
new \Twig_SimpleFunction('string', [$this, 'stringFunc']), new \Twig_SimpleFunction('string', [$this, 'stringFunc']),
new \Twig_simpleFunction('t', [$this, 'translate']),
new \Twig_simpleFunction('tl', [$this, 'translateLanguage']),
new \Twig_simpleFunction('ta', [$this, 'translateArray']),
new \Twig_SimpleFunction('url', [$this, 'urlFunc']), new \Twig_SimpleFunction('url', [$this, 'urlFunc']),
new \Twig_SimpleFunction('json_decode', [$this, 'jsonDecodeFilter']), new \Twig_SimpleFunction('json_decode', [$this, 'jsonDecodeFilter']),
new \Twig_SimpleFunction('get_cookie', [$this, 'getCookie']), new \Twig_SimpleFunction('get_cookie', [$this, 'getCookie']),
@@ -151,6 +157,10 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
new \Twig_SimpleFunction('nicefilesize', [$this, 'niceFilesizeFunc']), new \Twig_SimpleFunction('nicefilesize', [$this, 'niceFilesizeFunc']),
new \Twig_SimpleFunction('nicetime', [$this, 'nicetimeFilter']), new \Twig_SimpleFunction('nicetime', [$this, 'nicetimeFilter']),
// Translations
new \Twig_simpleFunction('t', [$this, 'translate']),
new \Twig_simpleFunction('tl', [$this, 'translateLanguage']),
new \Twig_simpleFunction('ta', [$this, 'translateArray']),
]; ];
} }
@@ -617,6 +627,62 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
return ltrim($value, $chars); return ltrim($value, $chars);
} }
/**
* Casts input to string.
*
* @param mixed $input
* @return string
*/
public function stringFilter($input)
{
return (string) $input;
}
/**
* Casts input to int.
*
* @param mixed $input
* @return int
*/
public function intFilter($input)
{
return (int) $input;
}
/**
* Casts input to bool.
*
* @param mixed $input
* @return bool
*/
public function boolFilter($input)
{
return (bool) $input;
}
/**
* Casts input to float.
*
* @param mixed $input
* @return float
*/
public function floatFilter($input)
{
return (float) $input;
}
/**
* Casts input to array.
*
* @param mixed $input
* @return array
*/
public function arrayFilter($input)
{
return (array) $input;
}
/** /**
* @return mixed * @return mixed
*/ */
@@ -693,7 +759,6 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
$template = $env->createTemplate($twig); $template = $env->createTemplate($twig);
return $template->render($context); return $template->render($context);
;
} }
/** /**
@@ -748,7 +813,7 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
* Output a Gist * Output a Gist
* *
* @param string $id * @param string $id
* @param string $file * @param string|bool $file
* *
* @return string * @return string
*/ */
@@ -788,19 +853,6 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
return str_pad($input, (int)$pad_length, $pad_string, $pad_type); return str_pad($input, (int)$pad_length, $pad_string, $pad_type);
} }
/**
* Cast a value to array
*
* @param $value
*
* @return array
*/
public function arrayFunc($value)
{
return (array)$value;
}
/** /**
* Workaround for twig associative array initialization * Workaround for twig associative array initialization
* Returns a key => val array * Returns a key => val array
@@ -976,7 +1028,7 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
public function redirectFunc($url, $statusCode = 303) public function redirectFunc($url, $statusCode = 303)
{ {
header('Location: ' . $url, true, $statusCode); header('Location: ' . $url, true, $statusCode);
die(); exit();
} }
/** /**
@@ -1060,7 +1112,7 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
if (file_exists($filepath)) { if (file_exists($filepath)) {
return file_get_contents($filepath); return file_get_contents($filepath);
} }
return false; return false;
} }
@@ -1245,11 +1297,12 @@ class TwigExtension extends \Twig_Extension implements \Twig_Extension_GlobalsIn
* Dump/Encode data into YAML format * Dump/Encode data into YAML format
* *
* @param $data * @param $data
* @param $inline integer number of levels of inline syntax
* @return mixed * @return mixed
*/ */
public function yamlEncodeFilter($data) public function yamlEncodeFilter($data, $inline = 10)
{ {
return Yaml::dump($data, 10); return Yaml::dump($data, $inline);
} }
/** /**
+41 -30
View File
@@ -11,6 +11,7 @@ namespace Grav\Common;
use Grav\Common\Config\Config; use Grav\Common\Config\Config;
use Grav\Common\Language\Language; use Grav\Common\Language\Language;
use Grav\Common\Page\Page; use Grav\Common\Page\Page;
use Grav\Common\Page\Pages;
use Grav\Framework\Route\RouteFactory; use Grav\Framework\Route\RouteFactory;
use Grav\Framework\Uri\UriFactory; use Grav\Framework\Uri\UriFactory;
use Grav\Framework\Uri\UriPartsFilter; use Grav\Framework\Uri\UriPartsFilter;
@@ -156,12 +157,6 @@ class Uri
$uri = preg_replace('|^' . preg_quote($setup_base, '|') . '|', '', $uri); $uri = preg_replace('|^' . preg_quote($setup_base, '|') . '|', '', $uri);
} }
// If configured to, redirect trailing slash URI's with a 302 redirect
$redirect = str_replace($this->root, '', rtrim($uri, '/'));
if ($redirect && $uri !== '/' && $redirect !== $this->base() && $config->get('system.pages.redirect_trailing_slash', false) && Utils::endsWith($uri, '/')) {
$grav->redirect($redirect, 302);
}
// process params // process params
$uri = $this->processParams($uri, $config->get('system.param_sep')); $uri = $this->processParams($uri, $config->get('system.param_sep'));
@@ -206,9 +201,9 @@ class Uri
} }
// Set some Grav stuff // Set some Grav stuff
$grav['base_url_absolute'] = $grav['config']->get('system.custom_base_url') ?: $this->rootUrl(true); $grav['base_url_absolute'] = $config->get('system.custom_base_url') ?: $this->rootUrl(true);
$grav['base_url_relative'] = $this->rootUrl(false); $grav['base_url_relative'] = $this->rootUrl(false);
$grav['base_url'] = $grav['config']->get('system.absolute_urls') ? $grav['base_url_absolute'] : $grav['base_url_relative']; $grav['base_url'] = $config->get('system.absolute_urls') ? $grav['base_url_absolute'] : $grav['base_url_relative'];
RouteFactory::setRoot($this->root_path); RouteFactory::setRoot($this->root_path);
RouteFactory::setLanguage($language->getLanguageURLPrefix()); RouteFactory::setLanguage($language->getLanguageURLPrefix());
@@ -376,6 +371,17 @@ class Uri
return $this->extension; return $this->extension;
} }
public function method()
{
$method = isset($_SERVER['REQUEST_METHOD']) ? strtoupper($_SERVER['REQUEST_METHOD']) : 'GET';
if ($method === 'POST' && isset($_SERVER['X-HTTP-METHOD-OVERRIDE'])) {
$method = strtoupper($_SERVER['X-HTTP-METHOD-OVERRIDE']);
}
return $method;
}
/** /**
* Return the scheme of the URI * Return the scheme of the URI
* *
@@ -481,11 +487,9 @@ class Uri
{ {
if ($include_root) { if ($include_root) {
return $this->uri; return $this->uri;
} else {
$uri = str_replace($this->root_path, '', $this->uri);
return $uri;
} }
return str_replace($this->root_path, '', $this->uri);
} }
/** /**
@@ -508,16 +512,10 @@ class Uri
{ {
$grav = Grav::instance(); $grav = Grav::instance();
// Link processing should prepend language /** @var Pages $pages */
$language = $grav['language']; $pages = $grav['pages'];
$language_append = '';
if ($language->enabled()) {
$language_append = $language->getLanguageURLPrefix();
}
$base = $grav['base_url_relative']; return $pages->baseUrl(null, false);
return rtrim($base . $grav['pages']->base(), '/') . $language_append;
} }
/** /**
@@ -633,10 +631,9 @@ class Uri
} }
return $ip; return $ip;
} }
/**
/**
* Returns current Uri. * Returns current Uri.
* *
* @return \Grav\Framework\Uri\Uri * @return \Grav\Framework\Uri\Uri
@@ -883,7 +880,26 @@ class Uri
public static function parseUrl($url) public static function parseUrl($url)
{ {
$grav = Grav::instance(); $grav = Grav::instance();
$parts = parse_url($url);
$encodedUrl = preg_replace_callback(
'%[^:/@?&=#]+%usD',
function ($matches) { return rawurlencode($matches[0]); },
$url
);
$parts = parse_url($encodedUrl);
if (false === $parts) {
return false;
}
foreach($parts as $name => $value) {
$parts[$name] = rawurldecode($value);
}
if (!isset($parts['path'])) {
$parts['path'] = '';
}
list($stripped_path, $params) = static::extractParams($parts['path'], $grav['config']->get('system.param_sep')); list($stripped_path, $params) = static::extractParams($parts['path'], $grav['config']->get('system.param_sep'));
@@ -1262,7 +1278,7 @@ class Uri
{ {
if (!$this->post) { if (!$this->post) {
$content_type = $this->getContentType(); $content_type = $this->getContentType();
if ($content_type == 'application/json') { if ($content_type === 'application/json') {
$json = file_get_contents('php://input'); $json = file_get_contents('php://input');
$this->post = json_decode($json, true); $this->post = json_decode($json, true);
} elseif (!empty($_POST)) { } elseif (!empty($_POST)) {
@@ -1270,7 +1286,7 @@ class Uri
} }
} }
if ($this->post && !is_null($element)) { if ($this->post && null !== $element) {
$item = Utils::getDotNotation($this->post, $element); $item = Utils::getDotNotation($this->post, $element);
if ($filter_type) { if ($filter_type) {
$item = filter_var($item, $filter_type); $item = filter_var($item, $filter_type);
@@ -1320,11 +1336,6 @@ class Uri
$scriptPath = str_replace('\\', '/', $_SERVER['PHP_SELF']); $scriptPath = str_replace('\\', '/', $_SERVER['PHP_SELF']);
$rootPath = str_replace(' ', '%20', rtrim(substr($scriptPath, 0, strpos($scriptPath, 'index.php')), '/')); $rootPath = str_replace(' ', '%20', rtrim(substr($scriptPath, 0, strpos($scriptPath, 'index.php')), '/'));
// check if userdir in the path and workaround PHP bug with PHP_SELF
if (strpos($this->uri, '/~') !== false && strpos($scriptPath, '/~') === false) {
$rootPath = substr($this->uri, 0, strpos($this->uri, '/', 1)) . $rootPath;
}
return $rootPath; return $rootPath;
} }
+28 -62
View File
@@ -45,8 +45,20 @@ abstract class Utils
/** @var UniformResourceLocator $locator */ /** @var UniformResourceLocator $locator */
$locator = Grav::instance()['locator']; $locator = Grav::instance()['locator'];
// Get relative path to the resource (or false if not found). $parts = Uri::parseUrl($input);
$resource = $locator->findResource($input, false);
if ($parts) {
$resource = $locator->findResource("{$parts['scheme']}://{$parts['host']}{$parts['path']}", false);
if (isset($parts['query'])) {
$resource = $resource . '?' . $parts['query'];
}
} else {
// Not a valid URL (can still be a stream).
$resource = $locator->findResource($input, false);
}
} else { } else {
$resource = $input; $resource = $input;
} }
@@ -262,7 +274,7 @@ abstract class Utils
// is $break present between $limit and the end of the string? // is $break present between $limit and the end of the string?
if ($up_to_break && false !== ($breakpoint = mb_strpos($string, $break, $limit))) { if ($up_to_break && false !== ($breakpoint = mb_strpos($string, $break, $limit))) {
if ($breakpoint < mb_strlen($string) - 1) { if ($breakpoint < mb_strlen($string) - 1) {
$string = mb_substr($string, 0, $breakpoint) . $break; $string = mb_substr($string, 0, $breakpoint) . $pad;
} }
} else { } else {
$string = mb_substr($string, 0, $limit) . $pad; $string = mb_substr($string, 0, $limit) . $pad;
@@ -705,11 +717,11 @@ abstract class Utils
* with reverse proxy setups. * with reverse proxy setups.
* *
* @param string $action * @param string $action
* @param bool $plusOneTick if true, generates the token for the next tick (the next 12 hours) * @param bool $previousTick if true, generates the token for the previous tick (the previous 12 hours)
* *
* @return string the nonce string * @return string the nonce string
*/ */
private static function generateNonceString($action, $plusOneTick = false) private static function generateNonceString($action, $previousTick = false)
{ {
$username = ''; $username = '';
if (isset(Grav::instance()['user'])) { if (isset(Grav::instance()['user'])) {
@@ -720,29 +732,8 @@ abstract class Utils
$token = session_id(); $token = session_id();
$i = self::nonceTick(); $i = self::nonceTick();
if ($plusOneTick) { if ($previousTick) {
$i++; $i--;
}
return ($i . '|' . $action . '|' . $username . '|' . $token . '|' . Grav::instance()['config']->get('security.salt'));
}
//Added in version 1.0.8 to ensure that existing nonces are not broken.
private static function generateNonceStringOldStyle($action, $plusOneTick = false)
{
if (isset(Grav::instance()['user'])) {
$user = Grav::instance()['user'];
$username = $user->username;
if (isset($_SERVER['REMOTE_ADDR'])) {
$username .= $_SERVER['REMOTE_ADDR'];
}
} else {
$username = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '';
}
$token = session_id();
$i = self::nonceTick();
if ($plusOneTick) {
$i++;
} }
return ($i . '|' . $action . '|' . $username . '|' . $token . '|' . Grav::instance()['config']->get('security.salt')); return ($i . '|' . $action . '|' . $username . '|' . $token . '|' . Grav::instance()['config']->get('security.salt'));
@@ -768,33 +759,20 @@ abstract class Utils
* action is the same for 12 hours. * action is the same for 12 hours.
* *
* @param string $action the action the nonce is tied to (e.g. save-user-admin or move-page-homepage) * @param string $action the action the nonce is tied to (e.g. save-user-admin or move-page-homepage)
* @param bool $plusOneTick if true, generates the token for the next tick (the next 12 hours) * @param bool $previousTick if true, generates the token for the previous tick (the previous 12 hours)
* *
* @return string the nonce * @return string the nonce
*/ */
public static function getNonce($action, $plusOneTick = false) public static function getNonce($action, $previousTick = false)
{ {
// Don't regenerate this again if not needed // Don't regenerate this again if not needed
if (isset(static::$nonces[$action])) { if (isset(static::$nonces[$action][$previousTick])) {
return static::$nonces[$action]; return static::$nonces[$action][$previousTick];
} }
$nonce = md5(self::generateNonceString($action, $plusOneTick)); $nonce = md5(self::generateNonceString($action, $previousTick));
static::$nonces[$action] = $nonce; static::$nonces[$action][$previousTick] = $nonce;
return static::$nonces[$action]; return static::$nonces[$action][$previousTick];
}
//Added in version 1.0.8 to ensure that existing nonces are not broken.
public static function getNonceOldStyle($action, $plusOneTick = false)
{
// Don't regenerate this again if not needed
if (isset(static::$nonces[$action])) {
return static::$nonces[$action];
}
$nonce = md5(self::generateNonceStringOldStyle($action, $plusOneTick));
static::$nonces[$action] = $nonce;
return static::$nonces[$action];
} }
/** /**
@@ -818,20 +796,8 @@ abstract class Utils
} }
//Nonce generated 12-24 hours ago //Nonce generated 12-24 hours ago
$plusOneTick = true; $previousTick = true;
if ($nonce === self::getNonce($action, $plusOneTick)) { if ($nonce === self::getNonce($action, $previousTick)) {
return true;
}
//Added in version 1.0.8 to ensure that existing nonces are not broken.
//Nonce generated 0-12 hours ago
if ($nonce === self::getNonceOldStyle($action)) {
return true;
}
//Nonce generated 12-24 hours ago
$plusOneTick = true;
if ($nonce === self::getNonceOldStyle($action, $plusOneTick)) {
return true; return true;
} }
+47
View File
@@ -0,0 +1,47 @@
<?php
/**
* @package Grav.Common
*
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common;
use Grav\Framework\File\Formatter\YamlFormatter;
abstract class Yaml
{
/** @var YamlFormatter */
private static $yaml;
public static function parse($data)
{
if (null === static::$yaml) {
static::init();
}
return static::$yaml->decode($data);
}
public static function dump($data, $inline = null, $indent = null)
{
if (null === static::$yaml) {
static::init();
}
return static::$yaml->encode($data, $inline, $indent);
}
private static function init()
{
$config = [
'inline' => 5,
'indent' => 2,
'native' => true,
'compat' => true
];
static::$yaml = new YamlFormatter($config);
}
}
+25 -13
View File
@@ -9,9 +9,9 @@
namespace Grav\Console\Cli; namespace Grav\Console\Cli;
use Grav\Console\ConsoleCommand; use Grav\Console\ConsoleCommand;
use RocketTheme\Toolbox\File\YamlFile;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Yaml\Yaml;
class InstallCommand extends ConsoleCommand class InstallCommand extends ConsoleCommand
{ {
@@ -71,9 +71,9 @@ class InstallCommand extends ConsoleCommand
// Look for dependencies file in ROOT and USER dir // Look for dependencies file in ROOT and USER dir
if (file_exists($this->user_path . $dependencies_file)) { if (file_exists($this->user_path . $dependencies_file)) {
$this->config = Yaml::parse(file_get_contents($this->user_path . $dependencies_file)); $file = YamlFile::instance($this->user_path . $dependencies_file);
} elseif (file_exists($this->destination . $dependencies_file)) { } elseif (file_exists($this->destination . $dependencies_file)) {
$this->config = Yaml::parse(file_get_contents($this->destination . $dependencies_file)); $file = YamlFile::instance($this->destination . $dependencies_file);
} else { } else {
$this->output->writeln('<red>ERROR</red> Missing .dependencies file in <cyan>user/</cyan> folder'); $this->output->writeln('<red>ERROR</red> Missing .dependencies file in <cyan>user/</cyan> folder');
if ($this->input->getArgument('destination')) { if ($this->input->getArgument('destination')) {
@@ -85,6 +85,9 @@ class InstallCommand extends ConsoleCommand
return; return;
} }
$this->config = $file->content();
$file->free();
// If yaml config, process // If yaml config, process
if ($this->config) { if ($this->config) {
if (!$this->input->getOption('symlink')) { if (!$this->input->getOption('symlink')) {
@@ -153,23 +156,32 @@ class InstallCommand extends ConsoleCommand
exec('cd ' . $this->destination); exec('cd ' . $this->destination);
foreach ($this->config['links'] as $repo => $data) { foreach ($this->config['links'] as $repo => $data) {
$from = $this->local_config[$data['scm'] . '_repos'] . $data['src']; $repos = (array) $this->local_config[$data['scm'] . '_repos'];
$from = false;
$to = $this->destination . $data['path']; $to = $this->destination . $data['path'];
if (file_exists($from)) { foreach ($repos as $repo) {
if (!file_exists($to)) { $path = $repo . $data['src'];
symlink($from, $to); if (file_exists($path)) {
$this->output->writeln('<green>SUCCESS</green> symlinked <magenta>' . $data['src'] . '</magenta> -> <cyan>' . $data['path'] . '</cyan>'); $from = $path;
$this->output->writeln(''); continue;
} else {
$this->output->writeln('<red>destination: ' . $to . ' already exists, skipping...</red>');
$this->output->writeln('');
} }
} else { }
if (!$from) {
$this->output->writeln('<red>source: ' . $from . ' does not exists, skipping...</red>'); $this->output->writeln('<red>source: ' . $from . ' does not exists, skipping...</red>');
$this->output->writeln(''); $this->output->writeln('');
} }
if (!file_exists($to)) {
symlink($from, $to);
$this->output->writeln('<green>SUCCESS</green> symlinked <magenta>' . $data['src'] . '</magenta> -> <cyan>' . $data['path'] . '</cyan>');
$this->output->writeln('');
} else {
$this->output->writeln('<red>destination: ' . $to . ' already exists, skipping...</red>');
$this->output->writeln('');
}
} }
} }
} }
+4 -2
View File
@@ -12,11 +12,11 @@ use Grav\Common\Grav;
use Grav\Common\Composer; use Grav\Common\Composer;
use Grav\Common\GravTrait; use Grav\Common\GravTrait;
use Grav\Console\Cli\ClearCacheCommand; use Grav\Console\Cli\ClearCacheCommand;
use RocketTheme\Toolbox\File\YamlFile;
use Symfony\Component\Console\Formatter\OutputFormatterStyle; use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Yaml\Yaml;
trait ConsoleTrait trait ConsoleTrait
{ {
@@ -123,7 +123,9 @@ trait ConsoleTrait
$local_config_file = $home_folder . '/.grav/config'; $local_config_file = $home_folder . '/.grav/config';
if (file_exists($local_config_file)) { if (file_exists($local_config_file)) {
$this->local_config = Yaml::parse(file_get_contents($local_config_file)); $file = YamlFile::instance($local_config_file);
$this->local_config = $file->content();
$file->free();
return $local_config_file; return $local_config_file;
} }
@@ -444,18 +444,21 @@ class InstallCommand extends ConsoleCommand
{ {
$matches = $this->getGitRegexMatches($package); $matches = $this->getGitRegexMatches($package);
foreach ($this->local_config as $path) { foreach ($this->local_config as $paths) {
if (Utils::endsWith($matches[2], '.git')) { if (Utils::endsWith($matches[2], '.git')) {
$repo_dir = preg_replace('/\.git$/', '', $matches[2]); $repo_dir = preg_replace('/\.git$/', '', $matches[2]);
} else { } else {
$repo_dir = $matches[2]; $repo_dir = $matches[2];
} }
$from = rtrim($path, '/') . '/' . $repo_dir; $paths = (array) $paths;
foreach ($paths as $repo) {
if (file_exists($from)) { $path = rtrim($repo, '/') . '/' . $repo_dir;
return $from; if (file_exists($path)) {
return $path;
}
} }
} }
return false; return false;
@@ -11,9 +11,9 @@ namespace Grav\Console\Gpm;
use Grav\Common\GPM\GPM; use Grav\Common\GPM\GPM;
use Grav\Common\GPM\Upgrader; use Grav\Common\GPM\Upgrader;
use Grav\Console\ConsoleCommand; use Grav\Console\ConsoleCommand;
use RocketTheme\Toolbox\File\YamlFile;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Yaml\Yaml;
class VersionCommand extends ConsoleCommand class VersionCommand extends ConsoleCommand
{ {
@@ -84,7 +84,10 @@ class VersionCommand extends ConsoleCommand
} }
} }
$package_yaml = Yaml::parse(file_get_contents($blueprints_path)); $file = YamlFile::instance($blueprints_path);
$package_yaml = $file->content();
$file->free();
$version = $package_yaml['version']; $version = $package_yaml['version'];
if (!$version) { if (!$version) {
+18 -9
View File
@@ -16,21 +16,18 @@ use Grav\Framework\Cache\Exception\InvalidArgumentException;
*/ */
trait CacheTrait trait CacheTrait
{ {
/** /** @var string */
* @var string
*/
private $namespace = ''; private $namespace = '';
/** /** @var int|null */
* @var int|null
*/
private $defaultLifetime = null; private $defaultLifetime = null;
/** /** @var \stdClass */
* @var \stdClass
*/
private $miss; private $miss;
/** @var bool */
private $validation = true;
/** /**
* Always call from constructor. * Always call from constructor.
* *
@@ -45,6 +42,14 @@ trait CacheTrait
$this->miss = new \stdClass; $this->miss = new \stdClass;
} }
/**
* @param $validation
*/
public function setValidation($validation)
{
$this->validation = (bool) $validation;
}
/** /**
* @return string * @return string
*/ */
@@ -307,6 +312,10 @@ trait CacheTrait
*/ */
protected function validateKeys($keys) protected function validateKeys($keys)
{ {
if (!$this->validation) {
return;
}
foreach ($keys as $key) { foreach ($keys as $key) {
$this->validateKey($key); $this->validateKey($key);
} }
@@ -24,11 +24,6 @@ class ArrayCollection extends BaseArrayCollection implements CollectionInterface
*/ */
public function reverse() public function reverse()
{ {
// TODO: remove when PHP 5.6 is minimum (with doctrine/collections v1.4).
if (!method_exists($this, 'createFrom')) {
return new static(array_reverse($this->toArray()));
}
return $this->createFrom(array_reverse($this->toArray())); return $this->createFrom(array_reverse($this->toArray()));
} }
@@ -42,11 +37,6 @@ class ArrayCollection extends BaseArrayCollection implements CollectionInterface
$keys = $this->getKeys(); $keys = $this->getKeys();
shuffle($keys); shuffle($keys);
// TODO: remove when PHP 5.6 is minimum (with doctrine/collections v1.4).
if (!method_exists($this, 'createFrom')) {
return new static(array_replace(array_flip($keys), $this->toArray()));
}
return $this->createFrom(array_replace(array_flip($keys), $this->toArray())); return $this->createFrom(array_replace(array_flip($keys), $this->toArray()));
} }
@@ -27,6 +27,7 @@ class ContentBlock implements ContentBlockInterface
protected $tokenTemplate = '@@BLOCK-%s@@'; protected $tokenTemplate = '@@BLOCK-%s@@';
protected $content = ''; protected $content = '';
protected $blocks = []; protected $blocks = [];
protected $checksum;
/** /**
* @param string $id * @param string $id
@@ -40,6 +41,7 @@ class ContentBlock implements ContentBlockInterface
/** /**
* @param array $serialized * @param array $serialized
* @return ContentBlockInterface * @return ContentBlockInterface
* @throws \InvalidArgumentException
*/ */
public static function fromArray(array $serialized) public static function fromArray(array $serialized)
{ {
@@ -48,14 +50,14 @@ class ContentBlock implements ContentBlockInterface
$id = isset($serialized['id']) ? $serialized['id'] : null; $id = isset($serialized['id']) ? $serialized['id'] : null;
if (!$type || !$id || !is_a($type, 'Grav\Framework\ContentBlock\ContentBlockInterface', true)) { if (!$type || !$id || !is_a($type, 'Grav\Framework\ContentBlock\ContentBlockInterface', true)) {
throw new \RuntimeException('Bad data'); throw new \InvalidArgumentException('Bad data');
} }
/** @var ContentBlockInterface $instance */ /** @var ContentBlockInterface $instance */
$instance = new $type($id); $instance = new $type($id);
$instance->build($serialized); $instance->build($serialized);
} catch (\Exception $e) { } catch (\Exception $e) {
throw new \RuntimeException(sprintf('Cannot unserialize Block: %s', $e->getMessage()), $e->getCode(), $e); throw new \InvalidArgumentException(sprintf('Cannot unserialize Block: %s', $e->getMessage()), $e->getCode(), $e);
} }
return $instance; return $instance;
@@ -104,9 +106,13 @@ class ContentBlock implements ContentBlockInterface
$array = [ $array = [
'_type' => get_class($this), '_type' => get_class($this),
'_version' => $this->version, '_version' => $this->version,
'id' => $this->id, 'id' => $this->id
]; ];
if ($this->checksum) {
$array['checksum'] = $this->checksum;
}
if ($this->content) { if ($this->content) {
$array['content'] = $this->content; $array['content'] = $this->content;
} }
@@ -158,6 +164,7 @@ class ContentBlock implements ContentBlockInterface
$this->checkVersion($serialized); $this->checkVersion($serialized);
$this->id = isset($serialized['id']) ? $serialized['id'] : $this->generateId(); $this->id = isset($serialized['id']) ? $serialized['id'] : $this->generateId();
$this->checksum = isset($serialized['checksum']) ? $serialized['checksum'] : null;
if (isset($serialized['content'])) { if (isset($serialized['content'])) {
$this->setContent($serialized['content']); $this->setContent($serialized['content']);
@@ -169,6 +176,25 @@ class ContentBlock implements ContentBlockInterface
} }
} }
/**
* @param string $checksum
* @return $this
*/
public function setChecksum($checksum)
{
$this->checksum = $checksum;
return $this;
}
/**
* @return string
*/
public function getChecksum()
{
return $this->checksum;
}
/** /**
* @param string $content * @param string $content
* @return $this * @return $this
@@ -222,7 +248,7 @@ class ContentBlock implements ContentBlockInterface
*/ */
protected function checkVersion(array $serialized) protected function checkVersion(array $serialized)
{ {
$version = isset($serialized['_version']) ? (string) $serialized['_version'] : '1'; $version = isset($serialized['_version']) ? (int) $serialized['_version'] : 1;
if ($version !== $this->version) { if ($version !== $this->version) {
throw new \RuntimeException(sprintf('Unsupported version %s', $version)); throw new \RuntimeException(sprintf('Unsupported version %s', $version));
} }
@@ -61,6 +61,17 @@ interface ContentBlockInterface extends \Serializable
*/ */
public function build(array $serialized); public function build(array $serialized);
/**
* @param string $checksum
* @return $this
*/
public function setChecksum($checksum);
/**
* @return string
*/
public function getChecksum();
/** /**
* @param string $content * @param string $content
* @return $this * @return $this
@@ -15,6 +15,7 @@ namespace Grav\Framework\ContentBlock;
*/ */
class HtmlBlock extends ContentBlock implements HtmlBlockInterface class HtmlBlock extends ContentBlock implements HtmlBlockInterface
{ {
protected $version = 1;
protected $frameworks = []; protected $frameworks = [];
protected $styles = []; protected $styles = [];
protected $scripts = []; protected $scripts = [];
@@ -0,0 +1,44 @@
<?php
/**
* @package Grav\Framework\File\Formatter
*
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Framework\File\Formatter;
interface FormatterInterface
{
/**
* Get default file extension from current formatter (with dot).
*
* Default file extension is the first defined extension.
*
* @return string File extension (can be empty).
*/
public function getDefaultFileExtension();
/**
* Get file extensions supported by current formatter (with dot).
*
* @return string[]
*/
public function getSupportedFileExtensions();
/**
* Encode data into a string.
*
* @param array $data
* @return string
*/
public function encode($data);
/**
* Decode a string into data.
*
* @param string $data
* @return array
*/
public function decode($data);
}
@@ -0,0 +1,83 @@
<?php
/**
* @package Grav\Framework\File\Formatter
*
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Framework\File\Formatter;
class IniFormatter implements FormatterInterface
{
/** @var array */
private $config;
/**
* IniFormatter constructor.
* @param array $config
*/
public function __construct(array $config = [])
{
$this->config = $config + [
'file_extension' => '.ini'
];
}
/**
* @deprecated 1.5 Use $formatter->getDefaultFileExtension() instead.
*/
public function getFileExtension()
{
return $this->getDefaultFileExtension();
}
/**
* {@inheritdoc}
*/
public function getDefaultFileExtension()
{
$extensions = $this->getSupportedFileExtensions();
return (string) reset($extensions);
}
/**
* {@inheritdoc}
*/
public function getSupportedFileExtensions()
{
return (array) $this->config['file_extension'];
}
/**
* {@inheritdoc}
*/
public function encode($data)
{
$string = '';
foreach ($data as $key => $value) {
$string .= $key . '="' . preg_replace(
['/"/', '/\\\/', "/\t/", "/\n/", "/\r/"],
['\"', '\\\\', '\t', '\n', '\r'],
$value
) . "\"\n";
}
return $string;
}
/**
* {@inheritdoc}
*/
public function decode($data)
{
$decoded = @parse_ini_string($data);
if ($decoded === false) {
throw new \RuntimeException('Decoding INI failed');
}
return $decoded;
}
}
@@ -0,0 +1,78 @@
<?php
/**
* @package Grav\Framework\File\Formatter
*
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Framework\File\Formatter;
class JsonFormatter implements FormatterInterface
{
/** @var array */
private $config;
public function __construct(array $config = [])
{
$this->config = $config + [
'file_extension' => '.json',
'encode_options' => 0,
'decode_assoc' => true
];
}
/**
* @deprecated 1.5 Use $formatter->getDefaultFileExtension() instead.
*/
public function getFileExtension()
{
return $this->getDefaultFileExtension();
}
/**
* {@inheritdoc}
*/
public function getDefaultFileExtension()
{
$extensions = $this->getSupportedFileExtensions();
return (string) reset($extensions);
}
/**
* {@inheritdoc}
*/
public function getSupportedFileExtensions()
{
return (array) $this->config['file_extension'];
}
/**
* {@inheritdoc}
*/
public function encode($data)
{
$encoded = @json_encode($data, $this->config['encode_options']);
if ($encoded === false) {
throw new \RuntimeException('Encoding JSON failed');
}
return $encoded;
}
/**
* {@inheritdoc}
*/
public function decode($data)
{
$decoded = @json_decode($data, $this->config['decode_assoc']);
if ($decoded === false) {
throw new \RuntimeException('Decoding JSON failed');
}
return $decoded;
}
}
@@ -0,0 +1,116 @@
<?php
/**
* @package Grav\Framework\File\Formatter
*
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Framework\File\Formatter;
class MarkdownFormatter implements FormatterInterface
{
/** @var array */
private $config;
/** @var FormatterInterface */
private $headerFormatter;
public function __construct(array $config = [], FormatterInterface $headerFormatter = null)
{
$this->config = $config + [
'file_extension' => '.md',
'header' => 'header',
'body' => 'markdown',
'raw' => 'frontmatter',
'yaml' => ['inline' => 20]
];
$this->headerFormatter = $headerFormatter ?: new YamlFormatter($this->config['yaml']);
}
/**
* @deprecated 1.5 Use $formatter->getDefaultFileExtension() instead.
*/
public function getFileExtension()
{
return $this->getDefaultFileExtension();
}
/**
* {@inheritdoc}
*/
public function getDefaultFileExtension()
{
$extensions = $this->getSupportedFileExtensions();
return (string) reset($extensions);
}
/**
* {@inheritdoc}
*/
public function getSupportedFileExtensions()
{
return (array) $this->config['file_extension'];
}
/**
* {@inheritdoc}
*/
public function encode($data)
{
$headerVar = $this->config['header'];
$bodyVar = $this->config['body'];
$header = isset($data[$headerVar]) ? (array) $data[$headerVar] : [];
$body = isset($data[$bodyVar]) ? (string) $data[$bodyVar] : '';
// Create Markdown file with YAML header.
$encoded = '';
if ($header) {
$encoded = "---\n" . trim($this->headerFormatter->encode($data['header'])) . "\n---\n\n";
}
$encoded .= $body;
// Normalize line endings to Unix style.
$encoded = preg_replace("/(\r\n|\r)/", "\n", $encoded);
return $encoded;
}
/**
* {@inheritdoc}
*/
public function decode($data)
{
$headerVar = $this->config['header'];
$bodyVar = $this->config['body'];
$rawVar = $this->config['raw'];
$content = [
$headerVar => [],
$bodyVar => ''
];
$headerRegex = "/^---\n(.+?)\n---\n{0,}(.*)$/uis";
// Normalize line endings to Unix style.
$data = preg_replace("/(\r\n|\r)/", "\n", $data);
// Parse header.
preg_match($headerRegex, ltrim($data), $matches);
if(empty($matches)) {
$content[$bodyVar] = $data;
} else {
// Normalize frontmatter.
$frontmatter = preg_replace("/\n\t/", "\n ", $matches[1]);
if ($rawVar) {
$content[$rawVar] = $frontmatter;
}
$content[$headerVar] = $this->headerFormatter->decode($frontmatter);
$content[$bodyVar] = $matches[2];
}
return $content;
}
}
@@ -0,0 +1,96 @@
<?php
/**
* @package Grav\Framework\File\Formatter
*
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Framework\File\Formatter;
class SerializeFormatter implements FormatterInterface
{
/** @var array */
private $config;
/**
* IniFormatter constructor.
* @param array $config
*/
public function __construct(array $config = [])
{
$this->config = $config + [
'file_extension' => '.ser'
];
}
/**
* @deprecated 1.5 Use $formatter->getDefaultFileExtension() instead.
*/
public function getFileExtension()
{
return $this->getDefaultFileExtension();
}
/**
* {@inheritdoc}
*/
public function getDefaultFileExtension()
{
$extensions = $this->getSupportedFileExtensions();
return (string) reset($extensions);
}
/**
* {@inheritdoc}
*/
public function getSupportedFileExtensions()
{
return (array) $this->config['file_extension'];
}
/**
* {@inheritdoc}
*/
public function encode($data)
{
return serialize($this->preserveLines($data, ["\n", "\r"], ['\\n', '\\r']));
}
/**
* {@inheritdoc}
*/
public function decode($data)
{
$decoded = @unserialize($data);
if ($decoded === false) {
throw new \RuntimeException('Decoding serialized data failed');
}
return $this->preserveLines($decoded, ['\\n', '\\r'], ["\n", "\r"]);
}
/**
* Preserve new lines, recursive function.
*
* @param mixed $data
* @param array $search
* @param array $replace
* @return mixed
*/
protected function preserveLines($data, $search, $replace)
{
if (is_string($data)) {
$data = str_replace($search, $replace, $data);
} elseif (is_array($data)) {
foreach ($data as &$value) {
$value = $this->preserveLines($value, $search, $replace);
}
unset($value);
}
return $data;
}
}
@@ -0,0 +1,103 @@
<?php
/**
* @package Grav\Framework\File\Formatter
*
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Framework\File\Formatter;
use Symfony\Component\Yaml\Exception\DumpException;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Yaml as YamlParser;
use RocketTheme\Toolbox\Compat\Yaml\Yaml as FallbackYamlParser;
class YamlFormatter implements FormatterInterface
{
/** @var array */
private $config;
public function __construct(array $config = [])
{
$this->config = $config + [
'file_extension' => '.yaml',
'inline' => 5,
'indent' => 2,
'native' => true,
'compat' => true
];
}
/**
* @deprecated 1.5 Use $formatter->getDefaultFileExtension() instead.
*/
public function getFileExtension()
{
return $this->getDefaultFileExtension();
}
/**
* {@inheritdoc}
*/
public function getDefaultFileExtension()
{
$extensions = $this->getSupportedFileExtensions();
return (string) reset($extensions);
}
/**
* {@inheritdoc}
*/
public function getSupportedFileExtensions()
{
return (array) $this->config['file_extension'];
}
/**
* {@inheritdoc}
*/
public function encode($data, $inline = null, $indent = null)
{
try {
return (string) YamlParser::dump(
$data,
$inline ? (int) $inline : $this->config['inline'],
$indent ? (int) $indent : $this->config['indent'],
YamlParser::DUMP_EXCEPTION_ON_INVALID_TYPE
);
} catch (DumpException $e) {
throw new \RuntimeException('Encoding YAML failed: ' . $e->getMessage(), 0, $e);
}
}
/**
* {@inheritdoc}
*/
public function decode($data)
{
// Try native PECL YAML PHP extension first if available.
if ($this->config['native'] && function_exists('yaml_parse')) {
// Safely decode YAML.
$saved = @ini_get('yaml.decode_php');
@ini_set('yaml.decode_php', 0);
$decoded = @yaml_parse($data);
@ini_set('yaml.decode_php', $saved);
if ($decoded !== false) {
return (array) $decoded;
}
}
try {
return (array) YamlParser::parse($data);
} catch (ParseException $e) {
if ($this->config['compat']) {
return (array) FallbackYamlParser::parse($data);
}
throw new \RuntimeException('Decoding YAML failed: ' . $e->getMessage(), 0, $e);
}
}
}
@@ -32,11 +32,6 @@ trait ObjectCollectionTrait
$list[$key] = is_object($value) ? clone $value : $value; $list[$key] = is_object($value) ? clone $value : $value;
} }
// TODO: remove when PHP 5.6 is minimum (with doctrine/collections v1.4).
if (!method_exists($this, 'createFrom')) {
return new static($list);
}
return $this->createFrom($list); return $this->createFrom($list);
} }
@@ -170,12 +165,7 @@ trait ObjectCollectionTrait
{ {
$collections = []; $collections = [];
foreach ($this->group($property) as $id => $elements) { foreach ($this->group($property) as $id => $elements) {
// TODO: remove when PHP 5.6 is minimum (with doctrine/collections v1.4). $collection = $this->createFrom($elements);
if (!method_exists($this, 'createFrom')) {
$collection = new static($elements);
} else {
$collection = $this->createFrom($elements);
}
$collections[$id] = $collection; $collections[$id] = $collection;
} }
@@ -15,7 +15,7 @@ namespace Grav\Framework\Object\Base;
*/ */
trait ObjectTrait trait ObjectTrait
{ {
static protected $prefix; /** @var string */
static protected $type; static protected $type;
/** /**
@@ -23,18 +23,28 @@ trait ObjectTrait
*/ */
private $_key; private $_key;
/**
* @return string
*/
protected function getTypePrefix()
{
return '';
}
/** /**
* @param bool $prefix * @param bool $prefix
* @return string * @return string
*/ */
public function getType($prefix = true) public function getType($prefix = true)
{ {
$type = $prefix ? $this->getTypePrefix() : '';
if (static::$type) { if (static::$type) {
return ($prefix ? static::$prefix : '') . static::$type; return $type . static::$type;
} }
$class = get_class($this); $class = get_class($this);
return ($prefix ? static::$prefix : '') . strtolower(substr($class, strrpos($class, '\\') + 1)); return $type . strtolower(substr($class, strrpos($class, '\\') + 1));
} }
/** /**
@@ -108,7 +118,7 @@ trait ObjectTrait
*/ */
public function serialize() public function serialize()
{ {
return serialize($this->jsonSerialize()); return serialize($this->doSerialize());
} }
/** /**
@@ -124,6 +134,14 @@ trait ObjectTrait
$this->doUnserialize($data); $this->doUnserialize($data);
} }
/**
* @return array
*/
protected function doSerialize()
{
return $this->jsonSerialize();
}
/** /**
* @param array $serialized * @param array $serialized
*/ */
@@ -159,10 +177,13 @@ trait ObjectTrait
/** /**
* @param string $key * @param string $key
* @return $this
*/ */
protected function setKey($key) protected function setKey($key)
{ {
$this->_key = (string) $key; $this->_key = (string) $key;
return $this;
} }
abstract protected function doHasProperty($property); abstract protected function doHasProperty($property);
@@ -0,0 +1,198 @@
<?php
/**
* @package Grav\Framework\Object
*
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Framework\Object\Collection;
use Doctrine\Common\Collections\Expr\ClosureExpressionVisitor;
use Doctrine\Common\Collections\Expr\Comparison;
class ObjectExpressionVisitor extends ClosureExpressionVisitor
{
/**
* Accesses the field of a given object.
*
* @param object $object
* @param string $field
*
* @return mixed
*/
public static function getObjectFieldValue($object, $field)
{
$op = $value = null;
$pos = strpos($field, '(');
if (false !== $pos) {
list ($op, $field) = explode('(', $field, 2);
$field = rtrim($field, ')');
}
if (isset($object[$field])) {
$value = $object[$field];
} else {
$accessors = array('', 'get', 'is');
foreach ($accessors as $accessor) {
$accessor .= $field;
if (!method_exists($object, $accessor)) {
continue;
}
$value = $object->{$accessor}();
break;
}
}
if ($op) {
$function = 'filter' . ucfirst(strtolower($op));
if (method_exists(static::class, $function)) {
$value = static::$function($value);
}
}
return $value;
}
public static function filterLower($str)
{
return mb_strtolower($str);
}
public static function filterUpper($str)
{
return mb_strtoupper($str);
}
public static function filterLength($str)
{
return mb_strlen($str);
}
public static function filterLtrim($str)
{
return ltrim($str);
}
public static function filterRtrim($str)
{
return rtrim($str);
}
public static function filterTrim($str)
{
return trim($str);
}
/**
* Helper for sorting arrays of objects based on multiple fields + orientations.
*
* @param string $name
* @param int $orientation
* @param \Closure $next
*
* @return \Closure
*/
public static function sortByField($name, $orientation = 1, \Closure $next = null)
{
if (!$next) {
$next = function() {
return 0;
};
}
return function ($a, $b) use ($name, $next, $orientation) {
$aValue = static::getObjectFieldValue($a, $name);
$bValue = static::getObjectFieldValue($b, $name);
if ($aValue === $bValue) {
return $next($a, $b);
}
return (($aValue > $bValue) ? 1 : -1) * $orientation;
};
}
/**
* {@inheritDoc}
*/
public function walkComparison(Comparison $comparison)
{
$field = $comparison->getField();
$value = $comparison->getValue()->getValue(); // shortcut for walkValue()
switch ($comparison->getOperator()) {
case Comparison::EQ:
return function ($object) use ($field, $value) {
return static::getObjectFieldValue($object, $field) === $value;
};
case Comparison::NEQ:
return function ($object) use ($field, $value) {
return static::getObjectFieldValue($object, $field) !== $value;
};
case Comparison::LT:
return function ($object) use ($field, $value) {
return static::getObjectFieldValue($object, $field) < $value;
};
case Comparison::LTE:
return function ($object) use ($field, $value) {
return static::getObjectFieldValue($object, $field) <= $value;
};
case Comparison::GT:
return function ($object) use ($field, $value) {
return static::getObjectFieldValue($object, $field) > $value;
};
case Comparison::GTE:
return function ($object) use ($field, $value) {
return static::getObjectFieldValue($object, $field) >= $value;
};
case Comparison::IN:
return function ($object) use ($field, $value) {
return \in_array(static::getObjectFieldValue($object, $field), $value, true);
};
case Comparison::NIN:
return function ($object) use ($field, $value) {
return !\in_array(static::getObjectFieldValue($object, $field), $value, true);
};
case Comparison::CONTAINS:
return function ($object) use ($field, $value) {
return false !== strpos(static::getObjectFieldValue($object, $field), $value);
};
case Comparison::MEMBER_OF:
return function ($object) use ($field, $value) {
$fieldValues = static::getObjectFieldValue($object, $field);
if (!is_array($fieldValues)) {
$fieldValues = iterator_to_array($fieldValues);
}
return \in_array($value, $fieldValues, true);
};
case Comparison::STARTS_WITH:
return function ($object) use ($field, $value) {
return 0 === strpos(static::getObjectFieldValue($object, $field), $value);
};
case Comparison::ENDS_WITH:
return function ($object) use ($field, $value) {
return $value === substr(static::getObjectFieldValue($object, $field), -strlen($value));
};
default:
throw new \RuntimeException("Unknown comparison operator: " . $comparison->getOperator());
}
}
}
@@ -8,13 +8,14 @@
namespace Grav\Framework\Object\Interfaces; namespace Grav\Framework\Object\Interfaces;
use Doctrine\Common\Collections\Selectable;
use Grav\Framework\Collection\CollectionInterface; use Grav\Framework\Collection\CollectionInterface;
/** /**
* ObjectCollection Interface * ObjectCollection Interface
* @package Grav\Framework\Collection * @package Grav\Framework\Collection
*/ */
interface ObjectCollectionInterface extends CollectionInterface, ObjectInterface interface ObjectCollectionInterface extends CollectionInterface, Selectable, ObjectInterface
{ {
/** /**
* Create a copy from this collection by cloning all objects in the collection. * Create a copy from this collection by cloning all objects in the collection.
@@ -8,9 +8,11 @@
namespace Grav\Framework\Object; namespace Grav\Framework\Object;
use Doctrine\Common\Collections\Criteria;
use Grav\Framework\Collection\ArrayCollection; use Grav\Framework\Collection\ArrayCollection;
use Grav\Framework\Object\Access\NestedPropertyCollectionTrait; use Grav\Framework\Object\Access\NestedPropertyCollectionTrait;
use Grav\Framework\Object\Base\ObjectCollectionTrait; use Grav\Framework\Object\Base\ObjectCollectionTrait;
use Grav\Framework\Object\Collection\ObjectExpressionVisitor;
use Grav\Framework\Object\Interfaces\NestedObjectInterface; use Grav\Framework\Object\Interfaces\NestedObjectInterface;
use Grav\Framework\Object\Interfaces\ObjectCollectionInterface; use Grav\Framework\Object\Interfaces\ObjectCollectionInterface;
@@ -36,6 +38,39 @@ class ObjectCollection extends ArrayCollection implements ObjectCollectionInterf
$this->setKey($key); $this->setKey($key);
} }
/**
* {@inheritDoc}
*/
public function matching(Criteria $criteria)
{
$expr = $criteria->getWhereExpression();
$filtered = $this->getElements();
if ($expr) {
$visitor = new ObjectExpressionVisitor();
$filter = $visitor->dispatch($expr);
$filtered = array_filter($filtered, $filter);
}
if ($orderings = $criteria->getOrderings()) {
$next = null;
foreach (array_reverse($orderings) as $field => $ordering) {
$next = ObjectExpressionVisitor::sortByField($field, $ordering == Criteria::DESC ? -1 : 1, $next);
}
uasort($filtered, $next);
}
$offset = $criteria->getFirstResult();
$length = $criteria->getMaxResults();
if ($offset || $length) {
$filtered = array_slice($filtered, (int)$offset, $length);
}
return $this->createFrom($filtered);
}
protected function getElements() protected function getElements()
{ {
return $this->toArray(); return $this->toArray();
@@ -95,10 +95,10 @@ trait ObjectPropertyTrait
} }
/** /**
* @param string $property Object property to be fetched. * @param string $property Object property to be fetched.
* @param mixed $default Default value if property has not been set. * @param mixed $default Default value if property has not been set.
* @param bool $doCreate Set true to create variable. * @param callable|bool $doCreate Set true to create variable.
* @return mixed Property value. * @return mixed Property value.
*/ */
protected function &doGetProperty($property, $default = null, $doCreate = false) protected function &doGetProperty($property, $default = null, $doCreate = false)
{ {
+4 -5
View File
@@ -178,7 +178,7 @@ class Route
*/ */
public function withGravParam($param, $value) public function withGravParam($param, $value)
{ {
return $this->withParam('gravParams', $param, $value); return $this->withParam('gravParams', $param, null !== $value ? (string)$value : null);
} }
/** /**
@@ -222,17 +222,16 @@ class Route
protected function withParam($type, $param, $value) protected function withParam($type, $param, $value)
{ {
$oldValue = isset($this->{$type}[$param]) ? $this->{$type}[$param] : null; $oldValue = isset($this->{$type}[$param]) ? $this->{$type}[$param] : null;
$newValue = null !== $value ? (string)$value : null;
if ($oldValue === $newValue) { if ($oldValue === $value) {
return $this; return $this;
} }
$new = clone $this; $new = clone $this;
if ($newValue === null) { if ($value === null) {
unset($new->{$type}[$param]); unset($new->{$type}[$param]);
} else { } else {
$new->{$type}[$param] = $newValue; $new->{$type}[$param] = $value;
} }
return $new; return $new;
@@ -28,6 +28,23 @@ class RouteFactory
return new Route($parts); return new Route($parts);
} }
public static function createFromString($path)
{
$path = ltrim($path, '/');
$parts = [
'path' => $path,
'query' => '',
'query_params' => [],
'grav' => [
'root' => self::$root,
'language' => self::$language,
'route' => $path,
'params' => ''
],
];
return new Route($parts);
}
public static function getRoot() public static function getRoot()
{ {
return self::$root; return self::$root;
@@ -0,0 +1,340 @@
<?php
/**
* @package Grav\Framework\Session
*
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Framework\Session;
/**
* Class Session
* @package Grav\Framework\Session
*/
class Session implements SessionInterface
{
/**
* @var bool
*/
protected $started = false;
/**
* @var Session
*/
protected static $instance;
/**
* @inheritdoc
*/
public static function getInstance()
{
if (null === self::$instance) {
throw new \RuntimeException("Session hasn't been initialized.", 500);
}
return self::$instance;
}
/**
* @inheritdoc
*/
public function __construct(array $options = [])
{
// Session is a singleton.
if (\PHP_SAPI === 'cli') {
self::$instance = $this;
return;
}
if (null !== self::$instance) {
throw new \RuntimeException('Session has already been initialized.', 500);
}
// Destroy any existing sessions started with session.auto_start
if ($this->isSessionStarted()) {
session_unset();
session_destroy();
}
// Set default options.
$options += array(
'cache_limiter' => 'nocache',
'use_trans_sid' => 0,
'use_cookies' => 1,
'lazy_write' => 1,
'use_strict_mode' => 1
);
$this->setOptions($options);
session_register_shutdown();
self::$instance = $this;
}
/**
* @inheritdoc
*/
public function getId()
{
return session_id();
}
/**
* @inheritdoc
*/
public function setId($id)
{
session_id($id);
return $this;
}
/**
* @inheritdoc
*/
public function getName()
{
return session_name();
}
/**
* @inheritdoc
*/
public function setName($name)
{
session_name($name);
return $this;
}
/**
* @inheritdoc
*/
public function setOptions(array $options)
{
if (headers_sent() || \PHP_SESSION_ACTIVE === session_status()) {
return;
}
$allowedOptions = [
'save_path' => true,
'name' => true,
'save_handler' => true,
'gc_probability' => true,
'gc_divisor' => true,
'gc_maxlifetime' => true,
'serialize_handler' => true,
'cookie_lifetime' => true,
'cookie_path' => true,
'cookie_domain' => true,
'cookie_secure' => true,
'cookie_httponly' => true,
'use_strict_mode' => true,
'use_cookies' => true,
'use_only_cookies' => true,
'referer_check' => true,
'cache_limiter' => true,
'cache_expire' => true,
'use_trans_sid' => true,
'trans_sid_tags' => true, // PHP 7.1
'trans_sid_hosts' => true, // PHP 7.1
'sid_length' => true, // PHP 7.1
'sid_bits_per_character' => true, // PHP 7.1
'upload_progress.enabled' => true,
'upload_progress.cleanup' => true,
'upload_progress.prefix' => true,
'upload_progress.name' => true,
'upload_progress.freq' => true,
'upload_progress.min-freq' => true,
'lazy_write' => true,
'url_rewriter.tags' => true, // Not used in PHP 7.1
'hash_function' => true, // Not used in PHP 7.1
'hash_bits_per_character' => true, // Not used in PHP 7.1
'entropy_file' => true, // Not used in PHP 7.1
'entropy_length' => true, // Not used in PHP 7.1
];
foreach ($options as $key => $value) {
if (is_array($value)) {
// Allow nested options.
foreach ($value as $key2 => $value2) {
$ckey = "{$key}.{$key2}";
if (isset($value2, $allowedOptions[$ckey])) {
$this->ini_set("session.{$ckey}", $value2);
}
}
} elseif (isset($value, $allowedOptions[$key])) {
$this->ini_set("session.{$key}", $value);
}
}
}
/**
* @inheritdoc
*/
public function start($readonly = false)
{
// Protection against invalid session cookie names throwing exception: http://php.net/manual/en/function.session-id.php#116836
if (isset($_COOKIE[session_name()]) && !preg_match('/^[-,a-zA-Z0-9]{1,128}$/', $_COOKIE[session_name()])) {
unset($_COOKIE[session_name()]);
}
$options = $readonly ? ['read_and_close' => '1'] : [];
$success = @session_start($options);
if (!$success) {
$last = error_get_last();
$error = $last ? $last['message'] : 'Unknown error';
throw new \RuntimeException('Failed to start session: ' . $error, 500);
}
$params = session_get_cookie_params();
setcookie(
session_name(),
session_id(),
time() + $params['lifetime'],
$params['path'],
$params['domain'],
$params['secure'],
$params['httponly']
);
$this->started = true;
return $this;
}
/**
* @inheritdoc
*/
public function invalidate()
{
$params = session_get_cookie_params();
setcookie(
session_name(),
'',
time() - 42000,
$params['path'],
$params['domain'],
$params['secure'],
$params['httponly']
);
session_unset();
session_destroy();
$this->started = false;
return $this;
}
/**
* @inheritdoc
*/
public function close()
{
if ($this->started) {
session_write_close();
}
$this->started = false;
return $this;
}
/**
* @inheritdoc
*/
public function clear()
{
session_unset();
return $this;
}
/**
* @inheritdoc
*/
public function getAll()
{
return $_SESSION;
}
/**
* @inheritdoc
*/
public function getIterator()
{
return new \ArrayIterator($_SESSION);
}
/**
* @inheritdoc
*/
public function isStarted()
{
return $this->started;
}
/**
* @inheritdoc
*/
public function __isset($name)
{
return isset($_SESSION[$name]);
}
/**
* @inheritdoc
*/
public function __get($name)
{
return isset($_SESSION[$name]) ? $_SESSION[$name] : null;
}
/**
* @inheritdoc
*/
public function __set($name, $value)
{
$_SESSION[$name] = $value;
}
/**
* @inheritdoc
*/
public function __unset($name)
{
unset($_SESSION[$name]);
}
/**
* http://php.net/manual/en/function.session-status.php#113468
* Check if session is started nicely.
* @return bool
*/
protected function isSessionStarted()
{
return \PHP_SAPI !== 'cli' ? \PHP_SESSION_ACTIVE === session_status() : false;
}
/**
* @param string $key
* @param mixed $value
*/
protected function ini_set($key, $value)
{
if (!is_string($value)) {
if (is_bool($value)) {
$value = $value ? '1' : '0';
}
$value = (string)$value;
}
ini_set($key, $value);
}
}
@@ -0,0 +1,147 @@
<?php
/**
* @package Grav\Framework\Session
*
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Framework\Session;
/**
* Class Session
* @package Grav\Framework\Session
*/
interface SessionInterface extends \IteratorAggregate
{
/**
* Get current session instance.
*
* @return Session
* @throws \RuntimeException
*/
public static function getInstance();
/**
* Get session ID
*
* @return string|null Session ID
*/
public function getId();
/**
* Set session ID
*
* @param string $id Session ID
*
* @return $this
*/
public function setId($id);
/**
* Get session name
*
* @return string|null
*/
public function getName();
/**
* Set session name
*
* @param string $name
*
* @return $this
*/
public function setName($name);
/**
* Sets session.* ini variables.
*
* @param array $options
*
* @see http://php.net/session.configuration
*/
public function setOptions(array $options);
/**
* Starts the session storage
*
* @param bool $readonly
* @return $this
* @throws \RuntimeException
*/
public function start($readonly = false);
/**
* Invalidates the current session.
*
* @return $this
*/
public function invalidate();
/**
* Force the session to be saved and closed
*
* @return $this
*/
public function close();
/**
* Free all session variables.
*
* @return $this
*/
public function clear();
/**
* Returns all session variables.
*
* @return array
*/
public function getAll();
/**
* Retrieve an external iterator
*
* @return \ArrayIterator Return an ArrayIterator of $_SESSION
*/
public function getIterator();
/**
* Checks if the session was started.
*
* @return Boolean
*/
public function isStarted();
/**
* Checks if session variable is defined.
*
* @param string $name
* @return bool
*/
public function __isset($name);
/**
* Returns session variable.
*
* @param string $name
* @return mixed
*/
public function __get($name);
/**
* Sets session variable.
*
* @param string $name
* @param mixed $value
*/
public function __set($name, $value);
/**
* Removes session variable.
*
* @param string $name
*/
public function __unset($name);
}
@@ -0,0 +1,18 @@
---
title: '12 juillet 2017'
image_align: left
id: rct_12072017
---
Je suis de retour rue de la boulangerie, à Franciade, la petite boutique où j'avais été lors de ma toute première visite à Saint-Denis. Le Tote bag " la vie dyonisienne" m'accompagne toujours.
Andreea, avec qui j'avais brièvement échangé lors de cette première visite, est présente dans la boutique et je profite de l'absence de clients pour lui proposer un petit entretien. Elle me raconte alors l'histoire de Franciade, autour de laquelle l'entretien tournera beaucoup. On sent toute l'importance et la place que prend cette association dans sa vie. Je comprends que lassociation Franciade a 13 ans et la boutique dans laquelle nous nous trouvons existe depuis 7 ans. Son but est de valoriser le patrimoine de la ville de Saint-Denis et le savoir faire des artisans. Si à lorigine il sagissait surtout de vendre des céramiques, répliques des objets trouvés dans les fouilles du site archéologique , il y a aujourdhui beaucoup de savoir-faire différents représentés à travers la boutique.
Les projets de lassociation sont multiples : édition, photographie, écriture collective, chantiers et création diverses.. touchant un panel dacteurs encore une fois très variés. Pour Andréea, ce sont les années de travail de la directrice qui font quelle a aujourdhui un grand réseau de contact et que l’équipe arrive à impliquer autant de gens. Alors, lorsque je demande si Franciade entretien des liens avec dautres associations du coin, la réponse est évidente :
« Oui, bien sûr, on a notre petit réseau comme Artefact, la coopérative Pointcarré, Adada.. il y a énormément dartistes et dinitiatives ici. Tout le monde connaît quelquun qui connaît quelquun… et les projets foisonnent ! »
Franciade diffuse par flyers et par Facebook. Placés dans une rue qui na pas beaucoup de passage, l’équipe a besoin de ramener des gens de lextérieur qui ne passeraient pas autrement ici. Cest pourquoi ils utilisent beaucoup les réseaux sociaux, qui leur permettent de rappeler aux gens quils sont présents. Leur site internet, quant à lui, existe surtout comme vitrine et pour archiver les 13 années dactivités. « Avec tous les artistes qui passent, cest bien davoir un endroit ou lon peut retrouver la trace de tout ça. »
Pour Andréea, loutil numérique a une importance toute singulière. En effet, celle-ci vit loin de sa famille et ne pourrait imaginer son quotidien sans Skype et la possibilité de « se voir » que le logiciel lui offre. Ses systèmes de communications se mélangent et se croisent au fil de ses besoins.
En terme de contact local pour mon expérience, et en conclusion de cet entretien, Andréae minvite à aller à la rencontre les membres de Point Carré.
Je my rend directement à la suite de cet échange : cest juste à côté, rue Gabriel Péri.
Point Carré est une coopérative dartisans locaux, avec un espace de vente, de café et un espace de co-working.Jy rencontre Wiebke.Nous échangeons brievement sur le projet mais, trop occupée dans les temps à venir pour envisager un entretien, Wiebke préfére m'envoyer directement vers l'une de ses amies dont un projet récent fait écho au mien. Elle s'appelle Manon, je note ses coordonnées pour plus tard. Wiebke travaille à la Coopérative mais est aussi artiste plasticienne, installée à la Briche Foraine, un lieu qui accueille des ateliers dartiste dans le nord de Saint Denis. Son activité porte notamment sur le travaille de la laine, en lien avec les Clinamen, des bergers de Saint Denis..
Pendant notre échange dans le café, un groupe de personnes discute de Royal Deluxe, une troupe de personnages géants articulés, passés au Havre, ou je réside, le week-end précédent. Une jeune femme minterpelle en plaisantant et m'informe qu'elle est à l'origine des dessins sérigraphiées sur le sac " la vie Dyonisienne" que je porte et quelle portait elle aussi lors de sa visite au Havre pour Royal deluxe. Nous sourions à lidée que nous étions au moins deux au Havre ce week-end là à arborer les couleurs dyonisiennes.
@@ -0,0 +1,13 @@
---
title: '12 septembre 2017'
image_align: left
id: rct_12092017
---
Depuis quelques semaines, je suis en contact avec Manon, dont Wiebke de Point Carré m'avait donné le numéro de téléphone. Il y a quelques temps déjà, Manon avait mené un projet de cartographie du réseau artistique de Saint-Denis et il a donc semblé évident à Wiebke que nous aurions beaucoup à échanger. Elle ne s’était pas trompée.
Après avoir eu un peu de mal à faire concorder nos disponibilités sur Saint-Denis, j'ai rejoint Manon un midi sur son lieu de travail, dans le quartier de La Défense à Paris.
Installées sur un banc, je l’écoute me raconter comment elle est tombée sous le charme de Saint-Denis au cours du projet qu'elle y a mené :
« Lidée de l’étude, c’était de comprendre les liens entre les lieux plutôt institutionnels et les lieux plutôt alternatifs. Jallai donc voir soit des lieux, soit des artistes, pour quils me parlent de leur parcours, de leurs réseaux etc.. et c’était génial parce que je rencontrais de personnes en personnes. »
Cest par le biais de toutes ces rencontres que Manon a pu avoir un apperçu de la richesse de projets, activités et émulations du territoire de Saint Denis.
« Ce qui me plait aussi là bas cest que par exemple si je vais au Pavillon je sais que je vais croiser des gens que je connais. Jy rejoins une amie, on est deux, on finit la soirée on est 10 à tables parce que les gens viennent discuter, tu tes croisé une fois ou deux dans un projet du coin et on fini par boire des coups ensemble. «  Rapidement, elle a décidé de sy installer et il lui a fallu moins de 8 mois pour se familiariser avec ce nouveau territoire.
Lorsque Manon décrit sa "ville à echelle humaine" comme un espace pleins de ressources et pleins de vie, sa bienveillance à l’égard de sa nouvelle ville me donne envie de continuer à découvrir les lieux à travers son regard. Le Pavillon, un café du centre ville, semble être l'un de ses lieux fétiche et je décide donc daller my installer l'après-midi même, le temps d'un café.
@@ -0,0 +1,10 @@
---
title: '13 septembre 2017'
image_align: left
id: rct_130917
---
Cet après midi, jai rendez-vous avec Nadia, du centre socioculturel coopératif de Saint-Denis. J'ai eu vent de ce centre sur Facebook, grâce à des "likes" de récents contact dyonisiens, comme Pascale.
Lorsque j'ai contacté l'équipe du centre par ladresse mail que j'ai trouvé sur la page Facebook, Nadia ma tout de suite répondu et invité à passer directement dans les locaux.
Arrivée au 28 rue Jean Jaurès je suis reçue très chaleureusement par Nadia. Autour dun café dans ce lieu encore peu meublé, Nadia entreprend de me retracer l'historique de ce projet coopératif, porté par l'association Coopérence dont elle soccupe. Anciennement situé rue Gabriel Péri, le centre coopératif se trouve aujourdhui dans les locaux de La Maison des projets, le temps dacceder au local aujourdhui en travaux - qui lui est destiné place du 8 mai 1945. Aujourdhui monté en association, le centre a vocation à devenir une SCIC, une société coopérative dinterêt collectif.
En pleine transition, le lieu est actif pour autant : Lentretien avec Nadia est sans cesse coupé par de nouveaux arrivants, des questions, des tâches à gérer. Une belle ruche se construit, faisant foi de toute l’énergie nécessaire à cet ambitieux projet. En repartant à la fin de cet échange, je rencontre Pascale, qui prépare justement les nouvelles activités quelle va commencer à mener dans le local prochainement.
@@ -0,0 +1,24 @@
---
title: '19 juin 2017'
image_align: left
id: rct_190617
---
Il y a quelques jours, j'ai échangé avec [Pasacale](#int_pascale) dont j'avais eu vent du travail sur [l'affiche](#img_mj2) de la Maison Jaune.
En début d'après midi, nous nous retrouvons à la Maison Jaune, où Pascale me fait découvrir le lieu. Elle mexplique : « Cest une maison de quartier qui a ouvert au mois de Novembre. lidée cest que cet espace soit mis à disposition des habitants, quon y organise des choses mais aussi quil y ai de la place pour leurs propres initiatives… »
Pascale a découvert le lieu en octobre 2016, en passant devant. Elle nhabite pas loin et était justement à la recherche dun lieu pour son projet de tricot-partage, un projet de tricot solidaire.
Je lui demande de men dire plus sur son projet et fait vite le lien avec le patchwork de formes tricotés, monumental, qui git au sol de la Maison Jaune :
«  Le projet de tricot solidaire c’était, au vu de la grosse vague de froid, de réaliser ,avec la participation du public, des carrés de laine de 30cm de côté assemblés en couvertures. Ces couvertures sont à destination de la Croix Rouge et distribuées lors des maraudes. Lenvie était aussi que chacun, tout âge confondu, puisse participer à sa manière, en venant pour tricoter, pour apprendre ou pour transmettre. »
Pour se fournir en laine, Pascale a lancé un appel au don dans Saint-Denis, qui a vite débordé du territoire par le biais dinternet. Elle a reçu des colis de Paris, de Bretagne, de Nice..
« jai même eu un message dune personne dans un petit village dardèche, qui ma écrit en me disant quelle tricoterait des petits carrés de laine pour nous les envoyer. »
Ici, internet est un vecteur indéniable de rencontre, mais le « fil participatif » comme lappel Pascale, sest construit de bien des manières. Le projet est nomade dans Saint-Denis, permettant au groupe de rencontrer davantage de personnes et douvrir le réseau initial. Les moyens de communication du projet sont aussi divers. Les habitants de Saint-Denis ont vent du projet par des relais Facebook, des affichages en ville, des flyers.. jusqu’à limpression même du flyer :
« Il y a quelques temps, chez limprimeur, je faisais mes photocopies pour l’évenement à la Maison Jaune. Au moment de régler jai posé mes copies sur le comptoir. Une dame était derrière moi et a lu « tricot ». Elle ma demandé ce qu’était l’événement et elle est venue. Depuis, elle vient régulièrement et nous sommes assez liées. »
Dans le collectif, cest sur lapplication whats app que la communication interne s'organise. Vers lextérieur, cest plutôt Facebook.
Lorsque nous évoquons les moyens d’échange et de communication à disposition dune association comme celle-ci, Pascale observe que loutil numérique leur permet dagréger des personnes qui ne sont pas forcément disponibles en temps et en heure, ou nont pas la possibilité de se déplacer, mais qui souhaitent quand même être informés, participer et être tenus au courant des activités.
Elle note cependant que « la communication papier et la communication numérique sont vraiment complémentaires. Certains utilisent les deux régulièrement mais dautres sont beaucoup moins familiers de loutil informatique. Il y a des personnes qui nont pas de-mail et ne sont joignables que par téléphone, voire qui nont pas de téléphone portable. Je crois aussi que cest bien de ne pas uniformiser linformation.»
Pendant notre entretien, apparaît Sofiane, un habitant impliqué dans le collectif de la Maison Jaune, qui projette d'y organiser des ateliers de patisseries, étant lui-même en formation patissier.
On sent dans ce lieu l’émulation de quelque chose qui naît, qui ne sait pas toujours où il va mais qui est bien là.
Ce premier échange a été très instructif et Pascale mayant évoqué plusieurs lieux de Saint-Denis, je profite de l'après-midi pour les repérer. En chemin, je croise des tricoteuses : un petit groupe tricote dans la rue, sur les arbres, les barrières. Ca fait tout de suite écho avec le projet tricot-partage de Pascale, et me pousse à m'arrêter discuter.
C'est comme ça que je fais la connaissance d'Océane, de l'association Dechets d'arts, qui participe ici à une "opération street mamies", me dit-elle. Elle me laisse sa carte pour que nous nous recontactions plus tard.
@@ -0,0 +1,24 @@
---
title: '25 juillet 2017'
image_align: left
id: rct_250717
---
Ce matin je suis allée visiter le 6B, un lieu de création et de diffusion installé dans un ancien immeuble de bureau situé sur les quais dans le quartier de la Gare à Saint-Denis.
Ouvert depuis 2010, le lieu dispose d'espaces de travail et de diffusion sur 6 étages et accueille plus de 150 résidents. Autogéré par ses résidents, il regroupe musiciens, cinéastes, graphistes, artisans, travailleurs sociaux, comédiens, danseurs, peintres, sculpteurs, architectes… et héberge des événements comme des expositions, spectacles, séminaires, colloques.. lance des actions culturelles de proximité, festivals éphémères,Son objectif est de proposer une culture à portée de tous.
Je ny croise pas grand monde, cest les vacances, mais découvre les étages par une petite visite improvisée.
Cette après-midi, je quitte Saint-Denis pour retourner dans Paris : j'ai rendez-vous avec Victoria - contactée via Marie.P. Victoria était étudiante à Paris 8 à Saint Denis mais vit dans Paris. Je la retrouve sur la place de la République et nous nous installons à la terasse du café Pierre.
Dès nos premiers échanges, ce quelle me dit de sa connaissance du territoire de Saint Denis me confirme ce quavais pu remarquer Marie : « Mes premières années à Paris 8, je ne connaissais que la sortie du métro université et les alentours de Paris 8.. jai découvert petit à petit Saint Denis par des amis de la fac qui y habitent. Au début je savais à peine dans quelle direction était le centre ville. »
Au fil du temps, elle a ainsi découvert Saint-Denis, surtout à travers les logements de ses amis, jusqu’à ce quelle découvre lAttiéké. Déclaré « Centre-Social Auto-Organisé », lAttiéké réunit des personnes «  avec ou sans papiers / avec ou sans-logis » travaillant ou non, en réquisitionnant un lieu jusque là resté vide, abandonné. Devenant un logement pour tous, le lieu devient aussi un espace de rencontre, d’échange, dentre-aide et dactivités publiques.
Cest au moment de lexpulsion du lieu que Victoria a fait la connaissance des ses occupants.
Des étudiants de Paris 8 sorganisaient alors pour les aider à occuper des lieux pour leur retrouver un espace de vie. «  Un groupe assez militant sest formé et jai découvert les activités et la diversité du lieu. Jy suis beaucoup allée. »
Lorsque nous évoquons les canaux de communications liées à un tel lieu, victoria mexplique que dans un système précaire comme celui-là, la communication investit peu les plate-formes comme Facebook avec les événements etc. Que pllus discrètement, ça fonctionne aussi très bien.
« On se tiens au courant quand on se rencontre ou on senvoie quelques messages, des mails ou des sms. On utilise beaucoup laffiche dans les quartiers donc ça a tendance à rester très local et à fédérer des groupes dhabitants. »
Au cours de son cursus à Paris 8, cest autour dun projet co-créé avec quelques autres étudiants que Victoria a pu explorer le collectif et lalternatif : Le Collectif Kabane.
« A luniversité j’étais en cours dart mais on avait ni atelier ni espace de convivialité pour se retrouver ou travailler en groupe. On a décidé de saménager un espace pour ça, tout autant symbolique que concret, et on a commencé à construire une cabane. »
Si la cabane a disparu, le collectif , lui, a continué à se documenter sur les réseaux alternatifs, les lieux auto-gérés. Kabane se définit comme un collectif expérimental nomade, qui travaille sur la co-production de nouvelles formes de vie et de rapport social.
Le partage de connaissance est au coeur de leur démarche, et sexprime aussi dans leur choix dutilisation du web et des outils numériques : « Pour la Kabane on a pas mal utilisé les outils Framasoft, des framapad etc, on a essayé d’éviter les google docs et compagnie au sein du collectif car la question du logiciel libre et de loutil collaboratif était important pour nous. Pour faire le lien avec dautres étudiants on utilisait surtout les listes de mails. On a beaucoup de gens qui ne sont pas sur Facebook dans nos contacts et on essaye aussi de s’émanciper car on a tendance à senfermer dans les infos qui sy trouves et à ne plus chercher linformation ailleurs.»
Lune de leur action retient mon attention : Le projet « Copiothèque » au coeur duquel le partage de connaissance prend toute sa place, mélangeant la rencontre physique et le partage numérique.
A la fois « lieu de rencontre nomade » qui invite à partager de la documentation sur un tapis, au sol dans lespace public, la copithèque est aussi un lieu d’échange numérique via une Pirate Box ou il est possible denvoyer ou télécharger du contenu en sy connectant.
Lorsque je quitte Victoria, elle prépare son départ pour un tour de France des lieux alternatifs de quelques mois..
@@ -0,0 +1,12 @@
---
title: '26 juillet 2017'
image_align: left
id: rct_260717
---
En ce début d après-midi, j'ai rendez-vous avez Océane, rencontrée quelques semaines plus tôt devant la Maison des Séniors. Elle m'avait proposé de la rejoindre sur un chantier quelle mène pour le collectif MundGawi. En arrivant, j'y rencontre Laurent, artisant-sculpteur qui participe lui aussi au chantier et qui maccueille avant larrivée dOcéane. Je lui propose de se joindre à nous le temps de lentretien. Océane fait partie de lassociation Dechets dArts, dont lobjectif est de sensibiliser les citoyens à des comportements eco-résponsables autour du dechet et au travers de la création artistique. La démarche de lassociation est de faire du dechet une resssource avec une approche poétique, tout en mettant en place des ateliers créatifspour apprendre des techniques de réemploi.
« Le projet des streeet mamies durant lequel tu mas croisé en fait partie. On part de la technique du crochet adapté à des materiaux de récupération comme du sac plastique et on invite des séniors femmes pour aussi réaffirmer la place et des séniors et de la femme dans lespace public. Cest un vecteur de rencontre. »
Océane est impliquée dans beaucoup dactions et de collectifs dans Saint-Denis. Il lui est arrivé de rencontrer des gens dabord sur des plateformes numériques pour la construction de projets. « Pour certains on a dabord discuté que ce soit sur des commentaires Facebook ou sur lapplication whats app. Je me suis notamment rapproché dune structure de maison de quartier coopérative qui est en train de se monter pas loin dici et eux, cest leur principal moyen de communiquer donc pour beaucoup jai fait leur connaissance sur whats app et je les rencontre au fur et à mesure. »
Laurent habite Paris et se rend à Saint-Denis essentiellement pour se rendre au 6B, ou il a son atelier, mais connais très peu Saint-Denis. Le chantier sur lequel nous nous trouvons est une occasion pour lui denclancher des collaborations avec dautres créateurs du site.
Océane en revanche habite et connaît bien sa ville de Saint Denis, elle me livre les endroits liés à son quotidien :
« L’école rue du corbillon, le college jsd place de la résistance, le petit café au Pavillon éventuellement en accompagnant mes filles le matin. Un petit tour au Point Carré de temps en temps, parce que je suis reliée au projet aussi. Le théâtre Gerard Phillipe, pas de manière assidue mais régulièrement. LAdada, lartefact 93 , le parc de la légion dhonneur avec mes filles ou simplement la place de la basilique pour faire du roller. Le 6B également, parce que jai mon bureau là bas et pour participer à des événement. Cest là où on sest rencontrés avec Laurent. Le chapiteau Rajganawak aussi, qui a ouvert récemment. Il y a un cabaret, des événements, des cours de yoga..et enfin les petits commerces de proximité autour de chez moi. »
+15
View File
@@ -0,0 +1,15 @@
---
title: '3 mai 2017'
image_align: left
id: rct_030517
---
En cette première semaine de mai, je réfléchi à la construction concrête du projet.
Après un premier et rapide- tour d'horizon de Saint-Denis, il m'apparait assez clairement que la ville possède un tissu associatif énorme, très diversifié et étendu. On peut sans doute en déduire une attitude plutot collective des riverains et lexistence dun grand nombre d'activités et de liens - discrets ou non - entre les habitants de Saint-Denis.
Considérant le territoire sous cet angle, je comprend l'aspect monumental de tenter de cartographier ces actions discrètes, ce réseau de convivialité. Comment simmiscer assez profondément dans la vie dyonisienne pour en ressortir ses anecdotes, ses "activités informelles" entre voisins, ses discussions de cafés.. sans même y habiter ?
Il me semble par ailleurs tout à fait subjectif d'affirmer à un moment donné une cartographie de la vie dun territoire, quel quil soit. Tout ce que j'aurais raté, tous ceux que je n'aurais pas croisé ne feraient pas partie de cette cartographie. Quel sens en tirer ?
Je décide daborder le projet d'une autre manière. Non pas avec l'ambition de cartographier ou dinventorier des liens ou des actions mais plutot avec l'envie de vivre une expérience au cœur de Saint-Denis. Celle-ci se construira au rythme de mes allées et venues. Je m'éloigne ainsi d'une intention de photographier un réseau à un moment précis, avec plutôt lenvie de me laisser porter par celui-ci. Ou plutôt par lun dentre eux. Par un réseau, celui dans lequel je cheminerai au fil du hasard, laissant l'intuition, le feeling des rencontres et le temps œuvrer.
Par ma première entrée qu'est Synesthésie dans cet environnement de Saint-Denis, je rencontre dors et déjà quelques personnes, et je prends le parti de discuter avec celles-ci et de leur demander à chacune de me présenter quelquun dautre,etc,etc. C'est dans ce réseau que je cheminerais, le découvrant au fur et à mesure.
Maintenant, quel sera l'objet des rencontres ? Il m'importe de découvrir les habitants comme des acteurs de la vie de Saint-Denis en comprenant le rôle qu'ils y jouent, quel qu'il soit. Mais je les questionnerais aussi sur leur rapport au numérique, afin de tenter de cerner quel place a celui-ci dans leurs échanges au quotidien.
Mon intuition est qu'il est aujourd'hui difficile de séparer un réseau dit "physique" dun réseau " virtuel" ou "numérique" mais que ceux-ci s'imbriquent, s'hybrident, se complètent.
A travers ces échanges je tenterais ainsi de questionner les outils numériques comme vecteurs de convivialité - ou non.
@@ -0,0 +1,8 @@
---
title: '3 octobre 2017'
image_align: left
id: rct_03102017
---
Retour au Pavillon, cette fois accompagnée. Jy rejoint Solen, que je rencontre sur les recommendations de Manon. Nous navons pas choisi ce point de rencontre au hasard : Solen habite juste à côté et habituée des lieux, cest justement ici quelle avait rencontré Manon, par le biais dune amie. Solen est arrivée à Saint Denis il y a quelques années pour se rapprocher de son lieu de travail, plus au Nord. En cherchant un logement, elle a été agréablement surprise par la ville dont elle s’était forgée une image négative. Ny connaissant personne au départ, cest principalement à travers le réseau de lAMAP quelle sest ouverte à de nouvelles activités et rencontres sur Saint-Denis. Plus tard, une période de chômage la encouragée à sinvestir davantage dans la vie associative du territoire et elle a notamment pu réfléchir à un projet de café culturel, toujours en cours avec dautres habitants, dont Olivier, que javais déjà prévu de rencontrer le lendemain. Lorsque je fais la remarque, Solen me confie que cest justement la personne vers qui elle maurait invité à aller.
Cette coïncidence me fait souligner que durant ces semaines passées ici je suis souvent retombé sur les même personnes et quainsi je perçois bien que je chemine dans un réseau sans tellement en croiser dautres. Solen réagit : « Je crois que partout, dans les réseaux de personnes, on peut vite avoir limpression dun réseau fermé sur lui-même . Cest sûr quau sein de lAMAP par exemple, ou des gens que je fréquente on retombe sur des profils assez similaires, socialement, culturellement.. quil y a plein dendroits qui ont beau être proches de chez moi , je ne les fréquente pas, parce que naturellement, on va toujours vers ce quon connaît je pense, les lieux qui nous sont familiers. Pour autant, entre chacun de ces réseaux, il y a des ponts qui se créent.  »
@@ -0,0 +1,15 @@
---
title: '4 octobre 2017'
image_align: left
id: rct_04102017
---
Pour la première fois, je ne marrête pas à la station de métro Basilique mais pousse jusqu’à Université : Ce matin, je rejoint Marie.W dont le contact mavait été donné par Victoria.
Anciennement étudiante à Paris 8 et vivant non loin, Marie ma donné rendez-vous devant luniversité pour me donner loccasion de la découvrir.
En ly attendant je découvre des affiches de lAttiéké datant de fin septembre, faisant état de leur recherche de lieux dont ils ont « besoin pour habiter le monde ». En attendant, accompagné dateliers vélos, cantine, kiosque,..le centre social auto-géré continue dans la rue.
Lorsque Marie me rejoint nous entrons dans luniversité en quête dun lieu pour discuter. La cafeteria en travaux nous pousse à chercher une salle vide pour sy poser. Marie massure quelle navait pas de difficulté à le faire à l’époque de ses études. Pourtant au bout dune dizaine de minutes, après avoir dérangé quelques classes, on ne trouve toujours pas notre bonheur.
Marie a alors lidée daller voir vers les studios, ou elle a passé beaucoup de temps et dont elle connait biens les équipes, si un bureau serait libre. On est invitées à utiliser un studio denregistrement radio, avec tout le dispositif qui va avec. Lexpérience est surprenante : nous nous retrouvons toutes deux autour de cette table, casques sur la tête, et on se prête au jeu..
Marie habite Saint-Denis depuis 5 ans : « Jy suis venu parce que je voulais aller à luniversité de Paris 8 et pour moi c’était très important dhabiter au même endroit que là ou j’étudiais pour pouvoir créer des passerelles entre luniversité et mon chez moi, pour pouvoir inviter des gens, avoir un rapport avec lextérieur. » Comme beaucoup, elle a mis du temps à découvrir Saint-Denis au-delà de luniversité, dautant plus quelle habite dans le secteur proche de luniversité. La taille de Paris-8, véritable université-monde, demande déjà dy consacrer beaucoup de temps.
Plus tard, cest à travers des lieux comme le bar Le Pavillon, la galerie ADADA, le cinéma l’écran ou encore le théatre Gerard Philippe quelle a pu rencontré davantage de personnes dans le centre de Saint-Denis..mais aussi via lAMAP « Court-circuit » de Saint-Denis, qui, pour elle comme pour beaucoup, représente bien plus quune question dalimentation.
« Cest surtout un moyen de se rencontrer et de se connaître entre dyonisiens. On a une liste mail où on se file des infos, quon ai quelques chose à donner, quon cherche un medecin, un plombier..quon informe dune date de concert.. tout ça passe par la liste de discussion. Mon dernier colocataire je lai trouvé par ce biais là.»
Par ailleurs, le rapport quentretien Marie au numérique et plus généralement à la connexion au monde est interessant : Par choix, elle na longtemps pas eu de téléphone, aujourdhui elle en possde un mais sans accè à un internet. Il y a encore quelques temps, elle se rendait au Taxiphone cyber café de son quartier pour acceder à sa boîte mail et effectuer des recherches. Par peur de la surconnexion, de se couper du contact direct avec le monde, Marie multiplie les astuces pour ne pas faciliter sa connexion numérique. Pour autant, des paradoxes se créent. Elle possède notamment une web-trotter, inititalement acquérie pour ses séjours à l a campagne, qui au contraire lui permet de se connecter partout.. mais uniquement sur ordinateur. Elle remarque delle même que les limites quelle se pose bougent avec le temps : «. je triche un peu, cest en train d’évoluer doucement. »
@@ -0,0 +1,15 @@
---
title: '5 octobre 2017'
image_align: left
id: rct_05102017
---
Lorsque javais rencontré Pascale la première fois, elle mavait évoqué la crêperie « La Bigoudène » comme un lieu assez fréquenté par les Dyonisiens. Je profite davoir du temps ce midi pour y déjeuner.
Un rayon de soleil bienvenu me permet de minstaller en terrasse, avec vue sur la Basilique. Autour de moi, jentend les discussions dun service de mairie venu déjeuner là.
Il est 16H lorsque je retrouve Olivier au 4 Place Paul Langevin, local de lAMAP Court-circuit.
Jai pu rencontrer Olivier par le biais dElodie, membre de synsthésie. Elle sest installée recemment sur Saint-Denis dans la colocation dOlivier dont elle a eu vent par la mailing list de lAMAP.
Ce nest donc pas par hasard que nous nous retrouvons dans ce local, car si ma rencontre avec olivier est relié à lAmap, pour lui, cest toute sa vie dyonisienne qui lest.
En effet, arrivé il y a 3 ans à Saint-Denis, cest par la rencontre dune « amapienne » comme il dit, quil sest inscrit à court-circuit et quil a pu souvrir sur la ville :
« Cest là que mon réseau Dyonisien à commencé à souvrir. Au début je venais pour mes paniers de légumes et petit à petit jai commencé à faire connaissance avec les gens, à utiliser le réseau. La liste de discussion est très active, sur une multitude de sujets. On est 700 inscrits sur la liste, donc 700 foyers, ça donne une nombre de personnes impressionnant. Il y a même un framapad pour se communiquer tous les bons plans, les bonnes adresses dartisants, de restaurants, de spécialistes médicaux etc.. »
Si ce nest pas la première fois quon me parle de lAMAP comme dun réseau qui permet de souvrir sur Saint-Denis je me pose pour autant la question de lenvergure de cette ouverture : Est-ce un réseau particulier ou un point de rencontre entre des réseaux, formé personnes dhorizons, modes de vies et cultures très différentes ? Olivier me fait part dun constat surprenant : « LAMAP est ici situé dans la cité Langevin, et je crois quon a aucun inscrit dans les habitants de la cité, ce qui peut être assez étonnant.. » LAMAP est géograhiquement située dans cette cité par lopportunité que représentait ce local, mais ne touche pas pour autant la population avoisinante. Je le questionne justement sur cette problématique de mixité sociale et culturelle, dont jai cru comprendre par solen et leur projet de café culturel quelle lui était importante. Olivier a en effet cette envie de créer un lieu féderateur, capable de rassembler et nourir les gens en toute simplicité. Sil est davantage séduit par une idée de lieu nomade, pouvant aller aux devant des autres, partir à la rencontre despaces plus isolés, il nexclu pas la piste de sassocier avec le nouveau centre social coopératif porté par Nadia et envisage de porter la partie café.
Olivier avait entendu parler de Nadia et son projet au hasard dune conversation avec une amapienne.. Nous concluons l’échange sur son constat : « Avoir du réseau ça simplifie vraiment les choses. » 
@@ -0,0 +1,11 @@
---
title: '5 septembre 2017'
image_align: left
id: rct_050917
---
Il est 14H lorsque je pars à la rencontre de Martine, dont j'ai eu le contact par Victoria. Nous avons rendez-vous au café le Basilic, sur la place de la Basilique à Saint-Denis. Nous y commandons deux cafés.
Lentretien avec Martine est dense: Sociologue et habitante de Saint-Denis depuis 1986, elle porte un regard riche et critique sur sa ville. Malgrès tout, elle confesse : «  Moi jhabite ici depuis 86 et finalement, je connais un peu le centre ville car jy habite, je connais la fac parce que jy ai été amené à y bosser, mais il y a des quartiers, ou je n ai jamais mis les pieds.  En centre ville, on a tendance à dire qu’« on » est Saint Denis. Mais en réalité lorsque lon parle de Saint Denis, on parle ou dune grande entité abstraite ou dun petit bout de quartier quon connaît. »
Sur ce constat, j’évoque la raison de ma venue et la notion de réseau qui se dégage du projet. Martine souligne :
« Lidée de réseau, cest compliqué car cest une notion abstraite. Il y a une époque ou jentendais beaucoup « Saint Denis est un village ». Saint-Denis est un village, cest vrai, mais dun petit noyau de gens. Par rapport à la Grande ville cest pas vraiment un village, peut etre DES villages.. »
Cette remarque minvite à préciser lobjectif de cette expérience: La réalité est que je viens à la rencontre dun réseau de Saint-Denis, parmis tant dautres. Ce sont mes premières rencontres qui on axé un cheminement vers dautres personnes et le réseau quelles constituent. Lexpérience que je mène ici ne pourra donc jamais prétendre à avoir rencontré «les habitants de Saint-Denis » mais bien « des »habitant de , voire dun, Saint-Denis.
@@ -0,0 +1,13 @@
---
title: '6 juillet 2017'
image_align: left
id: rct_060717
---
Ce matin, jai rendez-vous par Skype avec [Marie.P](), avec qui jai brièvement échangé par mail, pour un entretien à distance. La sonnerie retenti sur mon ordinateur, nous sommes « là » et je découvre le visage de [Marie](), dans son bureau baigné de soleil. Je suis au [Havre,]() elle à Paris, et durant lheure qui suit nous discuterons ensemble de Saint-Denis. Je lui explique le projet en quelques mots et linvite à se présenter.
Marie enseigne à [Paris 8]() depuis 4 ans mais nhabite pas Saint-Denis. Lorsquelle a commencé à y enseigner, elle a vite constaté quavec le metro qui va directement de Paris à luniversité, il est facile de circonscrire son réseau exclusivement à luniversité sans jamais marcher autour de la fac. «  Ce que jessaye de faire dans certains de mes séminaires à luniversité cest justement dessayer den sortir et de comprendre comment on peut travailler dehors. On a choisi le centre ville de Saint-Denis où on a travaillé notamment avec Synesthésie et La Maison Jaune. »
Durant lentretien, nous abordons le rapport au numérique et Marie mexplique quelle considère quInternet fluidifie la communication mais quil y est aussi beaucoup question de partage, de mise en commun au-delà de la mise en relation. Elle précise :
« Je men sers aussi beaucoup pour faire des recherches, comme un espace de connaissance.
Jactualise mon [site internet]() et jai deux blogs pédagogiques ou jessaye de poster des choses « utiles ». Internet propose des lieux de recherche sur la pédagogie collective ou je peux aussi toucher plus d’étudiants.»
Communiquant essentiellement par mail, Marie estime quInternet est fondamental pour elle. Avec ses étudiants, les outils numériques lui permettent par ailleurs de travailler en commun sans que tout le monde soit disponible au même moment, de faire vivre un projet avec plus dautonomie.
Cest dailleurs avec l'une de ses anciennes étudiantes, [Victoria](), que Marie choisi de me mettre en contact, par [mail]().
@@ -0,0 +1,12 @@
---
title: '6 juin 2017'
image_align: left
id: rct_060617
---
Je fais aujourd'hui la connaissance de [Loyce](), en stage à Synesthésie et étudiante à [Paris 8]().
Je teste avec elle le format dinterview que j'ai imaginé et la mise en contact avec une tierce personne. Elle menvoie vers [Marie.P](), artiste et enseignante à Paris 8 : elle a été l'une de ses étudiantes.
[Bachir,]() qui a justement travaillé avec elle lors dun atelier à Synesthésie, nous met en lien directement par mail.
Dans l'après-midi, je découvre lemplacement de la [Maison Jaune](), alors fermée. Sur la devanture, une affiche présente un projet de [Tricot Partage](img_tp), porté par une certaine [Pascale](). Je retrouve les infos sur la[ page Facebook]() et contacte Pascale pour lui proposer une rencontre.
Peu familière de lenvironnement, laspect labyrinthique de la [Dalle](), faite de coins et de recoins, ne minvite pas à my ballader : la formation de lespace accentue chez moi le sentiment d’être perdue.
Avant de repartir, je déambulle dans le centre de Saint-Denis, marchant au hasard. Jessaye de me rendre plus familière du centre mais ai tendance à graviter peu loin de la [station de métro ]()que je connais.
+46 -24
View File
@@ -1,53 +1,75 @@
--- ---
title: Andrea title: Andreea
image_align: left image_align: left
id: int_andreea id: int_andreea
--- ---
Andreea Cerguta ; [ 11 Juillet 2017] Rencontrée le 11 Juillet 2017 dans la boutique [Franciade](#mp_franciade#img_fr).
rencontrée le 11 Juillet 2017 dans la boutique Franciade.
Javais **auparavant** découvert Franciade en faisait des _recherches_ web sur Saint Denis et m’était rendue à Franciade lors de ma premiere visite à Saint-Denis. Javais découvert [Franciade](#img_fc) auparavant en faisait des [recherches]() internet et my était [rendue](#mp_franciade) lors de ma [premiere visite](#rct_110417) à Saint-Denis.
**Êtes-vous à lorigine de Franciade ?**
**Depuis quand travaillez-vous à Franciade, est-ce que vous êtes à lorigine de cette structure ?**
Non pas du tout, moi je suis arrivée il y a un peu plus de trois ans, mais ça va faire treize ans que lassociation existe. Tout était déjà lancé quand je suis arrivée. Non pas du tout, moi je suis arrivée il y a un peu plus de trois ans, mais ça va faire treize ans que lassociation existe. Tout était déjà lancé quand je suis arrivée.
Est-ce que vous pouvez mexpliquer un peu lhistoire de lassociation ?
**Est-ce que vous pouvez mexpliquer un peu lhistoire de lassociation ?**
Lassociation Franciade a donc 13 ans et la boutique dans laquelle nous nous trouvons a déjà 7 ans. Le but de lassociation est de valoriser le patrimoine de la ville de Saint-Denis et le savoir faire des artisans, à lorigine c’était surtout des céramiques qui sont enfait des répliques des objets trouvés dans les fouilles du site archéologique mais on ne se restreint pas aux céramiques. Beaucoup de savoir-faires différents sont représentés à travers la boutique. Lassociation Franciade a donc 13 ans et la boutique dans laquelle nous nous trouvons a déjà 7 ans. Le but de lassociation est de valoriser le patrimoine de la ville de Saint-Denis et le savoir faire des artisans, à lorigine c’était surtout des céramiques qui sont enfait des répliques des objets trouvés dans les fouilles du site archéologique mais on ne se restreint pas aux céramiques. Beaucoup de savoir-faires différents sont représentés à travers la boutique.
Jimagine que cette diversité dacteurs amène aussi une pluralité de projets ?
Oui tout à fait on ne reste pas que dans la production dobjets, on participe a beaucoup de projets qui nont rien a voir avec la céramique. On est en ce moment sur le projet «  la basilique vue de chez moi » avec les habitants qui ont vus sur la fameuse basilique. **Jimagine que cette diversité dacteurs amène aussi une pluralité de projets ?**
Oui tout à fait, on ne reste pas que dans la production dobjets: on participe a beaucoup de projets qui nont rien a voir avec la céramique. On est en ce moment sur le projet « la basilique vue de chez moi » avec les habitants qui ont vus sur la fameuse basilique.
Cest un projet d’édition de photo et de témoignage et leur rapport a la basilique. Cest un projet d’édition de photo et de témoignage et leur rapport a la basilique.
Comment est-ce que vous réunissez ces différents acteurs  ?
Ce sont les années de travail de la directrice Carine qui font quelle a un grand réseau de contact, et parfois on répond à des appels à projets, dautres fois on nous contacte de lextérieur.
On fonctionne beaucoup avec les appels à projets car la [boutique](/?rct=r110417&map=mp_maison_jaune) ne suffit pas à faire vivre lassocation et payer tous les salaires de Franciade. On a aussi des projets quon auto finance et quon lance nous même.
Comment est-ce que vous vous ancrez dans le territoire ? **Comment est-ce que vous réunissez ces différents acteurs  ?**
Déjà, nos clients se fidélisent car ils nous font confiance et parlent de nous à lextérieur. Nous on essaye de se faire connaître aussi par des flyers et par Facebook car on est pas très bien placés et on a pas beaucoup de passage. On doit donc ramener des gens de lextérieur qui ne passeraient pas autrement ici. On utilise donc les réseaux sociaux, ça nous permet de rappeller aux gens quon est présents.
Ce sont les années de travail de la directrice qui font quelle a un grand réseau de contact, et parfois on répond à des appels à projets, dautres fois on nous contacte de lextérieur.
On fonctionne beaucoup avec les appels à projets car la [boutique](https://www.franciade.fr?target=_blank) ne suffit pas à faire vivre lassocation et payer tous les salaires de Franciade. On a aussi des projets quon auto finance et quon lance nous même.
**Comment est-ce que vous vous ancrez dans le territoire ?**
Déjà, nos clients se fidélisent car ils nous font confiance et parlent de nous à lextérieur. Nous on essaye de se faire connaître aussi par des flyers et par [Facebook](https://www.facebook.com/profile.php?id=100010285387327?target=_blank) car on est pas très bien placés et on a pas beaucoup de passage. On doit donc ramener des gens de lextérieur qui ne passeraient pas autrement ici. On utilise donc les[ réseaux sociaux](), ça nous permet de rappeller aux gens quon est présents.
**Cest plus efficace pour vous les réseaux sociaux que le site internet ?**
Cest plus efficace pour vous les réseaux sociaux que le site internet ?
Oui car on en a pas la même utilité, sur le site on présente surtout lassociation et ses projets mais on ne présente pas les produits que lon vend. Cest surtout pour archiver les 13 années dactivités, avec tous les artistes qui passent, cest bien davoir un endroit ou lon peut retrouver la trace de tout ça. Oui car on en a pas la même utilité, sur le site on présente surtout lassociation et ses projets mais on ne présente pas les produits que lon vend. Cest surtout pour archiver les 13 années dactivités, avec tous les artistes qui passent, cest bien davoir un endroit ou lon peut retrouver la trace de tout ça.
Je vois sur le comptoir un flyer qui indique «  racontez nous votre ville », quest ce cest ?
**Je vois sur le comptoir un flyer qui indique « racontez nous votre ville », quest ce cest ?**
Ca cest un projet auto-financé, en cours, ou lambition cest d’écrire un « guide amoureux de la ville », on a recueilli des témoignages, commencé des ateliers d’écriture. Ca cest un projet auto-financé, en cours, ou lambition cest d’écrire un « guide amoureux de la ville », on a recueilli des témoignages, commencé des ateliers d’écriture.
Est-ce que ça a été facile de féderer les gens ?
**Est-ce que ça a été facile de féderer les gens ?**
Oui, réunir les gens et récolter les témoignages ça a été facile, le plus compliquer cest de financer pour produire le guide et payer les gens qui travaillent à le mettre en forme. Oui, réunir les gens et récolter les témoignages ça a été facile, le plus compliquer cest de financer pour produire le guide et payer les gens qui travaillent à le mettre en forme.
En tant quhabitante, est-ce que vous êtes liée à St denis ?
Pas tellement mais cest vrai que la plupart des choses que je fais cest en lien avec lassociation car on fait tellement de choses ici..
Franciade travaille avec dautres asso ? **En tant quhabitante, est-ce que vous êtes liée à St denis ?**
Pas tellement mais cest vrai que la plupart des choses que je fais sont en lien avec lassociation car on fait tellement de choses ici..
**Est-ce que Franciade travaille avec dautres structures ?**
Oui, un petit réseau comme Artefact, la coopérative Pointcarré, Adada.. il y a énormément dartistes et dinitiatives ici. Tout le monde connaît quelquun qui connaît quelquun… et les projets foisonnent ! On a fait des choses avec la maison des seniors aussi. Oui, un petit réseau comme Artefact, la coopérative Pointcarré, Adada.. il y a énormément dartistes et dinitiatives ici. Tout le monde connaît quelquun qui connaît quelquun… et les projets foisonnent ! On a fait des choses avec la maison des seniors aussi.
Javais croisé Dechets Dart qui intervenait a la maison des seniors..
**Jai justement croisé l'association Dechets Dart qui intervenait a la maison des seniors..**
Oui, ils font partis dArtefact aussi, on connaît bien tous les acteurs de ce groupe là. Oui, ils font partis dArtefact aussi, on connaît bien tous les acteurs de ce groupe là.
Et dans votre vie quotidienne, quelle place ont les outils numériques ?
**Et dans votre vie quotidienne, quelle place ont les outils numériques ?**
Dans le travail, on utilise beaucoup les mails, tout le temps, entre nous pour sorganiser et monter les projets. Dans le travail, on utilise beaucoup les mails, tout le temps, entre nous pour sorganiser et monter les projets.
Dans ma vie personnelle ça a une importance énorme car ma famille habite ailleurs. Dans ma vie personnelle ça a une importance énorme car ma famille habite ailleurs.
Ca nous facilite la vie. Ils sont en Roumanie alors skype, cest tous les jours.. je pourrais les appeler mais cest pas pareil. Avec mes amies aussi, elles sont éparpillées un peu partout dans le monde donc jaurais du mal à men passer. Cest sûr que je préférerais un contact face à face, mais sans ça je ne sais pas comment je pourrais tenir sans voir ma famille, là je les vois. Ca nous facilite la vie. Ils sont en Roumanie alors skype, cest tous les jours.. je pourrais les appeler mais cest pas pareil. Avec mes amies aussi, elles sont éparpillées un peu partout dans le monde donc jaurais du mal à men passer. Cest sûr que je préférerais un contact face à face, mais sans ça je ne sais pas comment je pourrais tenir sans voir ma famille.., je les vois.
Alors, une dernière question avec qui est-ce que vous pourriez me mettre en contact ? Pour la suite de votre projet, vous pouvez aller voir Elie ou Wiebke de [Point Carré](#mp_poincarre#img_pointcarre). Passez les voir directement, il y a plein de choses à découvrir là bas.
Elie ou Wiebke de Point Carré. Passez les voir directement, il y a plein de choses à découvrir là bas.
@@ -4,17 +4,19 @@ image_align: left
id: int_manon id: int_manon
--- ---
Manon; [12 septembre ] Rencontrée le [12 septembre 2017](#rct_120917) à [La Défense](#mp_defense).
renconte le 12 septembre 2017 à la Défense Cest [Wiebke]() qui ma évoqué le travail de Manon lors de mon passage à Point Carré le [12 Juillet](#rct_120717).
Cest Wiebke qui ma évoqué le travail de Manon lors de mon passage à Point Carré le 12 Juillet.
Wiebke ma parlé de toi en évoquant un travail de cartographie que tu avais mené sur les reseaux dartistes de Saint Denis, est-ce que tu peux me parler un peu de tout ça ? **Wiebke ma parlé de toi en évoquant un travail de cartographie que tu avais mené sur les reseaux dartistes de Saint Denis, est-ce que tu pourrais m'en dire un peu plus ?**
Je vais texpliquer un peu mon parcours. Jai fait mon mémoire sur la place des collectifs dans la production urbaine et pour ça jai étudié deux collectifs dans Paris. A loccasion dun projet qui croisait mon sujet de mémoire jai travaillé avec « Grand Paris aménagement » ou jai fini par entrer en stage. A loccasion de ce stage jai rencontré pas mal de monde à Aubervilliers dont les Frères poussières, la villa Medicis..et pleins dautres. La villa medicis cest un lieu de résidence ou tu peux rencontrer pleins dartistes, pleins de compagnies.. là bas jy ai rencontré iIris, ladministratrice, et Garance, qui était en alternance .. bref jai rencontré des gens qui sont devenus des amis et jai adoré travailler là bas.
Je vais texpliquer un peu mon parcours : jai fait mon mémoire sur la place des collectifs dans la production urbaine et pour ça jai étudié deux collectifs dans Paris. A loccasion dun projet qui croisait mon sujet de mémoire jai travaillé avec « Grand Paris aménagement » ou jai fini par entrer en stage. A loccasion de ce stage jai rencontré pas mal de monde à Aubervilliers dont les Frères poussières, la villa Medicis..et pleins dautres. La villa medicis cest un lieu de résidence ou tu peux rencontrer pleins dartistes, pleins de compagnies.. là bas jy ai rencontré iIris, ladministratrice, et Garance, qui était en alternance .. bref jai rencontré des gens qui sont devenus des amis et jai adoré travailler là bas.
Après ce mémoire et après ce stage, où javais donc travaillé avec Elsa vivant, elle ma proposé de laccompagner dans le labo de recherches en sciences sociales ou elle travaille, pour étudier les réseaux artistiques en Seine Saint-Denis, parce que jy avais déjà beaucoup dentrées dues à mon précédent travail sur Aubervilliers. Javais aussi déjà rencontré pas mal dacteurs du coin par le biais de Grand Paris aménagement. On avait rencontré déjà des gens de la Briche, de Synesthésie, du 6B... Lidée c’était de comprendre les liens entre les lieux plutôt institutionnels et les lieux plutôt alternatifs. Jallai donc voir soit des lieux, soit des artistes, pour quils me parlent de leur parcours, de leurs réseaux etc.. et c’était génial parce que je rencontrais de personnes en personnes. Après ce mémoire et après ce stage, où javais donc travaillé avec Elsa vivant, elle ma proposé de laccompagner dans le labo de recherches en sciences sociales ou elle travaille, pour étudier les réseaux artistiques en Seine Saint-Denis, parce que jy avais déjà beaucoup dentrées dues à mon précédent travail sur Aubervilliers. Javais aussi déjà rencontré pas mal dacteurs du coin par le biais de Grand Paris aménagement. On avait rencontré déjà des gens de la Briche, de Synesthésie, du 6B... Lidée c’était de comprendre les liens entre les lieux plutôt institutionnels et les lieux plutôt alternatifs. Jallai donc voir soit des lieux, soit des artistes, pour quils me parlent de leur parcours, de leurs réseaux etc.. et c’était génial parce que je rencontrais de personnes en personnes.
Dans ce cadre là jai rencontré notamment Wiebke, découvert la Briche etc. Dans ce cadre là jai rencontré notamment Wiebke, découvert la Briche etc.
Comment tu prenais connaissance de ces lieux ou ces artistes ? Comment tu rentrais en contact avec eux ?
**Comment prenais-tu connaissance de ces lieux ou ces artistes ? Et comment entrais-tu en contact avec eux ?**
Mon entrée principale était vraiment les lieux. Par exemple, pour la villa Medicis jai rencontré ladministratrice qui ma dit « tiens, va voir machin », à chaque fois que je rencontrais quelquun il me disais «  ah mais tu a déjà été voir machin ? » qui était aussi dans la ville, mais c’était dans un lieu bien défini. Mon entrée principale était vraiment les lieux. Par exemple, pour la villa Medicis jai rencontré ladministratrice qui ma dit « tiens, va voir machin », à chaque fois que je rencontrais quelquun il me disais «  ah mais tu a déjà été voir machin ? » qui était aussi dans la ville, mais c’était dans un lieu bien défini.
Le 6B cest pareil, je me suis rendu sur le lieux et jai pris les contacts quil y avait au rez-de-chaussé, directement sur place. Après dans le 6b c’était du bouche à oreille. Le 6B cest pareil, je me suis rendu sur le lieux et jai pris les contacts quil y avait au rez-de-chaussé, directement sur place. Après dans le 6b c’était du bouche à oreille.
Wiebke par exemple, j’étais passée par une personne de la villa Medicis, qui mavait transmis les coordonnée dOriane qui était venue à un moment à la villa medici pour des automates et qui a coté de ça bosse en tant quartisan dans un atelier de la Briche donc que jai rencontré. On a bien discuté, elle ma fait rencontrer des gens de la Briche et ma parlé du travail de Wiebke en me disant « elle a un super projet sur la toison dyonisienne, la laine etc » et voilà jai donc rencontré Wiebke. Wiebke par exemple, j’étais passée par une personne de la villa Medicis, qui mavait transmis les coordonnée dOriane qui était venue à un moment à la villa medici pour des automates et qui a coté de ça bosse en tant quartisan dans un atelier de la Briche donc que jai rencontré. On a bien discuté, elle ma fait rencontrer des gens de la Briche et ma parlé du travail de Wiebke en me disant « elle a un super projet sur la toison dyonisienne, la laine etc » et voilà jai donc rencontré Wiebke.
@@ -0,0 +1,66 @@
---
title: Marie.W
image_align: left
id: int_mariew
---
Marie.W
A Paris 8 dans le studio radio le 04 octobre 2017 à 10h30.
Cest Victoria qui ma donné son contact.
Peutx-tu me parler de ton rapport à Saint Denis
Jy habite depuis 5 ans. Jy suis venu parce que je voulais aller à luniversité de Paris 8 et pour moi c’était très important dhabiter au même endroit que là ou j’étudiais pour pouvoir créer des passerelles entre luniversité et mon chez moi, pour pouvoir inviter des gens, avoir un rapport avec lextérieur. C’était ma vision de ce que ça devait être. Jai toujours habité près des endroit ou jai étudié et ou jai travaillé et pour moi cest une donnée assez importante. Peut etre en regard aussi que jai pas de voiture, jessaye de faire tout à pied ou en vélo. Je me suis donc implantée à St Denis dans une petite maison en colloc et finallement après mes études jy suis restée parce que jai découvert une ville que je naurais jamais découvert autrement. Quand je suis arrivée à Paris au départ jhabitais Paris et j ‘étais à la Sorbonne, cest plus tard que jai eu envie de découvrir Paris 8.
Comment tu as eu envie dy aller ?
Jai fait mes études à Nancy en licence et mon rêve était daller à Paris, jy vais et je commence un master de lettre à Paris. Finallement le parcours tout tracé ne ma pas convenu et à la suite de ça je pars en stop pendant 2 ans en Europe et jy découvre les milieux alternatifs. Au cours de cette expérience je me questionne sur mon mode de vie et notamment mon rapport au collectif. Un jour une amie minvite à une réunion féministe à Paris 8, que jai trouvé super interessante. Je fais cependant remarquer à mon amie que je trouve quune personne prend quand même beaucoup la parole et elle me dit : Cest la prof. Cest un cours de philosophie. Et là je me dit Ok, il faut que je vienne à Paris 8. Je suis entrée donc à nouveau en Master de lettre mais avec un tout autre parcours, un tout autre profil.
Tu me disais que tu habites à coté de luniversité, comment est-ce que tu as limpression de connaître le reste de Saint Denis ?
Au début, j’étais vraiment très présente à Paris 8 parce que cest une université-monde, cest tellement énorme que ma vie c’était à Paris 8. Je ne connaissais donc même pas tant mon quartier que ça surtout que cest un espace qui est en train de bouger, davoir de plus en plus de commerces mais au départ yavais pas encore le tram, pas bcp de commerce donc pas bcp de vie dans ce quartier. Je sortais très peu à Saint Denis dans le centre. Par contre ma rue cest des anciennes maisons ouvrières et donc ya tout un tas de gens qui sont là depuis 40 ans , qui sont très aidants les uns envers les autres avec un rapport vraiment chouette entre les habitants. Ma voisine prend le courrier de toute le monde quand on est pas là.. Cest vraiment depuis lannée dernière que jai réussi un peu à quitter le cocon Paris 8 et à mintégrer à Saint Denis et finalement du coup cest principalement dans le centre de St Denis. Jai peu de rapport avec mon quartier à part avec mes voisins. Je croise aussi des habitants parce que je vais au taxiphone.
Le taxiphone ?
Cest comme un cyber café. Jusqu’à très recemment je navais pas Internet chez moi, par choix.
A Paris 8 avec la Bibliothèque ouverte jusque 20H je nen avais pas besoins et ça me permettais de segmenter un peu des parties de ma vie avec des espaces connectés et des espaces non connectés. Je nai pas de Smartphone non plus. Jai été un peu prof et parfois ça ma amené a devoir travailler un peu plus tard quand même et donc jallais au TaxiPhone. Je trouve que cest un endroit extraordinaire, à la fois il vend des clopes, des cafés, il récupère le courrier, il fait des photocopies, tu peux utiliser Internet, appeler en Afrique.. Et du coup par ce biais là je connais des gens du quartier que jy croisais régulièrement et que jaurais jamais rencontré par ailleurs. Tu y rencontre des gens qui ne parlent pas forcément français, tu y croise des sphères sociales et culturelles. Cest aussi que cest un endroit très peu cher et ty va tel que tes avec des besoins élémentaires de café, dappel, de connexion web, ça marche bien. Cest un lieu très vivant ou on se rencontre sans se rencontrer mais à force de sy croiser on discute, on crée du lien. Cest un lieu qui fait vraiment partie de mon intégration de quartier.
Mais sinon cest vrai que les gens que jai rencontré à st Denis ça a quand même été plus en centre ville à travers des lieux un peu symboliques à Saint Denis, comme le bar Le Pavillon, la galerie ADADA, le cinéma l’écran, le théatre Gerard Philippe. Les habitudes un peu des bobos de St Denis..Pour le coup à ce niveau là ça ressemble à un village. Tu regarde quand tu passe au Pavillon si ya pas des gens que tu connais, parce que cest un lieu qui se trouve un peu dans un carrefour et ou tu vois tout de suite les gens qui y sont installés. Depuis que je mintègre dans le centre ville je découvre st denis comme un village.
Est-ce que tu as limpression de découvrir un village dans une ville, parmis des villages?
Cest vrai qu’à la fois je trouve quil y a un milieu alternatif assez étendu et en meme temps..
Pourquoi le Pavillon est symbolique à St Denis cest parcque dans bcp de bars de st denis tu va trouver quasiment que des hommes, alors quau Pavillon cest un bar vraiment mixte , qui a un emplacement géographique assez stratégique, avec une bonne visibilité. Et le patron a une grande ouverture desprit. Tu peux y proposer des choses un peu insolites, des concerts, nous on y a fait une performance artistique avec une amie. Cest pour ça que ce lieu a une grande importance.
Tu fais des activité dans ST Denis ?
Je fais partie dun réseau qui est devenu vraiment important pour moi et pour bcp de gens je pense à St Denis qui est lAMAP. Et enfait cet amap cest plein de choses, cest plus quune amap. Manger des bons légumes cest cool mais cest presque un pretexte. Cest surtout un moyen de se rencontrer et de se connaître entre dyonisiens. On a une liste mail où on se file des infos, quon ai quelques chose à donner, quon cherche un medecin, un plombier..quon informe dune date de concert.. tout ça passe par la liste de discussion. Et ça marche très bien, tu envois un mail tu es sûr davoir une réponse. Mon dernier colocataire je lai trouvé par ce biais là. Ya même des gens qui ne sont plus à LAMAP, parce quils ont déménagé par exemple, et qui restent inscrits sur les listes de discussion pour avoir les infos.
Et pour être reliées à cette chaine de mails, tu le fais depuis le Taxiphone ? Tu nas toujours pas internet chez toi ?
Non maintenant jai une web-trotter, ce qui me permet davoir internet aussi quand je vais dans ma maison en normandie par exemple. Cest une petite boîte que tu peux trimballer partout et que tu branche en usb pour avoir le Wifi. Donc cest un peu comme une box que tu peux transporter facilement pour te connecter à la campagne, dans le train.. Donc je triche un peu, cest en train d’évoluer doucement.
Donc finallement pour quelquun de déconnecté tu es plutôt ultra connectée ?
Oui cest vrai mais je nai pas de smarthpone donc il faut que jai accès à un ordinateur si je veux me connecter, ce qui me met dans une autre configuration où je ne suis pas tout le temps connecté.
Je pense que dans le réseau alternatif ya encore beaucoup de gens comme moi qui ont des vieux téléphones basiques qui appellent et envoient des sms pour être joignable mais pas connectés en permanence. Pour ma part je privilégie toujours le contact direct au téléphone. A un moment donné je navais quun téléphone fixe, puis pendant quelques temps plus de téléphone du tout donc il fallait se fixer des rendez-vous. Mais on a pu me repprocher de faire subir mon mode de vie aux autres, par exemple une fois le rdv fixé on ne pouvait pas me prévenir dun retard, décaler dune demi heure etc..
Maintenant jai un téléphone portable mais jessaye de ne pas avoir de Smartphone. Je vois déjà l’évolution avant javais ni internet ni téléphone et déjà jai ma web-trotter et un téléphone. Cet englobement me fait peur, je nai pas envie davoir une vie virtuelle. Je veux bien que ce soit un outil qui me permette davoir un contact humain direct, mais pas que ça remplace les échanges directs. Mon usage du numérique se cantonne aux recherches et aux mails, je nai pas Facebook, pour des raisons idéologiques aussi, je nai pas twiter, pas snapchat, je ny connais rien.
Tu dissocie complétement une virtuelle dune vie physique ?
Oui, car jai limpression quon se crée une image de ce quon veut montrer de soit et jai pas envie dentrer dans cette logique, en montrant des choses attrayantes me concernant pour quon ai envie de me rencontrer.
Ca cest plus dans le concept du réseau social, mais loutil mail qui te permet de densifier ton réseau, par exemple avec lamap, peut difficilement être dissocié de ta vie physique non ?
Cest vrai mais en même temps ce qui est étrange parfois cest que des personnes vont me répondre, par exemple sur les discussions de lAMAP, et je ne sais pas qui elles sont. Ca engendre quand même une frustration. Par exemple lorsque jai cherché mon colocataire sur cette liste mail, des personnes mont répondu car mon annonce leur avait donné envie de me rencontrer, bien quils ne cherchaient pas de logement dans limmediat. Ce que jaime bien par exemple au Pavillon cest le fait que ça puisse etre une rencontre spontannée, physique. Jai limpression quavant, quand on avait pas de téléphone portable, il y avait les café ou on savait que vers tel heure on pourrait y rencontrer des connaissances, au Pavillon cest un peu comme ça.
Et il y encore un peu ça à St Denis parce que cest une ville ou, jai limpression, les gens sont très connectés, ya beaucoup de choses qui se passent, beaucoup de réseau. Et je trouve ça mieux que le réseau informatique , même si on peut dire que ça fonctionne ensemble, mais je pense que cest parcque les gens font des choses ensemble physiquement que ce réseau est interessant.
Du coup là effectivement on parle pas dune « autre vie », cest juste un outil derrière tout l’échange social entre les gens. Et ce qui minquiètes cest que cet outil prenne plus de place et « devienne la vie ». 
A saint Denis ya aussi un truc très pragmatique qui fait que cest pas le cas, cest quil y a des gens dans le milieu alternatif qui sont dans le refus de Facebook, Twitter, etc , nont pas de smartphone etc et concretement pour les voir il faut se débrouiller autrement. Et il y a aussi le fait que St Denis soit une ville pauvre, tout le monde nest pas équipé pour être connecté et il y a une sorte dinstabilité de la connexion qui pousse + les gens à se parler. Des fois, à mal se parler, cest sûr, mais en étant dans un échange direct.Il y a beaucoup de groupes de gens dans la rue qui discutent des heures de jour ou de nuit. Il y a une vraie facilité communicationnelle, même si bien sûr il reste des groupes. Mon ancien colloque, en 3 mois il connaissait tout Saint Denis..
Victoria ma évoqué lAtiéké, est-ce que tu saurais men parler un peu ?
Jai suivi surtout les débuts de lAtiéké, il nexiste plus aujourdhui, il y a eu des expulsions.
Ca a été un lieu qui a été important et vraiment interessant. Ils ont réussis à proposer des activités multiples pour des publics assez différents, à la fois atelier vélo, dalphabétisation, pour les enfants, des projections militantes, des débats, un lieu de vie. Ils ont vraiment réussi à en faire un lieu social auto-géré. Cest à dire qui répond à des demandes sociales, qui est ouvert sur lextérieur et pas un squat fermé et qui reste dans lentre-soit. Il y avait une vraie implication dans le territoire et ça à vraiment marché. Tallais en soirée à lAtiéké, tu croisais toute sorte de gens, de mentalité, de culture, sans être maté à lentrée, sans même payer, c’était prix libre.
Cest resté 2 ans et demi/3 ans sur le territoire, donc avec un vrai temps de simplanter, alors que souvent les squat ça a une durée de vie assez courte, plutot dun an.
Comment marchait la communication ?
N’étant pas très connectée jai peut-être loupé des choses mais en tous cas il y avait des mails pour les grands énévements, des sms.. des affiches aussi car il y avait une vraie volontée douverture.
Mais en réalité, généralement cest plus facile dentrée dans un squat si tu y connais quelquun.. Cest pour ça aussi que sur lAtiéké en particulier il y a eu un travail sémantique sur le fait dappeller ça un centre social auto géré et non un squat, pour pas renvoyer à un imaginaire «  sale, malpropre, drogue, illégal..) alors que centre social on parle tout de suite dun lieu de vie ouvert.
Le bouche à oreille est super important.
Dans les squat moi que jai pu rencontrer à loccasion de mon voyage en europe c’était des squats un peu plus fermés donc ça fonctionne plus par code, dans le sens ou tu rentre dans le groupe soit parce que tu y connais quelquun soit parce que, dans ta façon de texprimer, de te vétir, tu répond aux même « codes », tu es reconnu par le groupe. Par exemple parce que tu fait du stop, de la récup sur un marché.. ya une sorte de reconnaissant sociale qui agit et cest comme ça que tu rencontre le groupe. Ca reste très fermé donc la communication est assez limitée pour se contenter du bouche à oreilles, du contact direct.
Pour terminer peux-tu me dire comment tu as rencontré victoria ?
Jai connu victoria dans un cours dans un master science de l’éduc, on était dans un groupe affinitaire et on a travaillé sur un même projet. Ca a crée un lien entre nous, qui avons passé plus de temps au café à se questionner sur la pédagogie du cours en lui même que sur le sujet de travail de notre groupe.
Je n’étais plus à Paris 8 lorsquelle à monté le collectif Kabane avec dautres étudiants mais jai suivit linitiative.
@@ -0,0 +1,50 @@
---
title: Martine
image_align: left
id: int_martine
---
u café Le Basilic sur la place de la Basilique de Saint-Denis.
Le 5 Septembre 2017 à 14h30.
Victoria Linhares ma donné son contact mail/téléphone par mail à la suite de notre rencontre place de la république.
(Habite ilot 9 immeuble en brique)
Explication du projet..
Effectivement Saint Denis cest un bon endroit pour étudier cette question de réseau..
Quand on dit Saint-Denis, en plus, cest tellement vaste.. moi jhabite ici depuis 86 et finalement, je connais un peu le centre ville car jy habite, je connais la fac parce que jy ai été amené à y bosser, mais ya des quartiers, je ny ai jamais mis les pieds. Et je ny mettrais pas les pieds, ou vraiment occasionnellement, mais je ny connais pas quoi que ce soit.
En te parlant je me rend compte que nous, en centre ville, on a tendant à dire « on » est Saint Denis. Mais en réalité lorsque lon parle de Saint Denis, on parle ou dune grande entité abstraite ou dun petit bout de quartier quon connaît. Je connais un peu la plaine maintenant parce que que jy ai travaillé depuis 2 ans avec des étudiants, mais cest vrai que jai lhabitude de plaisanter sur le fait que « Mon » Saint Denis va de la Fac à la porte de Paris en passant par le théatre gérard philippe.
Comme on est dans du dense, ma connaissance de St Denis est à la fois approfondie sur certains plans et très très approximative sur pleins de choses.
Martine me requestionne sur le projet et ses problématiques
Moi je vois dans ma génération, alors que ça a été une appropriation assez tardive, quil y a une intégration vraiment impressionnante de ces outils, qui on pu nous paraître rébarbatifs au début mais qui sont entrés dans notre vie avec une facilité.. alors que c’était pas du tout notre culture.
Même les ordinateurs, moi au boulot en 90 javais demandé à mon boss un ordi et il me répondait « tes pas secrétaire », bon ben 3 ans plus tard on avait tous des ordinateurs avec nos mails et tout ça. Ya eu un changement de culture très rapide et c’était frappant dans le monde du travail.
Est-ce que tu peux me parler un peu de ton rapport à ce territoire ?
Donc moi je suis arrivée fin 1986 dans un immeuble de la zac basilique, au dessus de Mc Do, un immeuble dHLM. On est tous arrivés un peu en même temps, c’était des immeubles neufs. Moi j’étais au chômage à l’époque et très rapidement avec dautres gens on a monté une association de locataire qui a été très dynamique. Je me suis pas mal occupé de cette amicale là, après on a monté un collectif dasso qui faisait ses activités dans un local juste au dessus du C&A. Le collectif était branché sur des activités comme de la musique pour enfant, théâtre pour enfant, beaucoup de corporel : du yoga, qui est toujours là, du stretching, du théâtre.
En 2003 jai repris des études à la Fac de Paris 8, jai fait un DESS, une branche un peu tordue de la sociologie, qui ma bien plu et puis jai continué en thèse. Jai fait des enquêtes sociologiques avec un prof sur Saint-Denis et je viens de terminer et de soutenir ma thèse en juin. Un long parcours.
Entre temps jai pas mal bossé dans les milieux militants et des projets de quartiers. Avec des copains artistes on avait fabriqué des chars avec lesquels on déambulait dans les quartiers.
Par rapport à lasso de locataires, ya pas mal de choses qui ont changées aujourdhui.
Cest un peu les «  choses qui ne se disent pas ». Nous quand on est arrivés, en 1986 c’était dans un HLM privé. Une logique HLM mais pas lorganisme public, donc un peu plus cher et donc avec une population plutôt « Classe moyenne blanche ». Petit à petit, cette population soit sen va, soit accède à la propriété et peu à peu on assiste à un changement de population. Aujourdhui la grande majorité des familles sont africaines. Ce que ça change, cest quil y a plus de monde, plus de monde dans les espaces communs et on constate aussi un effondrement des associations. Ce qui se passe cest que notre méthode de militants classique ne fonctionne plus. On était très «  ordre du jour/réunions ». Mais on sest rendus compte que ça marchait plus, dabord parce que car beaucoup ne viennent pas aux réunions, Peut-être parce que trop formel, parce quils parlaient pas toujours bien, que les femmes ne viennent pas avec les hommes, toutes sortes de choses qui étaient plutôt de lordre culturel et quon a pas pu anticiper. On pu a trouver des astuces par exemple avec les femmes, on faisait des thés en passant par les copines, etc.. mais pas pour tout.
Pour la communication on a arrêté laffichage, les boites aux lettres, on passe quasiment que par mail avec ses avantages et ses inconvénients, car ceux qui nont pas dadresse mail ou ne sen servent pas beaucoup, ils nont juste pas linfo. Jai décidé de me créer une page Facebook parce que je me suis rendu compte que c’était plus simple pour relayer les infos du quartier, car chacun peut les découvrir aussi par ses propres contacts. Cest un peu une page «  locale » pour moi, jy raconte pas ma vie cest plus une démarche sociale/professionnelle..
Lidée de réseau, cest compliqué car cest une notion abstraite.
Il y a une époque ou jentendais beaucoup « Saint Denis est un village ». Saint-Denis est un village, cest vrai, mais dun petit noyau de gens. Par rapport à la Grande ville cest pas vraiment un village, peut etre DES villages..
Mais en même temps je remarque, notamment en étant dans cet HLM, que la proximité spatiale ne crée pas naturellement de proximité entre les gens. Il y a pleins de choses qui ne se croisent pas. Notre local avait été une grande leçon pour moi. Au début on avait lobjectif classique de défendre linteret du locataire. Mais on a eu envie de choses plus amusantes donc comme on avait ce local, on a ouvert des ateliers, des activités dans le local. On partait du principe quun travail de ce type allait forcément créer une vie sociale dans limmeuble. Mais chemin faisant on sest rendu compte quon avait quasi personnes de limmeuble qui venait. On avait aussi été cambriolé plusieurs fois et on avait su que quelques fois c’était des gamins du coin. Donc jai commencé à réfléchir en prenant un peu le problème à lenvers et je me suis dit, ben peut-être quon le squatte enfait ce local. Peut-être quon simpose et que quelque part les mômes nous disaient « Ben pourquoi nous on y entrerait pas aussi ? ». Dans les réunions, ça commençait à parler de sécuriser et fermer encore plus le local. Mais c’était pas du tout la solution. Du coup on a essayé douvrir beaucoup plus, de laisser lespace. Ya eu tout le travail de Julia aussi avec les jeunes, et au final c’était eux qui nous gardaient la salle. Et puis les gamins pour le coup, ils en ont du réseau. Ils savent tous qui est qui, qui habite où. Quand ya un gouter danniversaire il faut pas 15min pour que tout le monde soit au courant quil y a du gateau dans le coin. Des volets et des clefs, ok, un minimum, mais pas de grilles, pas de digicodes, il faut pas cloisonner tous les espaces.
Tu me disais tout à lheure que tu habite la Zac Basilique, cest étonnant larchitecture de lilot..
Oui cest sur, nous on habite de lautre coté de la Maison Jaune. Cest une drôle darchitecture, avec ses recoins aussi. Mais là ils veulent rénover et mettre des barrière, tout cloisonner. Cest un vrai mépris des usages. Au dessus de Carrefour ils en on foutu partout des grilles, cest immonde.
Si on avait un immeuble classique on aurait pu accepter des digicodes sans trop de problème mais avec larchitecture quon a, avec tous les accès, les ouvertures, on aura que ça partout.
Je vais revenir un peu à tes moyens d’échange, tu me parlais tout à lheure de ta page Facebook, quel rapport tu peux avoir toi à ses outils ?
Javais lhabitude denvoyer des mails à mon fils, pour lui faire part dune chose que javais vu ou lui dire quelque chose, et lautre jour il me dit «  maman cest les vieux qui communiquent par mail ». C’était au moment où jouvrais ma page Facebook et il ma envoyé un petit message dessus, alors maintenant je lui réponds là, sur messenger, et quand je lui envoie une info, un lien, même du salon à sa chambre, je lui envoie sur messenger.
Quand je me suis mise à lordinateur au boulot ça a bougé tres vite, je trouve que ma génération sest adapté super vite. On sest vite mis aux mails, à internet. Au début ça me gonflait et maintenant on sen sert tout le temps. Puis maintenant ya Facebook que je considère pour le moment comme un petit réseau de copains, plutot militant. Je poste un petit peu, tu iras voir ma page, et je relais quelques trucs, pas trop non plus car jai remarqué que ceux qui postent tout le temps ça peu devenir vite fatiguant et linfo se noie. Et je relais pas les histoires de petits chiens, de petits chats..qui mamusent bien par ailleurs. Cest plutot pour moi une manière de relayer. Pour échanger et converser, je suis beaucoup plus mail.
Jai un ami, Pascal, prof à Paris 8 avec qui jai monté la Fabrique des Sociologies, qui ecrit vraiment ses mails, pas comme on peut le faire des fois dans un style un peu oral. Il ecrit, il prend le temps, il formule. On a commencé à beaucoup correspondre un peu à la manière dun journal, on raconte. On reprend un point et on commence à le développer. Et du coup on avance. On pose vraiment nos réfléxions.
Avec tout une bande de doctorants on échange comme ça, par listes de mails, pour organiser nos événements et tout ça.. Pour un projet là, lun dentre nous, plutôt jeune, à ouvert un Wiki, mais nous qui sommes plus agés on est perdus là dessus. Ya pas de mail qui nous dit quil y a une nouveauté sur le Wiki alors on avait fini par un peu labandonner. On s’était remis à faire des mails, mais notre ami nous a relancé pour le Wiki car cest vrai que pour larchive cest mieux. Ca nous demande un effort, cest un peu plus long à intégrer. Une sorte dapproche visuelle qui est différente.
+22
View File
@@ -0,0 +1,22 @@
---
title: Nadia
image_align: left
id: int_nadia
---
On a commencé notre activité type centre social, on est parti sur le principe dun espace de vie sociale et on a commencé dans la cité Gabriel Péri, c’était un petit local résidentiel quon avait négocié avec le Bailleur un petit 2 pièces. Tu retrouveras des photos sur notre page Facebook car jessaye de raconter au fur et à mesure ce qui sy passe pour fédérer un peu. Et là on a négocié avec la ville pour intégrer cette « maison des projets » pour accueillir le projet de centre social en attendant que nos locaux définitis nous soient livrés pour mars 2018.
Donc lassociation coopérence gardera quand même un pied ici par la suite comme base opérationnelle car il y a beaucoup dautres projets à développer. Il y a donc une nouvelle entité juridique qui va naitre avec un statut de centre social coopératif qui sera la premier en France.
Lidée qui porte cette asso depuis le début cest celle dexpérimenter un centre social coopératif, aborder la question sociale en incluant la dimension économique mais pas nimporte laquelle.
On est membres des centres sociaux depuis longtemps, parfois même des administrations des centres sociaux, on connaît bien loutil centre social.
Donc pour le moment cest moi je gère un peu tout, qui trouve les outils pour faire marcher tout ça. Je me débrouille mais cest vrai que jai pas grandit avec et que je le ressens. Jespère trouver chemin faisant les bonnes compétences et les bons outils, car lidée de la coopérative cest aussi de trouver les compétences adéquates à ce quil y a faire dans les membres de la coop. Mettre au service du collectif ses compétences et voir ce que ça génère ensuite comme moyens. On commence pas par les moyens mais par les richesses « humaines » a disposition.
Pour le moment coté « administratif » je fait tout de chez moi car on viens darriver je nai pas encore de matériel, de connexion internet. La page Facebook je la gère de chez moi, le site internet aussi, javais commencé à travailler sur une NewsLetter mais ça me prends trop de temps.
Jai un google groupe où jinscris tous ceux qui me donnent leur coordonnées. On est 140 personnes pour le moment sur ce groupe. Il y a un peu de tout, des associations, des institutions, des habitants.. tout ça se monte au fur et à mesure et je tiens tout le monde au courant. On retrouve les mêmes infos sur ce groupe, dans les mails, que sur la page Facebook. Voilà en gros comment on communique et sinon on utilise les relais locaux, les réseaux de la ville, le JSD..
Mon job cest ça surtout, coordonner, mettre en lien, en relation.. le nom coopérence il ne vient pas de nul part. On pense quil y a quelque chose à creuser autour des territoires, des acteurs, des habitants,.. une intelligence collective avec une dimension territoriale à travailler. Ce projet de centre social cest un exemple, car par définition cest plurigénérationel, polyforme, polymorphe, donc cest parfait pour nous. Tout est possible.
Non je ny habite plus, jy ai habité. Jai travaillé pour cette ville en 90 et en 2013, comme éducatrice spécialisé dabord puis comme directrice de démarche quartier et jai vite basculé sur un poste de chargé de mission sur la création dun centre social dans le quartier de luniversité. On y a travaillé avec les acteurs du coin. Donc forcément pour ce projet là javais déjà un peu dexpérience, et du réseau. Du réseau de « long court ». Le facteur temps est important aussi dans la construction dun réseau.
Donc jai vécu ici dans les années 90 mais les premiers qui sont venus ici étaient mes grands parents dans le cadre de limmigration. Mon grand-père travaillait sur le site EDF-GDF donc il y a une partie de ma famille qui a habité ici. Ils ont fait partie des vielles vagues migratoires qui ont construit la ville au moment ou on était encore sur une urbanisation très industrielle.
@@ -0,0 +1,60 @@
---
title: 'Océane et Laurent'
image_align: left
id: int_ocelau
---
Sur la place du 8 mai 1945 à Saint-Denis, pendant un chantier pour le collectif Mund Gawi. Le 26 Juillet 2017.
Jai rencontré Océane quelques semaine plus tôt lors dune intervention des Street Mamies devant la maison des Séniors. Je la retrouve aujourdhui place du 8 mai 1945, lorsque jarrive elle est partie chercher à manger et je rencontre Laurent..
Explication du projet..
...Océane je tai donc rencontré il y a quelques semaines en face de la Maison des Seniors lors dune intervention tricot. Aujourdhui tu es avec Laurent place du 8 mai 1945 .
Peux-tu déjà me parler de ta structure Dechets darts ?
Alors Dechets darts cest dabord la rencontre de deux amies, Nicole qui travaillait surtout sur la prévention autour des dechets et de l’écologie et dAnne dominique qui elle est plasticienne et qui a toujours travaillé a partir de ce quelle trouvait et qui voit le dechet comme unr ressource. Cest donc le mélange de leur deux approche qui a crée dechets dart dont le concept est de sensibiliser à des comportements plus eco citoyens autour de dechet et au travers de la création artistique. Comment faire du dechet une resssource avec une approche poétique. On apporte cette sensibilité par le faire et sans panneau de sensibilisation avec du chiffre etc. On met en place pas mal dateliers pour apprendre des techniques, des outils etc ..
Voilà pour lasso. On fonctionne en réseau professionnel depuis le début. Ya un noyau dur qui change au fil des années et le reste cest des artistes indépendant avec qui on travail régulièrement ou non. Ca se fait sous forme dateliers ou dinterventions ponctuelles.
On a deux modes dinterventions. Dun coté on vend de la prestation et dun autre on développe nous meme des projets pour lesquels ont va chercher des subventions. Le projet des streeet mamies durant lequel tu mas croisé en fait partie. On part de la technique du crochet adapté à des materiaux de récup et on invite des séniors femmes pour aussi réaffirmer la place et des séniors et de la femme dans lespace public. Cest aussi un vecteur de rencontre.
Vous parlez de rencontre, justement comment vous faites se rencontrer tout ça, comment vous communiquez ?
Pendant un temps on a eu une newsletter et desormais on alimente surtout une ou des pages facebook, en fonction des projets. Je suis présente sur dautres réseaux mais finalement cest surtout ça quon alimente plus facilement et plus spontannément. On a un site internet également qui est aussi vieux qui lasso et quon actualise pas tellement mais qui nous sert de vitrine.
Pour un projet comme celui des street mamies je peux cependant deviner que la pateforme facebook nest peut être pas la plus adaptée pour le public quil touche, comment vous procedez dans ce cas ?
Effectivement là on va plus utiliser le téléphone. On a quelques personnes qui suivent leurs mails et donc on utilise beaucoup les listing et puis sinon cest le téléphone.
Pour des projets plus récents jai commencé à utiliser les google groupes après une tentative de groupe Facebook mais tout le monde n’était pas inscrit sur Facebook.
A un niveau « informationel » quest-ce que tu utilise comme outils ou plateforme ?
Donc comme on disais jutilise pas mal Facebook et pour certains projets on est pas mal sur Whats app.
Es-ce que tu y communique avec des personnes que tu avais déjà rencontré ou tu en a rencontré certaines via ces échanges numériques ?
Alors pour beaucoup on se connaissait déjà mais cest vrai que pour certains on a dabord discuté que ce soit sur des commentaires Facebook ou sur lapplication whats app. Je me suis notamment rapproché dune structure de maison de quartier coopérative là qui est en train de se monter pas loin et cest vrai queux cest leur principal moyen de communiquer donc pour beaucoup jai fait leur connaissance sur whats app et je les rencontre au fur et à mesure.
J’évoque le fait qu’à la Maison Jaune ils lutilisent aussi beaucoup et Laurent me demande de qui je parle, il na jamais été voir encore la maison jaune et est curieux de découvrir.
Laurent, quelle pratique du numérique tu peux avoir dans ton métier ?
Alors jutilise surtout des logiciels de plans pour mon travail, quelques outils administratifs à la limite pour constituer mes dossiers mais sinon je suis plus une personne de terrain, avec mes chantiers.
Et dans ta vie personnelle ?
Oui pour communiquer avec mes proches jutilise forcément les nouvelles technologies mais je crois que je fais partie des nostalgiques de « lavant  ça ».
Je regrette quon ai tant de mal à sen défaire. Jutilise aussi comme océane l’évoquait, Messenger, whats app, facebook mais aussi pour un côté plus « pratique » jutilise les réseaux Air Bnb, vélib..même Tinder ..!
Et lun comme lautre, vous avez des contacts avec qui vous échangez quasiment que via le web ?
Océane : oui moi par rapport à l’éloignement jai mes deux sœurs et dune très bonne amie qui sont en guadeloupe donc on échange essentiellement par messenger, facetime..
Laurent : Moi Skype et tout ça jaime pas du tout, j’évite dutiliser ça. Mais la plupart de mes amis, je peux ne pas les voir pendant des mois et quand on se voit on reprend comme la fois davant mais on échange pour ainsi dire pas entre deux, je naime pas devoir prévoir un créneau, sans se voir.
Quelle pratique de Saint Denis vous avez ? Océane toi tu es habitante de St Denis ?
Océane : Oui alors moi jhabite ici, pas loin du théâtre, mes filles vont a l’école ici. L’école rue du corbillon on lautre au college jds place de la résistance, le petit café au pavillon éventuellement en laccompagnant le matin. Un petit tour au point carré de temps en temps, parce que je suis reliée au projet aussi. Le théâtre, pas de manière assidue mais régulièrement. LAdada, lartefact 93 , le parc de la légion dhonneur avec mes filles ou simplement la place de la basilique pour faire du roller. Le 6B également, parce que jai mon bureau là bas et pour participer à des événement.
Le 6B cest là où on sest rencontrés avec Laurent. Le chapiteau Rajganawak aussi, qui a ouvert récemment. Ya un cabaret, des événements, des cours de yoga..
Au niveau des petits commerces après cest plus les petits commerces de proximité autour de chez moi.
Laurent : Moi cest vrai que je connais pas beaucoup les alentours, jhabite paris et pour le moment, comme jhabite loin, je viens essentiellement pour mon espace de travail au 6B. Je connais pas du tout Saint-Denis. Cest pas pareil quand tu met une heure pour venir.
Océane remarque sur mon carnet que jai rencontré Wiebke de Point Carré, elle me signale donc :
Wiebke est aussi très en lien avec Rajganawak, parcquelle fait partie de la Briche et ils font beaucoup de choses en collaboration. Dailleurs elle est aussi liée aux clinamen, les bergers urbains qui font partis du collectif Mund Gawi. 
Oui dailleurs aujourdhui vous travaillez ensemble pour le collectif MundGawi, est-ce que vous pouvez men dire plus ?
Océane : Donc cest un collectif qui sest monté au moment de la COP 21, il y avait à lorigine beaucoup de structures, dans lidée de faire un alternatiba Saint denis mais avec lidée de trouver un espace où expérimenter pérènne plutot quun village de 2/3 jours. Donc au départ beaucoup de structures étaient interessées et finalement dans le temps on est 5 structures à constituer et porter le collectif. Il y a donc : Rien ne se perd, Shakti 21 ( outils de cuisson écologique ), clinamen berger urbains, landykadi ( jardins potager, amélioration de vie de quartier, événements culturels autour de lafrique) , Dechet dart, et une strcuture daubervillers qui du coup suit dun peu plus loin du fait de sa non appartenance au territoire.
Jai vu aussi tout à lheure que tu avais repéré Bonjour Voisins CopBillon, moi je suis pas mal en lien du fait que ma fille soit à l’école là-bas, mais les street mamies aussi on fait des interventions dans cette rue.
@@ -0,0 +1,42 @@
---
title: olivier
image_align: left
id: int_olivier
---
Peux-tu me parler de ton rapport à Saint-Denis ?
Jhabite à Saint-Denis depuis 3 ans, jai découvert lAMAP via une Amapienne lors dun événement à la mairie. En discutant elle minvite à minscrire à lamap et effectivement je me suis renseigné et ça ma bien parlé, jai rencontré quelques personnes et je me suis inscrit. Cest là que mon réseau Dyonisien à commencé à souvrir. Au début je venais pour mes paniers de légumes et petit à petit jai commencé à faire connaissance avec les gens, à utiliser le réseau. La liste de discussion est très active, sur une multitude de sujets. On est 700 inscrits sur la liste, donc 700 foyers, ça donne une nombre de personnes impressionnant. Il y a même un framapad pour se communiquer tous les bons plans, les bonnes adresses dartisants, de restaurants, de spécialistes médicaux etc.. Il y a une acceptation de toute forme d’échange avec une auto-restriction pour ne pas « blinder » les boîtes mails des gens. Il y en a qui abandonnent parce quen terme de mails/jours cest très conséquent. Mon collocataire, lorsquil sest inscrit, à reçu une 50aine de mails le premier jour et a pris peur. Je lui ai dit de ne pas se décourager mais cest vrai quil faut prendre lhabitude de faire le ménage tous les jours.
Comment fonctionne lAMAP ?
La particularité de cette Amap cest que cest géré par les membres, sans chef, hierarchie, contrôle. Cest linitiative des gens qui engage les choses. Quelquun veut prendre des commandes aurpès dun producteur, il peut lancer le truc. Il y a de plus en plus de producteurs et de produits différents, avec tout le monde qui met la main à la pâte pour distribuer, organiser etc..cest basé sur la confiance. Il y a la question de la transmission qui est très importante dans la cohésion du groupe.
Javais vu sur le site internet quil y avait un forum, est-ce que cest utilisé ?
Non, ça cest le 1er site qui a été conçu mais il nest plus utilisé aujourdhui. Je nai pas connu cette période là. Le forum a juste été testé mais ça na pas été concluant. Ce réseau fonctionne aussi beaucoup par le contact direct, on a un cahier où lon fait des croix, pour les présences, les commances.. la ccopérative à coté fonctionne en parallèle avec un site qui permet de sinscrire lorsque lon ne peut pas se déplacer, pour les permanences par exemple, mais sans que a remette en cause lexistence du cahier : on repporte les inscritptions en ligne sur le cahier et pas le contraire.
Ne pas tout numériser systématiquement ça oblige aussi plus ou moins à venir, à se parler, se rencontrer. Et puis tout le monde na pas accès à ces outils.
Avec autant dinscrit cest étonnant que ça marche aussi simplement.
Oui surtout quaujourdhui il y a deux coop, la dyonicoop qui compte aussi environs 400 coopérateur, et la coop Bel air qui en a une centaine. Elles sont toutes deux issues du réseau amapien, cest les même personnes qui ont initiés la coopérative, qui date de 2/3 ans, mais ça attire un peu dautres personnes aussi. Ca reste plus rare car ça reste le même réseau de personne.
Par exemple, lamap est ici situé dans la cité Langevin, et je crois quon a aucun inscrit dans les habitants de la cité, ce qui peut être assez étonnant..
Et par exemple tes colloc tu les as trouvés sur le réseau de lAmap ?
Oui, je suis passé par la mailist pour me simplifier la vie et en me disant que javais certainement plus de chance dy trouver des personnes avec qui jaurais des points communs. Jai trouvé très facilement des gens très enthousiastes. Pour ça lamap me simplifie considérablement la vie, je nai plus cette sensation de manquer de quelque chose, lorsque je fais une recherche je suis quasi-certains de trouver une réponse à ma question ou une solution à mon problème via ce réseau. Il y a toujours quelquun pour te répondre. Cest comme ça que jai rencontré Elodie aussi.
Dailleurs il y a quelques jours j’étais avec quelquun que tu connais apparement, Solen, qui ma parlé de votre projet commun et mas dit que tu saurais certainement mieux en parler quelle.
On a monté un collectif il y a quelques mois, qui sappelle YAKA. Ce qui nous interessait à la base c’était douvrir un bar à vocation daccueillir et de faire vivre toute la mixité de Saint-Denis. Lidée du bar cest parti simplement de cette fonction de rassembler, nourrir les gens. On avait à cœur que ce soit Nomade, pour aller à la rencontre des gens. Puis ai venu le projet Coopérence qui se met doucement en place pour préparer leur futur local place du 8 mai dans lequel un café est prévu. Tout reste à imaginer et reste ouvert à qui veut pour la gestion donc on sy interesse pour penser notre projet. Avec le centre social on est dans la même envie douvrir à tous, du plus « bobo » au plus en difficulté, avec un esprit fédérateur ,de partage et de rassemblement. Cest la même envie qui porte notre projet nomade, de déconstruire les préjugés que les populations portent les unes sur les autres et donner la parole. Une personne de lasso à déjà lancé une première action sous forme de Radio avec un collectif de Lycéen qui sappelle les Kolporters et donc lidée cest de faire une radio mobile avec eux et de créer quelques émissions.
Comment tu vis ta vie Dyonisienne en dehors de lamap, des activités, des lieux emblématiques ?
Je suis dans la Chorale du Jazz Club qui a été montée il y a deux ans, je suis aussi au conservatoire de Saint-Denis. Jai connu la Chorale par la liste de lAMAP .
Tu utilises dautres canaux dinformations ?
Jutilise pas mal FB aussi, par exemple je sais que si je veux des infos sur Rajganawak je peux en trouver sur Facebook bien que ce ne soit pas ce que jutilise le plus.
Il y a beaucoup de bouches à oreille aussi, par exemple la rencontre avec Coopérence sest faites par une amie amapienne qui disais quelle allait à une réunion le lendemain et cest comme ça que jy suis allé. Avoir du réseau ça simplifie vraiment les choses.
Je pense que quand on densifie son réseau direct humain, loutil numérique reste à son stade doutil. Il sert le contact et la rencontre. Parfois à lAMAP on teste de nouveaux outils pour améliorer le système, comme umap pour se localiser lorsque lon ne peut venir chercher son panier, pour quun amapien voisin nous dépanne. Ca crée encore dautres liens.
Sinon avec YAKA on utlise whats app pour les conversations de groupe.
@@ -0,0 +1,43 @@
---
title: Pascale
image_align: left
id: int_pascale
---
Est-ce que vous pouvez me parler un peu de la Maison Jaune ?
Cest une maison de quartier qui a ouverte au mois de Novembre. lidée cest que cet espace soit mis à disposition des habitants, quon y organise des choses mais aussi quil y ai de la place pour leurs propres initiatives. Le but ce nest pas quon arrive avec déjà des projets x ou y fermés, car il y a pas mal dhabitants du coin qui ne sortent pas si facilement de chez eux donc on essaye de trouver un moyen de les impliquer davantage et de les amener à sapproprier aussi les lieux. Les enfants font pas mal le lien car ils sont curieux et pas trop timides, ils viennent voir ce qui se passe et participent. Et parfois ,derrière les parents découvrent à travers eux, en venant les chercher. Mais cest un vrai travail…
Comment sest créé le collectif ? Est-ce que vous vous connaissiez déjà ?
Non pas du tout, moi jai eu vent de louverture de ce lieu en octobre dernier, en passant dans le coin, et je cherchais justement un local pour mon projet de tricot et jai contacté Julia qui soccupait un peu dorganiser tout ça et qui cherchait à sentourer pour enclencher des dynamiques avec le voisinage.
On se réunit tous les lundi soirs pour structurer le collectif, avec de nouvelles personnes qui simpliquent au fur et à mesure.
Explication du projet, j’évoque le questionnement de la résidence sur la notion de réseau, de connexion et des outils numériques dans nos échanges..
Pascale réagit :
Cest vrai que loutil numérique permet dagréger des personnes qui ne sont pas forcément disponibles en temps et en heure, ou nont pas la possibilité de se déplacer, mais qui souhaitent quand même être informés, de participer et d’être tenus au courant des activités. Pour nous, en tant quassociation avec la Maison Jaune - cest une problématique intéressante et qui nous concerne directement. 
Est-ce que vous avez vécu la coupure Internet dil y a quelques semaines ? Et est-ce que cela a impacté votre quotidien, vos habitudes ?
Oui, jai été coupé pendant une dizaine de jours et forcément, ça impact : on regarde moins souvent les mails, on est un peu moins «  au courant ». On est un peu coupés du reste, des information et du réseau professionnel. Je ne dirais pas que ça ai impacté le plan « social » par contre, ça a retardé, cest sûr, mais na pas été lobjet dune rupture de contact. Cest surtout la fluidité des recherches et de linformation que ça entrave.
Quel pratique vous avez du numérique et du web ?
Jen ai un usage très régulier. Les échanges de mails sont quotidiens surtout dans le travail mais jutilise aussi beaucoup les réseaux sociaux comme Facebook ou même instagram que jutilise depuis très peu de temps essentiellement pour communiquer sur les projets, avoir de la visibilité. Cest un peu comme une vitrine de projet.
Sur Facebook, jai ma page professionnelle pour mon travail de plasticienne, une page plus privée où je partage les petites choses que jaime et jai crée une page spécifique au projet de tricot partage pour pouvoir communiquer facilement avec les participants.
Est-ce que vous pouvez men dire un peu plus sur le projet tricot-partage que bous venez d’évoquer ?
Ce projet de tricot à été démarré au mois de Fevrier. Globalement cest un projet de tricot solidaire. Lidée était, au vu de la grosse vague de froid, de réaliser avec la participation de public des carrés de laine de 30cm de côté assemblés en couvertures. Ces couvertures sont à destination de la Croix Rouge et distribuées lors des maraudes. Lenvie était aussi que chacun, tout âge confondu, puisse participer à sa manière, en venant pour tricoter, pour apprendre ou pour transmettre. Il y a beaucoup de dames expérimentés qui ont formés des débutants, même des petits. Beaucoup denfants sont revenus après avoir appris comme ça, pour continuer leurs œuvres. Ils se sont pris au jeu et je garde leurs petits tricots de semaines en semaines.
Jai aussi lancé un appel à dons de laine et jai reçu des colis de laine de plusieurs villes de France par des personnes qui avaient eu vent du projet à travers la page Facebook. Jai reçu comme ça des boîtes à chaussures remplie de pelotes venus de Paris mais aussi de Bretagne, de Nice.. jai même eu un message dune personne dans un petit village dardèche, qui ma écrit en me disant quelle tricoterait des petits carrés de laine pour nous les envoyer.
Des habitants de Saint-Denis mappellent ou me contactent sur internet pour me dire quils ont vus nos communications papier ou autre, pour me prévenir quils ont de la laine à offrir.
Le projet est aussi nomade, ce qui nous permet au fil du temps de rencontrer et de rallier davantage de personnes et douvrir le réseau initial. Dailleurs on a lenvie de participer à des événements nationaux.
Dans le symbole, cest un projet fort, ce que jappelle le « fil participatif » : le tricot nous permet de tisser du lien. La mixité quon y retrouve est forte entre les catégories sociales, les cultures, les générations et ça a répondu à beaucoup dattente en terme de convivialité mais aussi de bien être. Parce que le tricot cest aussi un moment de méditation et d’échange, les gens se rencontrent.
Il y a quelques temps, chez limprimeur, je faisais mes photocopies de flyers pour l’évenement à la Maison Jaune. Au moment de régler jai posé mes copies sur le comptoir. Une dame était derrière moi et a vu « tricot » et ma demandé ce qu’était l’événement. Je lui ai donc expliqué que c’était à la Maison Jaune, en quoi consistait le tricot partage, et elle est venue. Depuis, elle vient régulièrement et nous sommes assez liées. On sest donc rencontrée par ce heureux hasard et il se trouve que jaurais fait mes photocopies 10 min avant ou 10min après ça naurait peut-être jamais eu lieu.
Globalement, comment est-ce que vous communiquez sur vos projets ?
Pour ce projet, je communique les horaires et dates de rencontre surtout par mailing. Et par des affiches et des flyers dans le quartier.
Est-ce que vous pensez que vous auriez moins de monde présents aux rencontres de tricot sans la diffusion sur les réseaux web ?
Oui sans doute, même sil y en a certains qui viennent parce quils ont vu laffiche, ou un des flyers car jen dispose un peu partout, mais cest vrai que la communication papier et la communication numérique sont vraiment complémentaires. Certains utilisent les deux régulièrement mais dautres sont beaucoup moins familiers de loutil informatique. Il y a des personnes qui nont pas de-mail et ne sont joignables que par téléphone, voire qui nont pas de téléphone portable. 
Je crois aussi que cest bien de ne pas uniformiser linformation, je remarque que même les personnes habituées à communiquer par mail ou à se renseigner sur internet aiment bien avoir le petit papier qui récapitule les information à mettre dans le sac ou à placarder sur le frigo.
Comment est-ce que vous êtes relié au tissu associatif environnant ?
Déjà il faut dire qu’à saint Denis il y a énormément dassociations, qui balayent un éventail énorme. Que ce soit artistique, culturel, sportive, très militantes ou autour des cultures. On a beaucoup dassos qui représentent des communautés d’à travers le monde.
Il y a beaucoup de bouche à oreille mais il y a aussi la Maison de la vie associative qui regroupe la communication des différentes structures. On a aussi le journal de saint Denis qui fonctionne bien où il y a un espace pour les assos, publier des annonces etc. Et puis ya quelques lieux emblématiques ou lon dépose nos flyers, comme la crêperie La Bigoudène ou beaucoup de gens se retrouvent le matin pour boire le café, le cinéma, la maison de la vie associative..
+38
View File
@@ -0,0 +1,38 @@
---
title: Solen
image_align: left
id: int_solen
---
Rencontrée au Pavillon le 3 octobre à 17h30
Cest Manon Dumond qui ma transmis son contact.
Pour commencer, est-ce que tu peux me parler de ton lien avec Saint-denis, si tu y habites, y travailles.. ?
Jhabite à Saint-Denis depuis 4 ans, jhabite juste à côté de là où on se trouve, je suis arrivée en Juillet 2013 après un master durbanisme à Lyon. Jai trouvé du travail dans le Val dOise et Saint-Denis sest avéré être un bon compromis géographique entre le Val dOise et Paris, et financièrement aussi. Javais une amie qui venait de trouver du boulot dans le nord de Paris donc on a cherché à faire une collocation ici. Quand je suis venue visiter lappartement jai été agréablement surprise de Saint-Denis, jen avais une mauvaise image.
Avec ma deuxième colloc, on sest inscrites à lAMAP, et ça ma ouvert sur un réseau Dyonsien que je navais pas du tout. Ca fonctionne beaucoup par mails et beaucoup de gens se font passer des annonces en tous genre. Cest comme ça que jai vu une annonce pour des cours de chants qui savéraient être à deux pas de chez moi, donc je suis allée voir. Et via ce cours jai commencé à rencontrer dautres personnes sur Saint-Denis, à sortir plus.
Jai eu une période de chômage aussi qui ma permis de minvestir dans des assos, dans la coopérative de la Ferme et aux permanences de lAMAP. Ca ma permis de rencontrer une femme qui était en formation de coaching et je lui ai confié mes questionnements sur ma vie professionnel donc je suis devenu un peu son cobaye de coaching.
Dans ce questionnement de reconversion professionnel jai beaucoup réfléchi à un projet douverture de café culturel dans Saint-Denis. Jai commencé à en parler autour de moi, notamment à une personne de lAMAP qui sappelle Oliver.
Olivier ? Ah ben tiens, justement, je le rencontre Jeudi ..
Ah bon ? Cest drôle, cest vers lui que je taurais envoyé.
Ben du coup il pourra ten parler aussi. On en a beaucoup discuté ensemble car cest une envie quil avait aussi depuis son arrivée à Saint-Denis. Ici à part le Pavillon il ny a pas vraiment « dambiance café ». Il y a Point Carré maintenant mais cest pas un « grand » café. A lyon javais plus ça, des café ou tu peux te poser en journée, un peu ambiance canapé où tu te vois bien même aller travailler, et je retrouve pas trop ça à Saint-Denis. Et comme Olivier connais beaucoup de monde à Saint-Denis, via lAMAP aussi, il a proposé quon se retrouve à 4/5personnes pour discuter de cette envie. Il y avait un peu deux idées, certains partisans dun café mobile et dautres plutot un espace permanent, on sest dit que lun nempêche pas lautre.. on sest réunis comme ça assez régulièrement pour définir ce quon voulait mettre dans ce projet. On a rencontré une personne de la ville de Saint-Denis qui nous a parlé des initiatives qui existent déjà à Saint-Denis, notamment, le projet de centre social coopératif, Coopérence. On a été à une de leur réunion suite à ça, on a rencontré Nadia et dans leur projet ils envisageaient une partie café donc ils étaient assez enthousiastes que des gens puissent avoir envie de sen occuper.
Suite à ça on a été mis en contact avec une prof darchi qui a chapeauté lexposition dun concours sur le thème de « ma cantine en ville ». Des étudiants avaient imaginés des cantines mobiles et après lexposition des structures elles étaient démontées et stockées à Nantes. Donc on a pris contact avec les étudiants et certains, des Dyonisens dailleurs, ont acceptés de nous donner leur structure. Lidée avec ces structures cest de pouvoir imaginer un projet nomade dans différents quartiers. Je nen sais pas beaucoup plus en ce moment car avec la reprise du travail jai du mal à être présente dans le projet.. Mais en tous cas il y a vraiment quelque chose de stimulant à Saint-Denis, pas forcément visible de lextérieur mais quand tu commence un peu à rentrer dans certaines choses, à rencontrer des gens, tu te rends compte que ça fourmille. Cest une échelle particulière, cest pas comme à Paris. On peut encore se rencontrer « par hasard », comme ici au Pavillon. Cest là que jai rencontré Manon dailleurs car je connaissais lamie avec qui elle était et on a discuté comme ça.
Quelles activités tu pratiques dans St-Denis, tu me parlais du Chant tout à lheure ?
Là je viens darrêter le chant car notre prof est partie dans le sud. Je vais de temps en temps à Rajganawak voir des spectacles. Je trouve quil manque des choses pour la vie nocturne à Saint-Denis, Rajganawak participe à changer ça. Ici, au Pavillon, il y a bien des événements mais cest plus informel. Tu peux facilement y improviser un petit concert, une performance artistique.. mais ce nest pas communiqué. On vient surtout pour y boire des verres.
Andromède, une fille qui faisait du chant avec moi, est très investie dans la vie associative. Elle est sur un projet qui sappelle le jardin des chaumettes.
Quel usage des outils numériques tu as, pour communiquer, te tenir au courant ?
Les événements de Rajganawak cest essentiellement via Facebook que je suis au courant. Après jutilise beaucoup les mails notamment concernant lAMAP, cest très central.
Personnellement je mappuie beaucoup sur FB pour tout ce qui est évenementiel. Pour communiquer avec mes amis jutilise beaucoup messenger et whats app. Cest surtout des groupes.
Pour la vie Dyonisienne cest beaucoup de bouche à oreille, je sais beaucoup de choses par Olivier parce que lui il parle a tout le monde alors il est toujours au courant. La communication est pas forcément excellente par la communication classique, mais cest aussi quil ny a pas vraiment de grosses institutions en dehors du Théâtre Gérad Phillipe.
Comment est-ce que tu perçoit le réseau qui tentoure, est-ce que tu as le sentiments que beaucoup de réseaux fusionnent ou quils ne font que se croiser ?
Je crois que partout, dans les réseaux de personnes, on peut vite avoir limpression dun réseau fermé sur lui-même . Cest sûr quau sein de lAMAP par exemple, ou des gens que je fréquente on retombe sur des profils assez similaires, socialement, culturellement.. quil y a plein dendroits qui ont beau être proches de chez moi , je ne les fréquente pas, parce que naturellement, on va toujours vers ce quon connaît je pense, les lieux qui nous sont familiers. Pour autant, entre chacun de ces réseaux, il y a des ponts qui se créent.  
@@ -0,0 +1,39 @@
---
title: victoria
image_align: left
id: int_victoria
---
dans un café place de la République à Paris le 25 Juillet 2017.
Marie Preston connaissait Victoria via luniversité Paris 8 et nous a mis en contact par mail.
Explication du projet..
...Cest donc Marie Preston, enseignante de Paris 8 où tu étudie, qui ma envoyé vers toi..[…]
Marie ma expliqué que tu étais assez active notamment dans les réseaux militant de Saint Denis, que peux tu men dire ?
Alors, moi à Paris 8 je fais partie dun groupe d’étudiant mais qui nest pas liée aux comités d’étudiants etc. Enfait, à luniversité Paris 8 j’étais en cours dart mais on avait ni atelier ni espace de convivialité pour se retrouver ou travailler en groupe. On a décidé de saménager un espace pour ça, tout autant symbolique que concret, et on a commencé à construire une cabane. Ca a pris beaucoup de temps quil y a eu beaucoup de bureaucratie, on avait commencé a construire sans autorisation, puis il sest avéré quil nous en fallait une, etc..ça a bien pris trois ans à se monter jusqu’à ce quelle soit détruite cette année..par une erreur. Enfait le personnel de la logistique à commencé à sinquiéter je pense de lafluence quil y avait dedans. Ca commençait à être le bordel parcque on était beaucoup à squater. Cest lendroit parfait, un endroit un peu caché, tout petit, mais tout le monde aimait sy retrouver. Certains avaient commencé à aménager des espaces de jardins sur le côté. Les jardins continuent à vivre dailleurs. Cest vrai que nous comme on est en Master maintenant, on a moins de temps pour sen occuper et on a pas voulu que ce soit « notre » endroit donc on a complétement laissé la main là dessus. Du coup cest vrai que ya pas mal de monde qui squattait, yen avait même qui revenait le soir passer la barrière pour venir faire la fête le samedi.. c’était un peu trop du coup au bout dun moment luniversité à voulu chercher un responsable mais dans ce genre de projet yen a pas vraiment de désigné, donc au final, un peu sur un malentendu, ça a fini par être détruit.
Donc voilà on a arrêté avec la cabane mais de là on a rencontré dautres étudiants assez actifs qui occupait aussi des salles, notamment pour protester contre la loi travail ou autre et on en est venus à rencontrer des personnes qui étaient dans des lieux auto-géré etc.
On a rencontré une personne habitant de Saint Denis qui habite là depuis longtemps, qui connais pleins de choses ici et qui a participé notamment à loccupation de latiéké qui est un centre social auto-géré. On a commencé à visiter ce type dendroits auto-géré , des espaces artistiques comme La Briche.
Es-tu habitante de Saint-Denis ? Quel lien tu as avec ce territoire ?
Alors non, moi je connaissais pas du tout Saint-denis, je suis Brésilienne et jai habité à Aix-en-provence avant de venir ici. Je voulais entrer en école dart mais je ne connaissais pas la différence entre les beaux arts et lart à luniversité. Cest un peu par erreur que je suis arrivée là et cest vrai que je voulais pas venir à Saint-Denis car je trouvais ça loin, etc. Et finalement jai atteri ici et jai trop aimé. Jai même fait un master 1 à la Sorbone et jai préféré revenir ici.
Par contre je ny habite pas, je vis dans Paris et les premières années dans saint denis je ne connaissais que la sortie du métro université et les alentours de Paris 8.. jai découvert petit à petit par des amis de la fac qui habitent dans le coin. Au début je savais à peine dans quelle direction était le centre ville.
Et du coup par quels lieux tu as découvert Saint-Denis ? Des commerces, des cafés.. ?
Je nallais pas tellement dans les cafés, plutot chez des amis et la plupart habitent entre la gare et le centre ville donc cest plutot là que je suis allée au début, sur le chemin. On allait dans des cafés autour de la Fac mais sinon cest plutot des fêtes chez des amis. Et puis après jai découvert latiéké. C’était au moment où les occupant se faisaient expulser du lieu qui était désafecté et à ce moment là des étudiants de Paris 8 sen sont mêlés et les ont aidé à occuper des lieux pour leur permettre de retrouver un espace. Ya un groupe assez militant qui sest formé et jai découvert les activités et la diversité du lieu. Jy suis beaucoup allée.
Comment tu te tenais au courant des événements artistiques et autres auquels tu te rendait ?
Ben surtout par le réseau Paris 8. Il y a toujours des affiches, pour les festivals de cinéma notamment, il se passe pas mal de choses cool au cinéma pas loin de la Basilique.
Après mon copain il participe aussi à un lieu qui a été crée par des habitants de Saint-Denis où il y a un jardin. Il y a Martine Bodineau là bas, ce serait interessant que tu la rencontre, elle est très active ici et bavarde ! Et elle habite Saint-Denis depuis longtemps. Ils ont une petite association à côté de la Maison Jaune.
Donc pour toi ça se fait surtout par Bouche à Oreille ?
Oui complètement, on se tiens au courant quand on se rencontre ou on senvoie quelques messages, des mails ou des sms mais on a pas tellement investis les plate-formes styles Facebook avec les événements etc. Ca se fait plus discrètement peut être mais ça fonctionne très bien.
On utilise beaucoup laffiche dans les quartiers donc ça a tendance à rester très local et à fédérer des groupes dhabitants.
Pour la Kabane on a utilisé pas mal les outils Framasoft, des framapad etc, on a essayé d’éviter les google docs et compagnie au sein du collectif car la question du logiciel libre et de loutil collaboratif était important pour nous. Pour faire le lien avec dautres étudiant on utilisait surtout les listes de mails.
On a beaucoup de gens qui ne sont pas sur Facebook dans nos contacts et on essaye aussi de s’émanciper car on a tendance à senfermer dans les infos qui sy trouves et à ne plus chercher linformation ailleurs.
Et en parlant de Bouche à Oreille, vers qui tu pourrais menvoyer pour la suite ?
Je pourrais te donner le contact de Martine Bodineau dont je te parlais tout à lheure, et de Marie qui est une personne très active et qui connais beaucoup de monde. Je vais te renvoyer leur contacts par mail.
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

@@ -0,0 +1,8 @@
---
title: 'Rue Marie.W'
media_order: DSC04024.JPG
image_align: left
id: img_ruemariew
---
![](DSC04024.JPG)
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

Some files were not shown because too many files have changed in this diff Show More