Commit 75c43188 authored by webchick's avatar webchick

Issue #2271529 by attiks, alexpott, Lowell, YesCT, Jelle_S | mdrummond: Move...

Issue #2271529 by attiks, alexpott, Lowell, YesCT, Jelle_S | mdrummond: Move breakpoint settings to theme and module *.breakpoints.yml files at root level.
parent 673e1e58
......@@ -1068,4 +1068,11 @@ public function getModuleDirectories() {
return $dirs;
}
/**
* {@inheritdoc}
*/
public function getName($module) {
$module_data = system_rebuild_module_data();
return $module_data[$module]->info['name'];
}
}
......@@ -346,4 +346,15 @@ public function uninstall(array $module_list, $uninstall_dependents = TRUE);
*/
public function getModuleDirectories();
/**
* Gets the human readable name of a given module.
*
* @param string $module
* The machine name of the module which title should be shown.
*
* @return string
* Returns the human readable name of the module.
*/
public function getName($theme);
}
......@@ -682,4 +682,34 @@ protected function systemThemeList() {
return system_list('theme');
}
/**
* {@inheritdoc}
*/
public function getThemeDirectories() {
$dirs = array();
foreach ($this->listInfo() as $name => $theme) {
$dirs[$name] = DRUPAL_ROOT . '/' . $theme->getPath();
}
return $dirs;
}
/**
* {@inheritdoc}
*/
public function themeExists($theme) {
$themes = $this->listInfo();
return isset($themes[$theme]);
}
/**
* {@inheritdoc}
*/
public function getTheme($name) {
$themes = $this->listInfo();
if (isset($themes[$name])) {
return $themes[$name];
}
throw new \InvalidArgumentException(sprintf('The theme %s does not exist.', $name));
}
}
......@@ -139,4 +139,39 @@ public function getName($theme);
*/
public function getDefault();
/**
* Returns an array of directories for all enabled themes.
*
* Useful for tasks such as finding a file that exists in all theme
* directories.
*
* @return array
*/
public function getThemeDirectories();
/**
* Determines whether a given theme is enabled.
*
* @param string $theme
* The name of the theme (without the .theme extension).
*
* @return bool
* TRUE if the theme is both installed and enabled.
*/
public function themeExists($theme);
/**
* Returns a theme extension object from the currently active theme list.
*
* @param string $name
* The name of the theme to return.
*
* @return \Drupal\Core\Extension\Extension
* An extension object.
*
* @throws \InvalidArgumentException
* Thrown when the requested theme does not exist.
*/
public function getTheme($name);
}
......@@ -5,9 +5,6 @@
* Manage breakpoints and breakpoint groups for responsive designs.
*/
use Drupal\breakpoint\Entity\Breakpoint;
use Drupal\breakpoint\Entity\BreakpointGroup;
use Drupal\Core\Config\Entity\ConfigEntityStorage;
use Drupal\Core\Routing\RouteMatchInterface;
/**
......@@ -40,33 +37,15 @@ function breakpoint_help($route_name, RouteMatchInterface $route_match) {
}
/**
* Load all breakpoint groups as select options.
*
* @return array
* An array containing breakpoint group labels indexed by their ids.
* Implements hook_themes_enabled()
*/
function breakpoint_group_select_options() {
$options = array();
$breakpoint_groups = BreakpointGroup::loadMultiple();
foreach ($breakpoint_groups as $breakpoint_group) {
$options[$breakpoint_group->id()] = $breakpoint_group->label();
}
asort($options);
return $options;
function breakpoint_themes_enabled($theme_list) {
\Drupal::service('breakpoint.manager')->clearCachedDefinitions();
}
/**
* Load all breakpoints as select options.
*
* @return array
* An array containing breakpoints indexed by their ids.
* Implements hook_themes_disabled()
*/
function breakpoint_select_options() {
$options = array();
$breakpoints = Breakpoint::loadMultiple();
foreach ($breakpoints as $breakpoint) {
$options[$breakpoint->id()] = $breakpoint->label() . ' (' . $breakpoint->source . ' - ' . $breakpoint->sourceType . ') [' . $breakpoint->mediaQuery . ']';
}
asort($options);
return $options;
function breakpoint_themes_disabled($theme_list) {
\Drupal::service('breakpoint.manager')->clearCachedDefinitions();
}
services:
breakpoint.manager:
class: Drupal\breakpoint\BreakpointManager
arguments: ['@module_handler', '@theme_handler', '@cache.discovery', '@string_translation']
tags:
- { name: plugin_manager_cache_clear }
# Schema for the configuration files of the Breakpoint module.
breakpoint.breakpoint.*.*.*:
type: config_entity
label: 'Defines the Breakpoint entity'
mapping:
id:
type: string
label: 'ID'
label:
type: label
label: 'Label'
name:
type: string
label: 'Machine name'
mediaQuery:
type: string
label: 'Media query'
source:
type: string
label: 'Source'
sourceType:
type: string
label: 'Source type'
weight:
type: integer
label: 'Weight'
multipliers:
type: sequence
label: 'Multipliers'
sequence:
- type: string
label: 'Multiplier'
breakpoint.breakpoint_group.*.*.*:
type: config_entity
label: 'Breakpoint group settings'
mapping:
id:
type: string
label: 'ID'
label:
type: label
label: 'Label'
name:
type: string
label: 'Machine name'
breakpoint_ids:
type: sequence
label: 'Breakpoints'
sequence:
- type: string
label: 'Breakpoint name'
source:
type: string
label: 'Group source: theme or module name'
sourceType:
type: string
label: 'Group source type'
<?php
/**
* @file
* Contains \Drupal\breakpoint\Breakpoint.
*/
namespace Drupal\breakpoint;
use Drupal\Core\Plugin\PluginBase;
/**
* Default object used for breakpoint plugins.
*
* @see \Drupal\breakpoint\BreakpointManager
* @see plugin_api
*/
class Breakpoint extends PluginBase implements BreakpointInterface {
/**
* {@inheritdoc}
*/
public function getLabel() {
return $this->t($this->pluginDefinition['label'], array(), array('context' => 'breakpoint'));
}
/**
* {@inheritdoc}
*/
public function getWeight() {
return (int) $this->pluginDefinition['weight'];
}
/**
* {@inheritdoc}
*/
public function getMediaQuery() {
return $this->pluginDefinition['mediaQuery'];
}
/**
* {@inheritdoc}
*/
public function getMultipliers() {
return $this->pluginDefinition['multipliers'];
}
/**
* {@inheritdoc}
*/
public function getProvider() {
return $this->pluginDefinition['provider'];
}
/**
* {@inheritdoc}
*/
public function getGroup() {
return $this->pluginDefinition['group'];
}
}
<?php
/**
* @file
* Contains \Drupal\breakpoint\Entity\BreakpointGroupInterface.
*/
namespace Drupal\breakpoint;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
/**
* Provides an interface defining a breakpoint group entity.
*/
interface BreakpointGroupInterface extends ConfigEntityInterface {
/**
* Checks if the breakpoint group is valid.
*
* @throws \Drupal\breakpoint\InvalidBreakpointSourceTypeException
* @throws \Drupal\breakpoint\InvalidBreakpointSourceException
*
* @return bool
* Returns TRUE if the breakpoint group is valid.
*/
public function isValid();
/**
* Adds a breakpoint using a name and a media query.
*
* @param string $name
* The name of the breakpoint.
* @param string $media_query
* Media query.
*/
public function addBreakpointFromMediaQuery($name, $media_query);
/**
* Adds one or more breakpoints to this group.
*
* The breakpoint name is either the machine_name or the ID of a breakpoint.
*
* @param array $breakpoints
* Array containing breakpoint objects
*
* @return \Drupal\breakpoint\Entity\BreakpointGroup
* The breakpoint group object.
*/
public function addBreakpoints($breakpoints);
/**
* Gets the array of breakpoints for the breakpoint group.
*
* @return \Drupal\breakpoint\Entity\BreakpointInterface[]
* The array of breakpoints for the breakpoint group.
*/
public function getBreakpoints();
/**
* Gets a breakpoint from the breakpoint group by ID.
*
* @param string $id
* The breakpoint ID to get.
*
* @return \Drupal\breakpoint\Entity\BreakpointInterface|boolean
* The breakpoint or FALSE if not in the Breakpoint group.
*/
public function getBreakpointById($id);
}
......@@ -2,42 +2,62 @@
/**
* @file
* Contains \Drupal\breakpoint\Entity\BreakpointInterface.
* Contains \Drupal\breakpoint\BreakpointInterface.
*/
namespace Drupal\breakpoint;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
/**
* Provides an interface defining a breakpoint entity.
* Interface for Breakpoint plugins.
*/
interface BreakpointInterface extends ConfigEntityInterface {
interface BreakpointInterface {
/**
* Checks if the breakpoint is valid.
* Returns the translated label.
*
* @throws \Drupal\breakpoint\InvalidBreakpointSourceTypeException
* @throws \Drupal\breakpoint\InvalidBreakpointSourceException
* @throws \Drupal\breakpoint\InvalidBreakpointNameException
* @throws \Drupal\breakpoint\InvalidBreakpointMediaQueryException
* @return string
* The translated label.
*/
public function getLabel();
/**
* Returns the weight.
*
* @return int
* The weight.
*/
public function getWeight();
/**
* Returns the media query.
*
* @see isValidMediaQuery()
* @return string
* The media query.
*/
public function isValid();
public function getMediaQuery();
/**
* Checks if a mediaQuery is valid.
* Returns the multipliers.
*
* @throws \Drupal\breakpoint\InvalidBreakpointMediaQueryException
* @return array
* The multipliers.
*/
public function getMultipliers();
/**
* Returns the provider.
*
* @return bool
* Returns TRUE if the media query is valid.
* @return string
* The provider.
*/
public function getProvider();
/**
* Returns the breakpoint group.
*
* @see http://www.w3.org/TR/css3-mediaqueries/
* @see http://www.w3.org/Style/CSS/Test/MediaQueries/20120229/reports/implement-report.html
* @see https://github.com/adobe/webkit/blob/master/Source/WebCore/css/
* @return string
* The breakpoint group.
*/
public static function isValidMediaQuery($media_query);
public function getGroup();
}
<?php
/**
* @file
* Contains \Drupal\breakpoint\BreakpointManager.
*/
namespace Drupal\breakpoint;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Extension\ThemeHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
use Drupal\Core\Plugin\Discovery\YamlDiscovery;
use Drupal\Core\Plugin\Factory\ContainerFactory;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
/**
* Defines a breakpoint plugin manager to deal with breakpoints.
*
* Extension can define breakpoints in a EXTENSION_NAME.breakpoints.yml file
* contained in the extension's base directory. Each breakpoint has the
* following structure:
* @code
* MACHINE_NAME:
* label: STRING
* mediaQuery: STRING
* weight: INTEGER
* multipliers:
* - STRING
* @endcode
* For example:
* @code
* bartik.mobile:
* label: mobile
* mediaQuery: '(min-width: 0px)'
* weight: 0
* multipliers:
* - 1x
* - 2x
* @endcode
* Optionally a breakpoint can provide a group key. By default an extensions
* breakpoints will be placed in a group labelled with the extension name.
*
* @see \Drupal\breakpoint\Breakpoint
* @see \Drupal\breakpoint\BreakpointInterface
* @see plugin_api
*/
class BreakpointManager extends DefaultPluginManager implements BreakpointManagerInterface {
use StringTranslationTrait;
/**
* {@inheritdoc}
*/
protected $defaults = array(
// Human readable label for breakpoint.
'label' => '',
// The media query for the breakpoint.
'mediaQuery' => '',
// Weight used for ordering breakpoints.
'weight' => 0,
// Breakpoint multipliers.
'multipliers' => array(),
// The breakpoint group.
'group' => '',
// Default class for breakpoint implementations.
'class' => 'Drupal\breakpoint\Breakpoint',
// The plugin id. Set by the plugin system based on the top-level YAML key.
'id' => '',
);
/**
* The theme handler.
*
* @var \Drupal\Core\Extension\ThemeHandlerInterface
*/
protected $themeHandler;
/**
* Static cache of breakpoints keyed by group.
*
* @var array
*/
protected $breakpointsByGroup;
/**
* The plugin instances.
*
* @var array
*/
protected $instances = array();
/**
* Constructs a new BreakpointManager instance.
*
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
* The theme handler.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
* The cache backend.
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
* The string translation service.
*/
public function __construct(ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler, CacheBackendInterface $cache_backend, TranslationInterface $string_translation) {
$this->discovery = new YamlDiscovery('breakpoints', $module_handler->getModuleDirectories() + $theme_handler->getThemeDirectories());
$this->discovery = new ContainerDerivativeDiscoveryDecorator($this->discovery);
$this->factory = new ContainerFactory($this);
$this->moduleHandler = $module_handler;
$this->themeHandler = $theme_handler;
$this->setStringTranslation($string_translation);
$this->alterInfo('breakpoints');
$this->setCacheBackend($cache_backend, 'breakpoints', array('breakpoints' => TRUE));
}
/**
* {@inheritdoc}
*/
public function processDefinition(&$definition, $plugin_id) {
parent::processDefinition($definition, $plugin_id);
// Allow custom groups and therefore more than one group per extension.
if (empty($definition['group'])) {
$definition['group'] = $definition['provider'];
}
// Ensure a 1x multiplier exists.
if (!in_array('1x', $definition['multipliers'])) {
$definition['multipliers'][] = '1x';
}
// Ensure that multipliers are sorted correctly.
sort($definition['multipliers']);
}
/**
* {@inheritdoc}
*/
protected function findDefinitions() {
$definitions = $this->discovery->getDefinitions();
foreach ($definitions as $plugin_id => &$definition) {
$this->processDefinition($definition, $plugin_id);
}
if ($this->alterHook) {
$this->moduleHandler->alter($this->alterHook, $definitions);
}
// If this plugin was provided by a module that does not exist, remove the
// plugin definition.
foreach ($definitions as $plugin_id => $plugin_definition) {
// If the plugin definition is an object, attempt to convert it to an
// array, if that is not possible, skip further processing.
if (is_object($plugin_definition) && !($plugin_definition = (array) $plugin_definition)) {
continue;
}
// Allow themes to provide breakpoints.
if (isset($plugin_definition['provider']) && !in_array($plugin_definition['provider'], array('core', 'component')) && !$this->moduleHandler->moduleExists($plugin_definition['provider']) && !$this->themeHandler->themeExists($plugin_definition['provider'])) {
unset($definitions[$plugin_id]);
}
}
return $definitions;
}
/**
* {@inheritdoc}
*/
public function getBreakpointsByGroup($group) {
if (!isset($this->breakpointsByGroup[$group])) {
if ($cache = $this->cacheBackend->get($this->cacheKey . ':' . $group)) {
$this->breakpointsByGroup[$group] = $cache->data;
}
else {
$breakpoints = array();
foreach ($this->getDefinitions() as $plugin_id => $plugin_definition) {
if ($plugin_definition['group'] == $group) {
$breakpoints[$plugin_id] = $plugin_definition;
}
}
uasort($breakpoints, array('Drupal\Component\Utility\SortArray', 'sortByWeightElement'));
$this->cacheBackend->set($this->cacheKey . ':' . $group, $breakpoints, Cache::PERMANENT, array('breakpoints' => TRUE));
$this->breakpointsByGroup[$group] = $breakpoints;
}
}
$instances = array();
foreach ($this->breakpointsByGroup[$group] as $plugin_id => $definition) {
if (!isset($this->instances[$plugin_id])) {
$this->instances[$plugin_id] = $this->createInstance($plugin_id);
}
$instances[$plugin_id] = $this->instances[$plugin_id];
}
return $instances;
}
/**
* {@inheritdoc}
*/
public function getGroups() {
// Use a double colon so as to not clash with the cache for each group.
if ($cache = $this->cacheBackend->get($this->cacheKey . '::groups')) {
$groups = $cache->data;
}
else {
$groups = array();
foreach ($this->getDefinitions() as $plugin_definition) {
if (!isset($groups[$plugin_definition['group']])) {
$groups[$plugin_definition['group']] = $plugin_definition['group'];
}
}
$this->cacheBackend->set($this->cacheKey . '::groups', $groups, Cache::PERMANENT, array('breakpoints' => TRUE));
}
// Get the labels. This is not cacheable due to translation.
$group_labels = array();
foreach ($groups as $group) {
$group_labels[$group] = $this->getGroupLabel($group);
}
asort($group_labels);
return $group_labels;
}
/**
* {@inheritdoc}
*/
public function getGroupProviders($group) {
$providers = array();
$breakpoints = $this->getBreakpointsByGroup($group);
foreach ($breakpoints as $breakpoint) {
$provider = $breakpoint->getProvider();
$extension = FALSE;
if ($this->moduleHandler->moduleExists($provider)) {
$extension = $this->moduleHandler->getModule($provider);
}
elseif ($this->themeHandler->themeExists($provider)) {
$extension = $this->themeHandler->getTheme($provider);
}
if ($extension) {
$providers[$extension->getName()] = $extension->getType();
}
}
return $providers;
}
/**
* {@inheritdoc}
*/
public function clearCachedDefinitions() {
parent::clearCachedDefinitions();
$this->breakpointsByGroup = NULL;
$this->instances = array();
}
/**
* Gets the label for a breakpoint group.
*
* @param string $group
* The breakpoint group.
*
* @return string