mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-09-17 20:24:58 +00:00
Compare commits
99 Commits
bootstrap-
...
dune-risky
Author | SHA1 | Date | |
---|---|---|---|
915a2e7284 | |||
cb99074d1f | |||
cfd9f1bab1 | |||
358410cde1 | |||
06c74ed5ed | |||
25c986cc61 | |||
cad8174333 | |||
75c586fbf8 | |||
2cb9dfc250 | |||
5350a09951 | |||
9b1a66c992 | |||
74541f360b | |||
ea477a9842 | |||
73653744d7 | |||
c3ef8d112c | |||
|
8c98f2cf6e | ||
e1c8278f71 | |||
|
ed3f46ce7f | ||
7503c845df | |||
3686a294d3 | |||
2db847ada2 | |||
|
7819c1204c | ||
|
e470a6a97e | ||
|
cd8c47449b | ||
d6e7fadb4f | |||
|
2be1c08c44 | ||
|
d426d28ba0 | ||
|
72e69fc0b3 | ||
52472b0ec3 | |||
cab3b1059d | |||
4c82e65c1f | |||
cf4d7df7ad | |||
86d13410c3 | |||
19fdf2a503 | |||
|
5448238697 | ||
|
c5250a1059 | ||
b2c1a7b8de | |||
49c6915c0a | |||
b4583fc6dc | |||
f7c508939c | |||
a2160bef7d | |||
8641d6bdce | |||
5f4d513aa6 | |||
f47b15de39 | |||
7426dc02cf | |||
a570160aed | |||
6e81ef0065 | |||
e3cc76c689 | |||
b43f43fa0b | |||
7596bd5a06 | |||
f02d6df262 | |||
e4e2138765 | |||
395735e6f7 | |||
2c96c02261 | |||
53813f8f29 | |||
|
b8ef0d28f5 | ||
|
e973c4013f | ||
|
0f9a395dfc | ||
|
38ac3badef | ||
|
cda32fb925 | ||
|
58cb34f39b | ||
66426f5102 | |||
3445335b2d | |||
e52a9a4330 | |||
8adb05c24f | |||
b1207cbd3e | |||
|
09ed671734 | ||
93260ea36f | |||
|
8756982a78 | ||
a8f55e064d | |||
7df753f1cb | |||
b0fc4e190f | |||
928e0e1eed | |||
28e4b2abaf | |||
c5b21f360c | |||
2de8338651 | |||
fbec64664f | |||
6c81fe4ef2 | |||
520c0e0f96 | |||
2647d4c6a7 | |||
e7df62b373 | |||
b2ff470720 | |||
69ea88a4d5 | |||
9566ea89b1 | |||
425d51649f | |||
c32f730713 | |||
a48d6d2a0a | |||
16b155d449 | |||
083f56bff0 | |||
f526453ba3 | |||
|
03243605da | ||
|
48e2d2ceab | ||
|
777fb25860 | ||
|
03601b9707 | ||
89d95a0eae | |||
6bcdbaf668 | |||
0a0895584c | |||
d7d8f2a60a | |||
c205bbddd3 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,7 +1,8 @@
|
||||
.composer/*
|
||||
composer.phar
|
||||
composer.lock
|
||||
|
||||
docs/build/
|
||||
.php_cs.cache
|
||||
|
||||
###> symfony/framework-bundle ###
|
||||
/.env.local
|
||||
@@ -18,3 +19,4 @@ composer.lock
|
||||
/phpunit.xml
|
||||
.phpunit.result.cache
|
||||
###< phpunit/phpunit ###
|
||||
|
||||
|
@@ -30,6 +30,10 @@ variables:
|
||||
POSTGRES_PASSWORD: postgres
|
||||
# fetch the chill-app using git submodules
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
REDIS_HOST: redis
|
||||
REDIS_PORT: 6379
|
||||
REDIS_URL: redis://redis:6379
|
||||
|
||||
|
||||
# Run our tests
|
||||
test:
|
||||
|
9
README.md
Normal file
9
README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Chill framework
|
||||
|
||||
Documentation of the Chill software.
|
||||
|
||||
The online documentation can be found at http://docs.chill.social
|
||||
|
||||
See the [`docs`][1] directory for more.
|
||||
|
||||
[1]: docs/README.md
|
@@ -42,6 +42,7 @@
|
||||
"symfony/validator": "4.*",
|
||||
"sensio/framework-extra-bundle": "^5.5",
|
||||
"symfony/yaml": "4.*",
|
||||
"symfony/webpack-encore-bundle": "^1.11",
|
||||
"knplabs/knp-menu": "^3.1",
|
||||
"knplabs/knp-menu-bundle": "^3.0",
|
||||
"symfony/templating": "4.*",
|
||||
@@ -56,7 +57,8 @@
|
||||
"symfony/browser-kit": "^5.2",
|
||||
"symfony/css-selector": "^5.2",
|
||||
"twig/markdown-extra": "^3.3",
|
||||
"erusev/parsedown": "^1.7"
|
||||
"erusev/parsedown": "^1.7",
|
||||
"symfony/serializer": "^5.2"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/symfony": "*"
|
||||
|
@@ -10,10 +10,19 @@ Compilation into HTML
|
||||
|
||||
To compile this documentation :
|
||||
|
||||
1. Install [sphinx-doc](http://sphinx-doc.org) (eg. pip install sphinx & pip install sphinx_rtd_theme)
|
||||
1. Install [sphinx-doc](http://sphinx-doc.org)
|
||||
``` bash
|
||||
$ virtualenv .venv # creation of the virtual env (only the first time)
|
||||
$ source .venv/bin/activate # activate the virtual env
|
||||
(.venv) $ pip install -r requirements.txt
|
||||
```
|
||||
2. Install submodules : $ git submodule update --init;
|
||||
3. run `make html` from the root directory
|
||||
4. The base file is located on build/html/index.html
|
||||
``` bash
|
||||
$ cd build/html
|
||||
$ python -m http.server 8888 # will serve the site on the port 8888
|
||||
```
|
||||
|
||||
Contribute
|
||||
===========
|
||||
|
@@ -29,7 +29,7 @@ As Chill rely on the `symfony <http://symfony.com>`_ framework, reading the fram
|
||||
Timelines <timelines.rst>
|
||||
Exports <exports.rst>
|
||||
Embeddable comments <embeddable-comments.rst>
|
||||
Testing <make-test-working.rst>
|
||||
Run tests <run-tests.rst>
|
||||
Useful snippets <useful-snippets.rst>
|
||||
manual/index.rst
|
||||
Assets <assets.rst>
|
||||
|
@@ -1,231 +0,0 @@
|
||||
.. Copyright (C) 2014 Champs Libres Cooperative SCRLFS
|
||||
Permission is granted to copy, distribute and/or modify this document
|
||||
under the terms of the GNU Free Documentation License, Version 1.3
|
||||
or any later version published by the Free Software Foundation;
|
||||
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
|
||||
A copy of the license is included in the section entitled "GNU
|
||||
Free Documentation License".
|
||||
|
||||
Make tests working
|
||||
******************
|
||||
|
||||
Unit and functional tests are important to ensure that bundle may be deployed securely.
|
||||
|
||||
In reason of the Chill architecture, test should be runnable from the bundle's directory and works correctly: this will allow continuous integration tools to run tests automatically.
|
||||
|
||||
.. note::
|
||||
|
||||
Integration tools (i.e. `travis-ci <https://travis-ci.org>`_) works like this :
|
||||
|
||||
* they clone the bundle repository in a virtual machine, using git
|
||||
* they optionnaly run `composer` to download and install depedencies
|
||||
* they optionnaly run other command to prepare a database, insert fixtures, ...
|
||||
* they run test
|
||||
|
||||
On the developer's machine test should be runnable with two or three commands **runned from the bundle directory** :
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ composer install --dev
|
||||
$ // command to insert fixtures, ...
|
||||
$ phpunit
|
||||
|
||||
This chapter has been inspired by `this useful blog post <http://blog.kevingomez.fr/2013/01/09/functional-testing-standalone-symfony2-bundles/>`_.
|
||||
|
||||
Bootstrap phpunit for a standalone bundle
|
||||
==========================================
|
||||
|
||||
Unit tests should run after achieving this step.
|
||||
|
||||
|
||||
phpunit.xml
|
||||
-----------
|
||||
|
||||
A `phpunit.xml.dist` file should be present at the bundle root.
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit bootstrap="./Tests/bootstrap.php" colors="true">
|
||||
<!-- the file "./Tests/boostrap.php" will be created on the next step -->
|
||||
<testsuites>
|
||||
<testsuite name="ChillMain test suite">
|
||||
<directory suffix="Test.php">./Tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory>./</directory>
|
||||
<exclude>
|
||||
<directory>./Resources</directory>
|
||||
<directory>./Tests</directory>
|
||||
<directory>./vendor</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
|
||||
|
||||
bootstrap.php
|
||||
--------------
|
||||
|
||||
A file `boostrap.php`, located in the `Tests` directory, will allow phpunit to resolve class autoloading :
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
if (!is_file($autoloadFile = __DIR__.'/../vendor/autoload.php')) {
|
||||
throw new \LogicException('Could not find autoload.php in vendor/. Did you run "composer install --dev"?');
|
||||
}
|
||||
|
||||
require $autoloadFile;
|
||||
|
||||
|
||||
composer.json
|
||||
-------------
|
||||
|
||||
The `composer.json` file **located at the bundle's root** should contains all depencies needed to run test (and to execute bundle functions).
|
||||
|
||||
Ensure that all dependencies are included in the `require` and `require-dev` sections.
|
||||
|
||||
|
||||
Functional tests
|
||||
================
|
||||
|
||||
If you want to access services, database, and run functional tests, you will have to bootstrap a symfony app, with the minimal configuration. Three files are required :
|
||||
|
||||
* a `config_test.yml` file (eventually with a `config.yml`);
|
||||
* a `routing.yml` file
|
||||
* an `AppKernel.php` file
|
||||
|
||||
Adapt phpunit.xml
|
||||
-----------------
|
||||
|
||||
You should add reference to the new application within `phpunit.xml.dist`. The directive `<php>` should be added like this, if your `AppKernel.php` file is located in `Tests/Fixtures/App` directory:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit bootstrap="./Tests/bootstrap.php" colors="true">
|
||||
<testsuites>
|
||||
<testsuite name="ChillMain test suite">
|
||||
<directory suffix="Test.php">./Tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory>./</directory>
|
||||
<exclude>
|
||||
<directory>./Resources</directory>
|
||||
<directory>./Tests</directory>
|
||||
<directory>./vendor</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
<!-- the lines we added -->
|
||||
<php>
|
||||
<server name="KERNEL_DIR" value="./Tests/Fixtures/App/" />
|
||||
</php>
|
||||
</phpunit>
|
||||
|
||||
AppKernel.php
|
||||
-------------
|
||||
|
||||
This file boostrap the app. It contains three functions. This is the file used in the ChillMain bundle :
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Symfony\Component\HttpKernel\Kernel;
|
||||
use Symfony\Component\Config\Loader\LoaderInterface;
|
||||
|
||||
class AppKernel extends Kernel
|
||||
{
|
||||
public function registerBundles()
|
||||
{
|
||||
return array(
|
||||
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
|
||||
new Chill\MainBundle\ChillMainBundle(),
|
||||
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
|
||||
new Symfony\Bundle\TwigBundle\TwigBundle(),
|
||||
new \Symfony\Bundle\AsseticBundle\AsseticBundle(),
|
||||
#add here all the required bundle (some bundle are not required)
|
||||
);
|
||||
}
|
||||
|
||||
public function registerContainerConfiguration(LoaderInterface $loader)
|
||||
{
|
||||
$loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCacheDir()
|
||||
{
|
||||
return sys_get_temp_dir().'/ChillMainBundle/cache';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLogDir()
|
||||
{
|
||||
return sys_get_temp_dir().'/ChillMainBundle/logs';
|
||||
}
|
||||
}
|
||||
|
||||
config_test.yml
|
||||
---------------
|
||||
|
||||
There are only few parameters required for the config file. This is a basic version for ChillMain :
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# config/config_test.yml
|
||||
imports:
|
||||
- { resource: config.yml } #here we import a config.yml file, this is not required
|
||||
|
||||
framework:
|
||||
test: ~
|
||||
session:
|
||||
storage_id: session.storage.filesystem
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# config/config.yml
|
||||
framework:
|
||||
secret: Not very secret
|
||||
router: { resource: "%kernel.root_dir%/config/routing.yml" }
|
||||
form: true
|
||||
csrf_protection: true
|
||||
session: ~
|
||||
default_locale: fr
|
||||
translator: { fallback: fr }
|
||||
profiler: { only_exceptions: false }
|
||||
templating: #required for assetic. Remove if not needed
|
||||
engines: ['twig']
|
||||
|
||||
.. note::
|
||||
|
||||
You must adapt config.yml file according to your required bundle. Some options will be missing, other may be removed...
|
||||
|
||||
.. note::
|
||||
|
||||
If you would like to tests different environments, with differents configuration, you could create differents config_XXX.yml files.
|
||||
|
||||
routing.yml
|
||||
------------
|
||||
|
||||
You should add there all routing information needed for your bundle.
|
||||
|
||||
.. code-block: yaml
|
||||
|
||||
chill_main_bundle:
|
||||
resource: "@CLChillMainBundle/Resources/config/routing.yml"
|
||||
|
||||
That's it. Tests should pass.
|
68
docs/source/development/run-tests.rst
Normal file
68
docs/source/development/run-tests.rst
Normal file
@@ -0,0 +1,68 @@
|
||||
.. Copyright (C) 2014 Champs Libres Cooperative SCRLFS
|
||||
Permission is granted to copy, distribute and/or modify this document
|
||||
under the terms of the GNU Free Documentation License, Version 1.3
|
||||
or any later version published by the Free Software Foundation;
|
||||
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
|
||||
A copy of the license is included in the section entitled "GNU
|
||||
Free Documentation License".
|
||||
|
||||
Run tests
|
||||
*********
|
||||
|
||||
In reason of the Chill architecture, test should be runnable from the bundle's directory and works correctly: this will allow continuous integration tools to run tests automatically.
|
||||
|
||||
From chill app
|
||||
==============
|
||||
|
||||
This is the most convenient method for developer: run test for chill bundle from the main app.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# run into a container
|
||||
docker-compose exec --user $(id -u) php bash
|
||||
# execute all tests suites
|
||||
bin/phpunit
|
||||
# .. or execute a single test
|
||||
bin/phpunit vendor/chill-project/chill-bundles/src/Bundle/ChillMainBundle/Tests/path/to/FileTest.php
|
||||
|
||||
You can also run tests in a single command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker-compose exec --user $(id -u) php bin/phpunit
|
||||
|
||||
|
||||
Tests from a bundle (chill-bundles)
|
||||
-----------------------------------
|
||||
|
||||
Those tests needs the whole symfony app to execute Application Tests (which test html page).
|
||||
|
||||
For ease, the app is cloned using a :code:`git submodule`, which clone the main app into :code:`tests/app`, and tests are bootstrapped to this app. The dependencies are also installed into `tests/app/vendor` to ensure compliance with relative path from this symfony application.
|
||||
|
||||
You may boostrap the tests fro the chill bundle this way:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# ensure to be located into the environement (provided by docker suits well)
|
||||
docker-compose exec --user $(id -u) php bash
|
||||
# go to chill subdirectory
|
||||
cd vendor/chill-project/chill-bundles
|
||||
# install submodule
|
||||
git submodule init
|
||||
git submodule update
|
||||
# install composer and dependencies
|
||||
curl -sS https://getcomposer.org/installer | php
|
||||
# run tests
|
||||
bin/phpunit
|
||||
|
||||
.. note::
|
||||
|
||||
If you are on a fresh install, you will need to migrate database schema.
|
||||
|
||||
The path to console tool must be adapted to the app. To load migration and add fixtures, one can execute the following commands:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
tests/app/bin/console doctrine:migrations:migrate
|
||||
tests/app/bin/console doctrine:fixtures:load
|
||||
|
@@ -97,7 +97,7 @@ The has the following signature :
|
||||
*
|
||||
* @param string $context
|
||||
* @param mixed[] $args the argument to the context.
|
||||
* @return string[]
|
||||
* @return TimelineSingleQuery
|
||||
* @throw \LogicException if the context is not supported
|
||||
*/
|
||||
public function fetchQuery($context, array $args);
|
||||
@@ -163,18 +163,16 @@ The has the following signature :
|
||||
The `fetchQuery` function
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The fetchQuery function help to build the UNION query to gather events. This function should return an associative array MUST have the following key :
|
||||
The fetchQuery function help to build the UNION query to gather events. This function should return an instance of :code:`TimelineSingleQuery`. For you convenience, this object may be build using an associative array with the following keys:
|
||||
|
||||
* `id` : the name of the id column
|
||||
* `type`: a string to indicate the type
|
||||
* `date`: the name of the datetime column, used to order entities by date
|
||||
* `FROM` (in capital) : the FROM clause. May contains JOIN instructions
|
||||
* `FROM`: the FROM clause. May contains JOIN instructions
|
||||
* `WHERE`: the WHERE clause;
|
||||
* `parameters`: the parameters to pass to the query
|
||||
|
||||
Those key are optional:
|
||||
|
||||
* `WHERE` (in capital) : the WHERE clause.
|
||||
|
||||
Where relevant, the data must be quoted to avoid SQL injection.
|
||||
The parameters should be replaced into the query by :code:`?`. They will be replaced into the query using prepared statements.
|
||||
|
||||
`$context` and `$args` are defined by the bundle which will call the timeline rendering. You may use them to build a different query depending on this context.
|
||||
|
||||
@@ -186,6 +184,15 @@ For instance, if the context is `'person'`, the args will be this array :
|
||||
'person' => $person //a \Chill\PersonBundle\Entity\Person entity
|
||||
);
|
||||
|
||||
For the context :code:`center`, the args will be:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
array(
|
||||
'centers' => [ ] // an array of \Chill\MainBundle\Entity\Center entities
|
||||
);
|
||||
|
||||
|
||||
You should find in the bundle documentation which contexts are arguments the bundle defines.
|
||||
|
||||
.. note::
|
||||
@@ -199,13 +206,12 @@ Example of an implementation :
|
||||
namespace Chill\ReportBundle\Timeline;
|
||||
|
||||
use Chill\MainBundle\Timeline\TimelineProviderInterface;
|
||||
use Chill\MainBundle\Timeline\TimelineSingleQuery;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
|
||||
/**
|
||||
* Provide report for inclusion in timeline
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
* @author Champs Libres <info@champs-libres.coop>
|
||||
*/
|
||||
class TimelineReportProvider implements TimelineProviderInterface
|
||||
{
|
||||
@@ -227,16 +233,17 @@ Example of an implementation :
|
||||
|
||||
$metadata = $this->em->getClassMetadata('ChillReportBundle:Report');
|
||||
|
||||
return array(
|
||||
return TimelineSingleQuery::fromArray([
|
||||
'id' => $metadata->getColumnName('id'),
|
||||
'type' => 'report',
|
||||
'date' => $metadata->getColumnName('date'),
|
||||
'FROM' => $metadata->getTableName(),
|
||||
'WHERE' => sprintf('%s = %d',
|
||||
'WHERE' => sprintf('%s = ?',
|
||||
$metadata
|
||||
->getAssociationMapping('person')['joinColumns'][0]['name'],
|
||||
$args['person']->getId())
|
||||
);
|
||||
->getAssociationMapping('person')['joinColumns'][0]['name'])
|
||||
)
|
||||
'parameters' => [ $args['person']->getId() ]
|
||||
]);
|
||||
}
|
||||
|
||||
//....
|
||||
|
@@ -18,11 +18,11 @@
|
||||
<testsuite name="MainBundle">
|
||||
<directory suffix="Test.php">src/Bundle/ChillMainBundle/Tests/</directory>
|
||||
</testsuite>
|
||||
|
||||
<!--
|
||||
<testsuite name="PersonBundle">
|
||||
<directory suffix="Test.php">src/Bundle/ChillPersonBundle/Tests/</directory>
|
||||
</testsuite>
|
||||
|
||||
-->
|
||||
</testsuites>
|
||||
|
||||
<listeners>
|
||||
|
@@ -38,7 +38,7 @@ use Chill\MainBundle\Validator\Constraints\Entity\UserCircleConsistency;
|
||||
* Class Activity
|
||||
*
|
||||
* @package Chill\ActivityBundle\Entity
|
||||
* @ORM\Entity()
|
||||
* @ORM\Entity(repositoryClass="Chill\ActivityBundle\Repository\ActivityRepository")
|
||||
* @ORM\Table(name="activity")
|
||||
* @ORM\HasLifecycleCallbacks()
|
||||
* @UserCircleConsistency(
|
||||
|
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* Copyright (C) 2021, Champs Libres Cooperative SCRLFS,
|
||||
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Chill\ActivityBundle\Repository;
|
||||
|
||||
use Chill\ActivityBundle\Entity\Activity;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\ActivityBundle\Repository\ActivityRepository;
|
||||
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Doctrine\ORM\Query\Expr\Orx;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
|
||||
final class ActivityACLAwareRepository
|
||||
{
|
||||
private AuthorizationHelper $authorizationHelper;
|
||||
|
||||
private TokenStorageInterface $tokenStorage;
|
||||
|
||||
private ActivityRepository $repository;
|
||||
|
||||
private EntityManagerInterface $em;
|
||||
|
||||
public function __construct(
|
||||
AuthorizationHelper $authorizationHelper,
|
||||
TokenStorageInterface $tokenStorage,
|
||||
ActivityRepository $repository,
|
||||
EntityManagerInterface $em
|
||||
) {
|
||||
$this->authorizationHelper = $authorizationHelper;
|
||||
$this->tokenStorage = $tokenStorage;
|
||||
$this->repository = $repository;
|
||||
$this->em = $em;
|
||||
}
|
||||
|
||||
public function queryTimelineIndexer(string $context, array $args = []): array
|
||||
{
|
||||
$metadataActivity = $this->em->getClassMetadata(Activity::class);
|
||||
|
||||
$from = $this->getFromClauseCenter($args);
|
||||
[$where, $parameters] = $this->getWhereClause($context, $args);
|
||||
|
||||
return [
|
||||
'id' => $metadataActivity->getTableName()
|
||||
.'.'.$metadataActivity->getColumnName('id'),
|
||||
'type' => 'activity',
|
||||
'date' => $metadataActivity->getTableName()
|
||||
.'.'.$metadataActivity->getColumnName('date'),
|
||||
'FROM' => $from,
|
||||
'WHERE' => $where,
|
||||
'parameters' => $parameters
|
||||
];
|
||||
}
|
||||
|
||||
private function getFromClauseCenter(array $args): string
|
||||
{
|
||||
$metadataActivity = $this->em->getClassMetadata(Activity::class);
|
||||
$metadataPerson = $this->em->getClassMetadata(Person::class);
|
||||
$associationMapping = $metadataActivity->getAssociationMapping('person');
|
||||
|
||||
return $metadataActivity->getTableName().' JOIN '
|
||||
.$metadataPerson->getTableName().' ON '
|
||||
.$metadataPerson->getTableName().'.'.
|
||||
$associationMapping['joinColumns'][0]['referencedColumnName']
|
||||
.' = '
|
||||
.$associationMapping['joinColumns'][0]['name']
|
||||
;
|
||||
}
|
||||
|
||||
private function getWhereClause(string $context, array $args): array
|
||||
{
|
||||
$where = '';
|
||||
$parameters = [];
|
||||
|
||||
$metadataActivity = $this->em->getClassMetadata(Activity::class);
|
||||
$metadataPerson = $this->em->getClassMetadata(Person::class);
|
||||
$activityToPerson = $metadataActivity->getAssociationMapping('person')['joinColumns'][0]['name'];
|
||||
$activityToScope = $metadataActivity->getAssociationMapping('scope')['joinColumns'][0]['name'];
|
||||
$personToCenter = $metadataPerson->getAssociationMapping('center')['joinColumns'][0]['name'];
|
||||
|
||||
|
||||
// acls:
|
||||
$role = new Role(ActivityVoter::SEE);
|
||||
$reachableCenters = $this->authorizationHelper->getReachableCenters($this->tokenStorage->getToken()->getUser(),
|
||||
$role);
|
||||
|
||||
if (count($reachableCenters) === 0) {
|
||||
// insert a dummy condition
|
||||
return 'FALSE = TRUE';
|
||||
}
|
||||
|
||||
if ($context === 'person') {
|
||||
// we start with activities having the person_id linked to person
|
||||
$where .= sprintf('%s = ? AND ', $activityToPerson);
|
||||
$parameters[] = $person->getId();
|
||||
}
|
||||
|
||||
// we add acl (reachable center and scopes)
|
||||
$where .= '('; // first loop for the for centers
|
||||
$centersI = 0; // like centers#i
|
||||
foreach ($reachableCenters as $center) {
|
||||
// we pass if not in centers
|
||||
if (!\in_array($center, $args['centers'])) {
|
||||
continue;
|
||||
}
|
||||
// we get all the reachable scopes for this center
|
||||
$reachableScopes = $this->authorizationHelper->getReachableScopes($this->tokenStorage->getToken()->getUser(), $role, $center);
|
||||
// we get the ids for those scopes
|
||||
$reachablesScopesId = array_map(
|
||||
function(Scope $scope) { return $scope->getId(); },
|
||||
$reachableScopes
|
||||
);
|
||||
|
||||
// if not the first center
|
||||
if ($centersI > 0) {
|
||||
$where .= ') OR (';
|
||||
}
|
||||
|
||||
// condition for the center
|
||||
$where .= sprintf(' %s.%s = ? ', $metadataPerson->getTableName(), $personToCenter);
|
||||
$parameters[] = $center->getId();
|
||||
|
||||
// begin loop for scopes
|
||||
$where .= ' AND (';
|
||||
$scopesI = 0; //like scope#i
|
||||
|
||||
foreach ($reachablesScopesId as $scopeId) {
|
||||
if ($scopesI > 0) {
|
||||
$where .= ' OR ';
|
||||
}
|
||||
$where .= sprintf(' %s.%s = ? ', $metadataActivity->getTableName(), $activityToScope);
|
||||
$parameters[] = $scopeId;
|
||||
$scopesI ++;
|
||||
}
|
||||
// close loop for scopes
|
||||
$where .= ') ';
|
||||
$centersI++;
|
||||
}
|
||||
// close loop for centers
|
||||
$where .= ')';
|
||||
|
||||
return [$where, $parameters];
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* Copyright (C) 2021, Champs Libres Cooperative SCRLFS,
|
||||
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Chill\ActivityBundle\Repository;
|
||||
|
||||
use Chill\ActivityBundle\Entity\Activity;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @method AccompanyingPeriodParticipation|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method AccompanyingPeriodParticipation|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method AccompanyingPeriodParticipation[] findAll()
|
||||
* @method AccompanyingPeriodParticipation[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class ActivityRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, Activity::class);
|
||||
}
|
||||
|
||||
}
|
@@ -1,11 +1,11 @@
|
||||
{% import 'ChillActivityBundle:ActivityReason:macro.html.twig' as m %}
|
||||
|
||||
<div>
|
||||
<h3>{{ activity.date|format_date('long') }}<span class="activity"> / {{ 'Activity'|trans }}</span></h3>
|
||||
<h3>{{ activity.date|format_date('long') }}<span class="activity"> / {{ 'Activity'|trans }}</span>{% if 'person' != context %} / {{ activity.person|chill_entity_render_box({'addLink': true}) }}{% endif %}</h3>
|
||||
<div class="statement">
|
||||
<span class="statement">{{ '%user% has done an %activity_type%'|trans(
|
||||
{
|
||||
'%user%' : user,
|
||||
'%user%' : activity.user,
|
||||
'%activity_type%': activity.type.name|localize_translatable_string,
|
||||
'%date%' : activity.date|format_date('long') }
|
||||
) }}</span>
|
||||
@@ -29,13 +29,13 @@
|
||||
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a href="{{ path('chill_activity_activity_show', { 'person_id': person.id, 'id': activity.id} ) }}" class="sc-button bt-view">
|
||||
<a href="{{ path('chill_activity_activity_show', { 'person_id': activity.person.id, 'id': activity.id} ) }}" class="sc-button bt-view">
|
||||
{{ 'Show the activity'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
{% if is_granted('CHILL_ACTIVITY_UPDATE', activity) %}
|
||||
<li>
|
||||
<a href="{{ path('chill_activity_activity_edit', { 'person_id': person.id, 'id': activity.id} ) }}" class="sc-button bt-edit">
|
||||
<a href="{{ path('chill_activity_activity_edit', { 'person_id': activity.person.id, 'id': activity.id} ) }}" class="sc-button bt-edit">
|
||||
{{ 'Edit the activity'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
|
@@ -21,6 +21,7 @@
|
||||
namespace Chill\ActivityBundle\Timeline;
|
||||
|
||||
use Chill\MainBundle\Timeline\TimelineProviderInterface;
|
||||
use Chill\ActivityBundle\Repository\ActivityACLAwareRepository;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
@@ -28,13 +29,13 @@ use Symfony\Component\Security\Core\Role\Role;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\ActivityBundle\Entity\Activity;
|
||||
use Chill\MainBundle\Timeline\TimelineSingleQuery;
|
||||
|
||||
/**
|
||||
* Provide activity for inclusion in timeline
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
* @author Champs Libres <info@champs-libres.coop>
|
||||
*/
|
||||
*/
|
||||
class TimelineActivityProvider implements TimelineProviderInterface
|
||||
{
|
||||
|
||||
@@ -55,6 +56,10 @@ class TimelineActivityProvider implements TimelineProviderInterface
|
||||
* @var \Chill\MainBundle\Entity\User
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
protected ActivityACLAwareRepository $aclAwareRepository;
|
||||
|
||||
private const SUPPORTED_CONTEXTS = [ 'center', 'person'];
|
||||
|
||||
/**
|
||||
* TimelineActivityProvider constructor.
|
||||
@@ -66,11 +71,13 @@ class TimelineActivityProvider implements TimelineProviderInterface
|
||||
public function __construct(
|
||||
EntityManager $em,
|
||||
AuthorizationHelper $helper,
|
||||
TokenStorageInterface $storage
|
||||
TokenStorageInterface $storage,
|
||||
ActivityACLAwareRepository $aclAwareRepository
|
||||
)
|
||||
{
|
||||
$this->em = $em;
|
||||
$this->helper = $helper;
|
||||
$this->aclAwareRepository = $aclAwareRepository;
|
||||
|
||||
if (!$storage->getToken()->getUser() instanceof \Chill\MainBundle\Entity\User)
|
||||
{
|
||||
@@ -86,67 +93,69 @@ class TimelineActivityProvider implements TimelineProviderInterface
|
||||
*/
|
||||
public function fetchQuery($context, array $args)
|
||||
{
|
||||
$this->checkContext($context);
|
||||
if ('center' === $context) {
|
||||
return TimelineSingleQuery::fromArray($this->aclAwareRepository
|
||||
->queryTimelineIndexer($context, $args));
|
||||
}
|
||||
|
||||
$metadataActivity = $this->em->getClassMetadata('ChillActivityBundle:Activity');
|
||||
$metadataPerson = $this->em->getClassMetadata('ChillPersonBundle:Person');
|
||||
$metadataActivity = $this->em->getClassMetadata(Activity::class);
|
||||
|
||||
[$where, $parameters] = $this->getWhereClauseForPerson($args['person']);
|
||||
|
||||
return array(
|
||||
return TimelineSingleQuery::fromArray([
|
||||
'id' => $metadataActivity->getTableName()
|
||||
.'.'.$metadataActivity->getColumnName('id'),
|
||||
'type' => 'activity',
|
||||
'date' => $metadataActivity->getTableName()
|
||||
.'.'.$metadataActivity->getColumnName('date'),
|
||||
'FROM' => $this->getFromClause($metadataActivity, $metadataPerson),
|
||||
'WHERE' => $this->getWhereClause($metadataActivity, $metadataPerson,
|
||||
$args['person'])
|
||||
);
|
||||
'FROM' => $this->getFromClausePerson($args['person']),
|
||||
'WHERE' => $where,
|
||||
'parameters' => $parameters
|
||||
]);
|
||||
}
|
||||
|
||||
private function getWhereClause(ClassMetadata $metadataActivity,
|
||||
ClassMetadata $metadataPerson, Person $person)
|
||||
private function getWhereClauseForPerson(Person $person)
|
||||
{
|
||||
$role = new Role('CHILL_ACTIVITY_SEE');
|
||||
$reachableCenters = $this->helper->getReachableCenters($this->user,
|
||||
$role);
|
||||
$parameters = [];
|
||||
$metadataActivity = $this->em->getClassMetadata(Activity::class);
|
||||
$associationMapping = $metadataActivity->getAssociationMapping('person');
|
||||
|
||||
if (count($reachableCenters) === 0) {
|
||||
return 'FALSE = TRUE';
|
||||
$role = new Role('CHILL_ACTIVITY_SEE');
|
||||
$reachableScopes = $this->helper->getReachableScopes($this->user,
|
||||
$role, $person->getCenter());
|
||||
$whereClause = sprintf(' {activity.person_id} = ? AND {activity.scope_id} IN ({scopes_ids}) ');
|
||||
$scopes_ids = [];
|
||||
|
||||
// first parameter: activity.person_id
|
||||
$parameters[] = $person->getId();
|
||||
|
||||
// loop on reachable scopes
|
||||
foreach ($reachableScopes as $scope) {
|
||||
if (\in_array($scope->getId(), $scopes_ids)) {
|
||||
continue;
|
||||
}
|
||||
$scopes_ids[] = '?';
|
||||
$parameters[] = $scope->getId();
|
||||
}
|
||||
|
||||
// we start with activities having the person_id linked to person
|
||||
// (currently only context "person" is supported)
|
||||
$whereClause = sprintf('%s = %d',
|
||||
$associationMapping['joinColumns'][0]['name'],
|
||||
$person->getId());
|
||||
|
||||
// we add acl (reachable center and scopes)
|
||||
$centerAndScopeLines = array();
|
||||
foreach ($reachableCenters as $center) {
|
||||
$reachablesScopesId = array_map(
|
||||
function(Scope $scope) { return $scope->getId(); },
|
||||
$this->helper->getReachableScopes($this->user, $role,
|
||||
$person->getCenter())
|
||||
);
|
||||
|
||||
$centerAndScopeLines[] = sprintf('(%s = %d AND %s IN (%s))',
|
||||
$metadataPerson->getTableName().'.'.
|
||||
$metadataPerson->getAssociationMapping('center')['joinColumns'][0]['name'],
|
||||
$center->getId(),
|
||||
$metadataActivity->getTableName().'.'.
|
||||
|
||||
return [
|
||||
\strtr(
|
||||
$whereClause,
|
||||
[
|
||||
'{activity.person_id}' => $associationMapping['joinColumns'][0]['name'],
|
||||
'{activity.scope_id}' => $metadataActivity->getTableName().'.'.
|
||||
$metadataActivity->getAssociationMapping('scope')['joinColumns'][0]['name'],
|
||||
implode(',', $reachablesScopesId));
|
||||
|
||||
}
|
||||
$whereClause .= ' AND ('.implode(' OR ', $centerAndScopeLines).')';
|
||||
|
||||
return $whereClause;
|
||||
'{scopes_ids}' => \implode(", ", $scopes_ids)
|
||||
,
|
||||
]
|
||||
),
|
||||
$parameters
|
||||
];
|
||||
}
|
||||
|
||||
private function getFromClause(ClassMetadata $metadataActivity,
|
||||
ClassMetadata $metadataPerson)
|
||||
private function getFromClausePerson()
|
||||
{
|
||||
$metadataActivity = $this->em->getClassMetadata(Activity::class);
|
||||
$metadataPerson = $this->em->getClassMetadata(Person::class);
|
||||
$associationMapping = $metadataActivity->getAssociationMapping('person');
|
||||
|
||||
return $metadataActivity->getTableName().' JOIN '
|
||||
@@ -157,14 +166,14 @@ class TimelineActivityProvider implements TimelineProviderInterface
|
||||
.$associationMapping['joinColumns'][0]['name']
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getEntities(array $ids)
|
||||
{
|
||||
$activities = $this->em->getRepository('ChillActivityBundle:Activity')
|
||||
$activities = $this->em->getRepository(Activity::class)
|
||||
->findBy(array('id' => $ids));
|
||||
|
||||
$result = array();
|
||||
@@ -183,14 +192,13 @@ class TimelineActivityProvider implements TimelineProviderInterface
|
||||
{
|
||||
$this->checkContext($context);
|
||||
|
||||
return array(
|
||||
return [
|
||||
'template' => 'ChillActivityBundle:Timeline:activity_person_context.html.twig',
|
||||
'template_data' => array(
|
||||
'template_data' => [
|
||||
'activity' => $entity,
|
||||
'person' => $args['person'],
|
||||
'user' => $entity->getUser()
|
||||
)
|
||||
);
|
||||
'context' => $context
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -210,7 +218,7 @@ class TimelineActivityProvider implements TimelineProviderInterface
|
||||
*/
|
||||
private function checkContext($context)
|
||||
{
|
||||
if ($context !== 'person') {
|
||||
if (FALSE === \in_array($context, self::SUPPORTED_CONTEXTS)) {
|
||||
throw new \LogicException("The context '$context' is not "
|
||||
. "supported. Currently only 'person' is supported");
|
||||
}
|
||||
|
@@ -22,6 +22,8 @@ services:
|
||||
- '@doctrine.orm.entity_manager'
|
||||
- '@chill.main.security.authorization.helper'
|
||||
- '@security.token_storage'
|
||||
- '@Chill\ActivityBundle\Repository\ActivityACLAwareRepository'
|
||||
public: true
|
||||
tags:
|
||||
- { name: chill.timeline, context: 'person' }
|
||||
- { name: chill.timeline, context: 'center' }
|
||||
|
@@ -1,18 +1,32 @@
|
||||
---
|
||||
services:
|
||||
chill_activity.repository.activity_type:
|
||||
class: Doctrine\ORM\EntityRepository
|
||||
factory: ['@doctrine.orm.entity_manager', getRepository]
|
||||
arguments:
|
||||
- 'Chill\ActivityBundle\Entity\ActivityType'
|
||||
|
||||
|
||||
chill_activity.repository.reason:
|
||||
class: Doctrine\ORM\EntityRepository
|
||||
factory: ['@doctrine.orm.entity_manager', getRepository]
|
||||
arguments:
|
||||
- 'Chill\ActivityBundle\Entity\ActivityReason'
|
||||
|
||||
|
||||
chill_activity.repository.reason_category:
|
||||
class: Doctrine\ORM\EntityRepository
|
||||
factory: ['@doctrine.orm.entity_manager', getRepository]
|
||||
arguments:
|
||||
- 'Chill\ActivityBundle\Entity\ActivityReasonCategory'
|
||||
|
||||
Chill\ActivityBundle\Repository\ActivityRepository:
|
||||
tags: [doctrine.repository_service]
|
||||
arguments:
|
||||
- '@Doctrine\Persistence\ManagerRegistry'
|
||||
|
||||
Chill\ActivityBundle\Repository\ActivityACLAwareRepository:
|
||||
arguments:
|
||||
$tokenStorage: '@Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'
|
||||
$authorizationHelper: '@Chill\MainBundle\Security\Authorization\AuthorizationHelper'
|
||||
$repository: '@Chill\ActivityBundle\Repository\ActivityRepository'
|
||||
$em: '@Doctrine\ORM\EntityManagerInterface'
|
||||
|
||||
|
@@ -22,13 +22,12 @@ class ChoiceWithOtherType extends AbstractType
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
|
||||
//add an 'other' entry in choices array
|
||||
$options['choices'][$this->otherValueLabel] = '_other';
|
||||
//ChoiceWithOther must always be expanded
|
||||
$options['expanded'] = true;
|
||||
// adding a default value for choice
|
||||
$options['empty_data'] = null;
|
||||
$options['empty_data'] = $options['multiple'] ? [] : null;
|
||||
|
||||
$builder
|
||||
->add('_other', TextType::class, array('required' => false))
|
||||
|
@@ -23,6 +23,7 @@ namespace Chill\EventBundle\Timeline;
|
||||
use Chill\EventBundle\Entity\Event;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Timeline\TimelineProviderInterface;
|
||||
use Chill\MainBundle\Timeline\TimelineSingleQuery;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
@@ -88,13 +89,14 @@ class TimelineEventProvider implements TimelineProviderInterface
|
||||
$metadataParticipation = $this->em->getClassMetadata('ChillEventBundle:Participation');
|
||||
$metadataPerson = $this->em->getClassMetadata('ChillPersonBundle:Person');
|
||||
|
||||
$query = array(
|
||||
$query = TimelineSingleQuery::fromArray([
|
||||
'id' => $metadataEvent->getTableName().'.'.$metadataEvent->getColumnName('id'),
|
||||
'type' => 'event',
|
||||
'date' => $metadataEvent->getTableName().'.'.$metadataEvent->getColumnName('date'),
|
||||
'FROM' => $this->getFromClause($metadataEvent, $metadataParticipation, $metadataPerson),
|
||||
'WHERE' => $this->getWhereClause($metadataEvent, $metadataParticipation, $metadataPerson, $args['person'])
|
||||
);
|
||||
'WHERE' => $this->getWhereClause($metadataEvent, $metadataParticipation, $metadataPerson, $args['person']),
|
||||
'parameters' => []
|
||||
]);
|
||||
|
||||
return $query;
|
||||
}
|
||||
@@ -238,4 +240,4 @@ class TimelineEventProvider implements TimelineProviderInterface
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Champs-Libres Coopérative <info@champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Chill\MainBundle\Timeline\TimelineBuilder;
|
||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
||||
use Symfony\Component\Security\Core\Role\Role;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
||||
class TimelineCenterController extends AbstractController
|
||||
{
|
||||
|
||||
protected TimelineBuilder $timelineBuilder;
|
||||
|
||||
protected PaginatorFactory $paginatorFactory;
|
||||
|
||||
private Security $security;
|
||||
|
||||
public function __construct(
|
||||
TimelineBuilder $timelineBuilder,
|
||||
PaginatorFactory $paginatorFactory,
|
||||
Security $security
|
||||
) {
|
||||
$this->timelineBuilder = $timelineBuilder;
|
||||
$this->paginatorFactory = $paginatorFactory;
|
||||
$this->security = $security;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/{_locale}/center/timeline",
|
||||
* name="chill_center_timeline",
|
||||
* methods={"GET"}
|
||||
* )
|
||||
*/
|
||||
public function centerAction(Request $request)
|
||||
{
|
||||
// collect reachable center for each group
|
||||
$user = $this->security->getUser();
|
||||
$centers = [];
|
||||
foreach ($user->getGroupCenters() as $group) {
|
||||
$centers[] = $group->getCenter();
|
||||
}
|
||||
|
||||
if (0 === count($centers)) {
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
$nbItems = $this->timelineBuilder->countItems('center',
|
||||
[ 'centers' => $centers ]
|
||||
);
|
||||
|
||||
$paginator = $this->paginatorFactory->create($nbItems);
|
||||
|
||||
return $this->render('@ChillMain/Timeline/index.html.twig', array
|
||||
(
|
||||
'timeline' => $this->timelineBuilder->getTimelineHTML(
|
||||
'center',
|
||||
[ 'centers' => $centers ],
|
||||
$paginator->getCurrentPage()->getFirstItemNumber(),
|
||||
$paginator->getItemsPerPage()
|
||||
),
|
||||
'nb_items' => $nbItems,
|
||||
'paginator' => $paginator
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@@ -47,11 +47,11 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
|
||||
{
|
||||
/**
|
||||
* widget factory
|
||||
*
|
||||
*
|
||||
* @var WidgetFactoryInterface[]
|
||||
*/
|
||||
protected $widgetFactories = array();
|
||||
|
||||
|
||||
/**
|
||||
* @param WidgetFactoryInterface $factory
|
||||
*/
|
||||
@@ -59,7 +59,7 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
|
||||
{
|
||||
$this->widgetFactories[] = $factory;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return WidgetFactoryInterface[]
|
||||
*/
|
||||
@@ -67,7 +67,7 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
|
||||
{
|
||||
return $this->widgetFactories;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @param array $configs
|
||||
@@ -79,31 +79,31 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
|
||||
// configuration for main bundle
|
||||
$configuration = $this->getConfiguration($configs, $container);
|
||||
$config = $this->processConfiguration($configuration, $configs);
|
||||
|
||||
|
||||
$container->setParameter('chill_main.installation_name',
|
||||
$config['installation_name']);
|
||||
|
||||
$container->setParameter('chill_main.available_languages',
|
||||
$config['available_languages']);
|
||||
|
||||
$container->setParameter('chill_main.routing.resources',
|
||||
$config['routing']['resources']);
|
||||
|
||||
|
||||
$container->setParameter('chill_main.routing.resources',
|
||||
$config['routing']['resources']);
|
||||
|
||||
$container->setParameter('chill_main.pagination.item_per_page',
|
||||
$config['pagination']['item_per_page']);
|
||||
|
||||
$container->setParameter('chill_main.notifications',
|
||||
|
||||
$container->setParameter('chill_main.notifications',
|
||||
$config['notifications']);
|
||||
|
||||
$container->setParameter('chill_main.redis',
|
||||
|
||||
$container->setParameter('chill_main.redis',
|
||||
$config['redis']);
|
||||
|
||||
$container->setParameter('chill_main.phone_helper',
|
||||
|
||||
$container->setParameter('chill_main.phone_helper',
|
||||
$config['phone_helper'] ?? []);
|
||||
|
||||
|
||||
// add the key 'widget' without the key 'enable'
|
||||
$container->setParameter('chill_main.widgets',
|
||||
isset($config['widgets']['homepage']) ?
|
||||
$container->setParameter('chill_main.widgets',
|
||||
isset($config['widgets']['homepage']) ?
|
||||
array('homepage' => $config['widgets']['homepage']):
|
||||
array()
|
||||
);
|
||||
@@ -131,10 +131,11 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
|
||||
$loader->load('services/templating.yaml');
|
||||
$loader->load('services/timeline.yaml');
|
||||
$loader->load('services/search.yaml');
|
||||
|
||||
$loader->load('services/serializer.yaml');
|
||||
|
||||
$this->configureCruds($container, $config['cruds'], $loader);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $config
|
||||
* @param ContainerBuilder $container
|
||||
@@ -144,11 +145,11 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
|
||||
{
|
||||
return new Configuration($this->widgetFactories, $container);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param ContainerBuilder $container
|
||||
*/
|
||||
public function prepend(ContainerBuilder $container)
|
||||
public function prepend(ContainerBuilder $container)
|
||||
{
|
||||
//add installation_name and date_format to globals
|
||||
$chillMainConfig = $container->getExtensionConfig($this->getAlias());
|
||||
@@ -163,7 +164,7 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
|
||||
'form_themes' => array('@ChillMain/Form/fields.html.twig')
|
||||
);
|
||||
$container->prependExtensionConfig('twig', $twigConfig);
|
||||
|
||||
|
||||
//add DQL function to ORM (default entity_manager)
|
||||
$container->prependExtensionConfig('doctrine', array(
|
||||
'orm' => array(
|
||||
@@ -182,7 +183,7 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
|
||||
)
|
||||
)
|
||||
));
|
||||
|
||||
|
||||
//add dbal types (default entity_manager)
|
||||
$container->prependExtensionConfig('doctrine', array(
|
||||
'dbal' => [
|
||||
@@ -191,23 +192,23 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
|
||||
]
|
||||
]
|
||||
));
|
||||
|
||||
|
||||
//add current route to chill main
|
||||
$container->prependExtensionConfig('chill_main', array(
|
||||
'routing' => array(
|
||||
'resources' => array(
|
||||
'@ChillMainBundle/config/routes.yaml'
|
||||
)
|
||||
|
||||
|
||||
)
|
||||
));
|
||||
|
||||
|
||||
//add a channel to log app events
|
||||
$container->prependExtensionConfig('monolog', array(
|
||||
'channels' => array('chill')
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param ContainerBuilder $container
|
||||
* @param array $config the config under 'cruds' key
|
||||
@@ -218,31 +219,31 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
|
||||
if (count($config) === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$loader->load('services/crud.yaml');
|
||||
|
||||
|
||||
$container->setParameter('chill_main_crud_route_loader_config', $config);
|
||||
|
||||
|
||||
$definition = new Definition();
|
||||
$definition
|
||||
->setClass(\Chill\MainBundle\CRUD\Routing\CRUDRoutesLoader::class)
|
||||
->addArgument('%chill_main_crud_route_loader_config%')
|
||||
;
|
||||
|
||||
|
||||
$container->setDefinition('chill_main_crud_route_loader', $definition);
|
||||
|
||||
|
||||
$alreadyExistingNames = [];
|
||||
|
||||
|
||||
foreach ($config as $crudEntry) {
|
||||
$controller = $crudEntry['controller'];
|
||||
$controllerServiceName = 'cscrud_'.$crudEntry['name'].'_controller';
|
||||
$name = $crudEntry['name'];
|
||||
|
||||
|
||||
// check for existing crud names
|
||||
if (\in_array($name, $alreadyExistingNames)) {
|
||||
throw new LogicException(sprintf("the name %s is defined twice in CRUD", $name));
|
||||
}
|
||||
|
||||
|
||||
if (!$container->has($controllerServiceName)) {
|
||||
$controllerDefinition = new Definition($controller);
|
||||
$controllerDefinition->addTag('controller.service_arguments');
|
||||
@@ -250,7 +251,7 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
|
||||
$controllerDefinition->setClass($crudEntry['controller']);
|
||||
$container->setDefinition($controllerServiceName, $controllerDefinition);
|
||||
}
|
||||
|
||||
|
||||
$container->setParameter('chill_main_crud_config_'.$name, $crudEntry);
|
||||
$container->getDefinition($controllerServiceName)
|
||||
->addMethodCall('setCrudConfig', ['%chill_main_crud_config_'.$name.'%']);
|
||||
|
@@ -27,6 +27,7 @@ use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Chill\MainBundle\Form\Type\DataTransformer\ObjectToIdTransformer;
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
use Chill\MainBundle\Form\Type\Select2ChoiceType;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
|
||||
/**
|
||||
* Extends choice to allow adding select2 library on widget
|
||||
@@ -41,15 +42,26 @@ class Select2CountryType extends AbstractType
|
||||
*/
|
||||
private $requestStack;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var TranslatableStringHelper
|
||||
*/
|
||||
protected $translatableStringHelper;
|
||||
|
||||
/**
|
||||
* @var ObjectManager
|
||||
*/
|
||||
private $em;
|
||||
|
||||
public function __construct(RequestStack $requestStack,ObjectManager $em)
|
||||
public function __construct(
|
||||
RequestStack $requestStack,
|
||||
ObjectManager $em,
|
||||
TranslatableStringHelper $translatableStringHelper
|
||||
)
|
||||
{
|
||||
$this->requestStack = $requestStack;
|
||||
$this->em = $em;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
}
|
||||
|
||||
public function getBlockPrefix()
|
||||
@@ -75,7 +87,7 @@ class Select2CountryType extends AbstractType
|
||||
$choices = array();
|
||||
|
||||
foreach ($countries as $c) {
|
||||
$choices[$c->getId()] = $c->getName()[$locale];
|
||||
$choices[$c->getId()] = $this->translatableStringHelper->localize($c->getName());
|
||||
}
|
||||
|
||||
asort($choices, SORT_STRING | SORT_FLAG_CASE);
|
||||
|
@@ -27,6 +27,7 @@ use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Chill\MainBundle\Form\Type\DataTransformer\MultipleObjectsToIdTransformer;
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
use Chill\MainBundle\Form\Type\Select2ChoiceType;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
|
||||
/**
|
||||
* Extends choice to allow adding select2 library on widget for languages (multiple)
|
||||
@@ -43,10 +44,21 @@ class Select2LanguageType extends AbstractType
|
||||
*/
|
||||
private $em;
|
||||
|
||||
public function __construct(RequestStack $requestStack,ObjectManager $em)
|
||||
/**
|
||||
*
|
||||
* @var TranslatableStringHelper
|
||||
*/
|
||||
protected $translatableStringHelper;
|
||||
|
||||
public function __construct(
|
||||
RequestStack $requestStack,
|
||||
ObjectManager $em,
|
||||
TranslatableStringHelper $translatableStringHelper
|
||||
)
|
||||
{
|
||||
$this->requestStack = $requestStack;
|
||||
$this->em = $em;
|
||||
$this->translatableStringHelper = $translatableStringHelper;
|
||||
}
|
||||
|
||||
public function getBlockPrefix()
|
||||
@@ -72,7 +84,7 @@ class Select2LanguageType extends AbstractType
|
||||
$choices = array();
|
||||
|
||||
foreach ($languages as $l) {
|
||||
$choices[$l->getId()] = $l->getName()[$locale];
|
||||
$choices[$l->getId()] = $this->translatableStringHelper->localize($l->getName());
|
||||
}
|
||||
|
||||
asort($choices, SORT_STRING | SORT_FLAG_CASE);
|
||||
|
@@ -104,7 +104,7 @@ class Mailer
|
||||
* @param \User $to
|
||||
* @param array $subject Subject of the message [ 0 => $message (required), 1 => $parameters (optional), 3 => $domain (optional) ]
|
||||
* @param array $bodies The bodies. An array where keys are the contentType and values the bodies
|
||||
* @param \callable $callback a callback to customize the message (add attachment, etc.)
|
||||
* @param callable $callback a callback to customize the message (add attachment, etc.)
|
||||
*/
|
||||
public function sendNotification(
|
||||
$recipient,
|
||||
|
@@ -21,9 +21,7 @@ global.chill = chill;
|
||||
/*
|
||||
* load requirements in chill entrypoint
|
||||
*/
|
||||
|
||||
require('./sass/scratch.scss');
|
||||
|
||||
require('./scss/chillmain.scss');
|
||||
require('./css/chillmain.css');
|
||||
require('./css/pikaday.css');
|
||||
|
||||
@@ -37,10 +35,11 @@ require('./modules/download-report/index.js');
|
||||
require('./modules/select_interactive_loading/index.js');
|
||||
require('./modules/export-list/export-list.scss');
|
||||
require('./modules/entity/index.js');
|
||||
//require('./modules/tabs/index.js');
|
||||
|
||||
/*
|
||||
* load img
|
||||
*/
|
||||
require('./img/favicon.ico');
|
||||
require('./img/logo-chill-sans-slogan_white.png');
|
||||
require('./img/logo-chill-outil-accompagnement_white.png');
|
||||
require('./img/logo-chill-outil-accompagnement_white.png');
|
||||
|
@@ -0,0 +1,4 @@
|
||||
/*
|
||||
* These custom styles will override bootstrap enabled stylesheets
|
||||
*/
|
||||
|
47
src/Bundle/ChillMainBundle/Resources/public/modules/bootstrap/bootstrap.scss
vendored
Normal file
47
src/Bundle/ChillMainBundle/Resources/public/modules/bootstrap/bootstrap.scss
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Enable / disable bootstrap assets
|
||||
*/
|
||||
|
||||
@import "bootstrap/scss/functions";
|
||||
|
||||
/* replace variables */
|
||||
// @import "bootstrap/scss/variables";
|
||||
@import "custom/_variables";
|
||||
|
||||
@import "bootstrap/scss/mixins";
|
||||
// @import "bootstrap/scss/root";
|
||||
// @import "bootstrap/scss/reboot";
|
||||
// @import "bootstrap/scss/type";
|
||||
// @import "bootstrap/scss/images";
|
||||
// @import "bootstrap/scss/code";
|
||||
// @import "bootstrap/scss/grid";
|
||||
// @import "bootstrap/scss/tables";
|
||||
// @import "bootstrap/scss/forms";
|
||||
// @import "bootstrap/scss/buttons";
|
||||
// @import "bootstrap/scss/transitions";
|
||||
// @import "bootstrap/scss/dropdown";
|
||||
// @import "bootstrap/scss/button-group";
|
||||
// @import "bootstrap/scss/input-group";
|
||||
// @import "bootstrap/scss/custom-forms";
|
||||
// @import "bootstrap/scss/nav";
|
||||
// @import "bootstrap/scss/navbar";
|
||||
// @import "bootstrap/scss/card";
|
||||
// @import "bootstrap/scss/breadcrumb";
|
||||
// @import "bootstrap/scss/pagination";
|
||||
// @import "bootstrap/scss/badge";
|
||||
// @import "bootstrap/scss/jumbotron";
|
||||
// @import "bootstrap/scss/alert";
|
||||
// @import "bootstrap/scss/progress";
|
||||
// @import "bootstrap/scss/media";
|
||||
// @import "bootstrap/scss/list-group";
|
||||
// @import "bootstrap/scss/close";
|
||||
// @import "bootstrap/scss/toasts";
|
||||
@import "bootstrap/scss/modal";
|
||||
// @import "bootstrap/scss/tooltip";
|
||||
// @import "bootstrap/scss/popover";
|
||||
// @import "bootstrap/scss/carousel";
|
||||
// @import "bootstrap/scss/spinners";
|
||||
// @import "bootstrap/scss/utilities";
|
||||
// @import "bootstrap/scss/print";
|
||||
|
||||
@import "custom";
|
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* when bootstrap.css comes after chill.css
|
||||
* we have to disable conflict classes
|
||||
*/
|
||||
|
||||
@import "bootstrap/scss/functions";
|
||||
@import "bootstrap/scss/variables";
|
||||
@import "bootstrap/scss/mixins";
|
||||
@import "bootstrap/scss/root";
|
||||
//@import "bootstrap/scss/reboot"; // h1, h2, h3, ...
|
||||
//@import "bootstrap/scss/type"; // h1, h2, h3, ...
|
||||
@import "bootstrap/scss/images";
|
||||
@import "bootstrap/scss/code";
|
||||
//@import "bootstrap/scss/grid"; // container
|
||||
@import "bootstrap/scss/tables";
|
||||
@import "bootstrap/scss/forms";
|
||||
@import "bootstrap/scss/buttons";
|
||||
@import "bootstrap/scss/transitions";
|
||||
@import "bootstrap/scss/dropdown";
|
||||
@import "bootstrap/scss/button-group";
|
||||
@import "bootstrap/scss/input-group";
|
||||
@import "bootstrap/scss/custom-forms";
|
||||
@import "bootstrap/scss/nav";
|
||||
@import "bootstrap/scss/navbar";
|
||||
@import "bootstrap/scss/card";
|
||||
@import "bootstrap/scss/breadcrumb";
|
||||
@import "bootstrap/scss/pagination";
|
||||
@import "bootstrap/scss/badge";
|
||||
@import "bootstrap/scss/jumbotron";
|
||||
@import "bootstrap/scss/alert";
|
||||
@import "bootstrap/scss/progress";
|
||||
@import "bootstrap/scss/media";
|
||||
@import "bootstrap/scss/list-group";
|
||||
@import "bootstrap/scss/close";
|
||||
@import "bootstrap/scss/toasts";
|
||||
@import "bootstrap/scss/modal";
|
||||
@import "bootstrap/scss/tooltip";
|
||||
@import "bootstrap/scss/popover";
|
||||
@import "bootstrap/scss/carousel";
|
||||
@import "bootstrap/scss/spinners";
|
||||
@import "bootstrap/scss/utilities";
|
||||
@import "bootstrap/scss/print";
|
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,9 @@
|
||||
// Compile all bootstrap assets from nodes-modules
|
||||
//require('bootstrap/scss/bootstrap.scss')
|
||||
|
||||
// Compile custom styles to adapt bootstrap in chill context
|
||||
require('./custom.scss')
|
||||
// Or compile bootstrap only enabled assets
|
||||
require('./bootstrap.scss');
|
||||
|
||||
// You can specify which plugins you need
|
||||
//import { Tooltip, Toast, Popover } from 'bootstrap';
|
||||
//import Alert from 'bootstrap/js/dist/alert';
|
||||
import Modal from 'bootstrap/js/dist/modal';
|
||||
|
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* NOTE 2021.04
|
||||
* scss/chill.scss is the main sass file for the new chill.2
|
||||
* scratch will be replaced by bootstrap, please avoid to edit in modules/scratch/_custom.scss
|
||||
*/
|
||||
|
||||
// YOUR CUSTOM SCSS
|
||||
@import 'custom/config/colors';
|
||||
@import 'custom/config/variables';
|
||||
@@ -156,7 +162,6 @@ dl.chill_view_data {
|
||||
|
||||
}
|
||||
|
||||
|
||||
blockquote.chill-user-quote,
|
||||
div.chill-user-quote {
|
||||
border-left: 10px solid $chill-yellow;
|
||||
@@ -164,12 +169,12 @@ div.chill-user-quote {
|
||||
padding: 0.5em 10px;
|
||||
quotes: "\201C""\201D""\2018""\2019";
|
||||
background-color: $chill-llight-gray;
|
||||
|
||||
|
||||
blockquote {
|
||||
margin: 1.5em 10px;
|
||||
padding: 0.5em 10px;
|
||||
}
|
||||
|
||||
|
||||
blockquote:before {
|
||||
color: #ccc;
|
||||
content: open-quote;
|
||||
@@ -182,5 +187,4 @@ div.chill-user-quote {
|
||||
|
||||
.chill-no-data-statement {
|
||||
font-style: italic;
|
||||
|
||||
}
|
@@ -1,42 +1,42 @@
|
||||
@charset "UTF-8";
|
||||
|
||||
/// Outputs the spec and prefixed versions of the `::selection` pseudo-element.
|
||||
///
|
||||
/// @param {Bool} $current-selector [false]
|
||||
/// If set to `true`, it takes the current element into consideration.
|
||||
///
|
||||
/// @example scss - Usage
|
||||
/// .element {
|
||||
/// @include selection(true) {
|
||||
/// background-color: #ffbb52;
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// @example css - CSS Output
|
||||
/// .element::-moz-selection {
|
||||
/// background-color: #ffbb52;
|
||||
/// }
|
||||
///
|
||||
/// .element::selection {
|
||||
/// background-color: #ffbb52;
|
||||
/// }
|
||||
|
||||
@mixin selection($current-selector: false) {
|
||||
@if $current-selector {
|
||||
&::-moz-selection {
|
||||
@content;
|
||||
}
|
||||
|
||||
&::selection {
|
||||
@content;
|
||||
}
|
||||
} @else {
|
||||
::-moz-selection {
|
||||
@content;
|
||||
}
|
||||
|
||||
::selection {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
}
|
||||
@charset "UTF-8";
|
||||
|
||||
/// Outputs the spec and prefixed versions of the `::selection` pseudo-element.
|
||||
///
|
||||
/// @param {Bool} $current-selector [false]
|
||||
/// If set to `true`, it takes the current element into consideration.
|
||||
///
|
||||
/// @example scss - Usage
|
||||
/// .element {
|
||||
/// @include selection(true) {
|
||||
/// background-color: #ffbb52;
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// @example css - CSS Output
|
||||
/// .element::-moz-selection {
|
||||
/// background-color: #ffbb52;
|
||||
/// }
|
||||
///
|
||||
/// .element::selection {
|
||||
/// background-color: #ffbb52;
|
||||
/// }
|
||||
|
||||
@mixin selection($current-selector: false) {
|
||||
@if $current-selector {
|
||||
&::-moz-selection {
|
||||
@content;
|
||||
}
|
||||
|
||||
&::selection {
|
||||
@content;
|
||||
}
|
||||
} @else {
|
||||
::-moz-selection {
|
||||
@content;
|
||||
}
|
||||
|
||||
::selection {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user