modif comtabilite

This commit is contained in:
2019-01-16 16:23:31 +01:00
parent 6b741a9666
commit 9312edc3ae
1844 changed files with 158848 additions and 55874 deletions
+29
View File
@@ -0,0 +1,29 @@
email: anissa.bensalah@gmail.com
fullname: 'Anissa bensalah'
title: Modérator
state: enabled
access:
admin:
super: 'false'
login: 'true'
cache: 'false'
configuration: 'false'
configuration_system: 'false'
configuration_site: 'false'
configuration_media: 'false'
configuration_info: 'false'
settings: 'false'
pages: 'true'
maintenance: 'false'
statistics: 'true'
plugins: 'false'
themes: 'false'
users: 'false'
site:
login: 'true'
hashed_password: $2y$10$oKsKkddquAgbF6hQnYSP1.8MGJQOewwBoYWrNpTKnpZTXGZjcSNgu
groups:
- moderator
language: en
twofa_enabled: false
twofa_secret: FILKLI3TRO3LDFLG4DUATUQVJPZFZ4ID
+11
View File
@@ -0,0 +1,11 @@
moderator:
groupname: moderator
readableName: Modérator
description: 'The group of Modérator'
icon: child
access:
admin:
login: true
cache: false
site:
login: true
+4 -4
View File
@@ -1,19 +1,19 @@
enabled: true
built_in_css: true
add_editor_button: true
add_editor_button: false
player_parameters:
autoplay: 0
cc_load_policy: 0
cc_load_policy: 1
color: red
controls: 1
disablekb: 0
enablejsapi: 0
fs: 1
iv_load_policy: 1
iv_load_policy: 3
loop: 0
modestbranding: 0
playsinline: 0
rel: 1
rel: 0
showinfo: 0
vq: default
privacy_enhanced_mode: false
+5 -14
View File
@@ -1,12 +1,9 @@
absolute_urls: false
timezone: ''
default_locale: null
param_sep: ':'
wrapped_site: false
reverse_proxy_setup: false
force_ssl: false
force_lowercase_urls: true
custom_base_url: ''
username_regex: '^[a-z0-9_-]{3,16}$'
pwd_regex: '(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}'
intl_enabled: true
@@ -32,7 +29,6 @@ pages:
list:
count: 20
dateformat:
default: null
short: 'jS M Y'
long: 'F jS \a\t g:ia'
publish_dates: true
@@ -60,9 +56,7 @@ pages:
- json
- rss
- atom
append_url_extension: ''
expires: 604800
cache_control: null
last_modified: false
etag: false
vary_accept_encoding: false
@@ -92,8 +86,6 @@ cache:
lifetime: 604800
gzip: false
allow_webserver_gzip: false
redis:
socket: false
twig:
cache: false
debug: true
@@ -117,7 +109,7 @@ assets:
collections:
jquery: 'system://assets/jquery/jquery-2.x.min.js'
errors:
display: '1'
display: 1
log: true
debugger:
enabled: false
@@ -126,14 +118,12 @@ debugger:
twig: true
images:
default_image_quality: 85
cache_all: false
cache_all: true
cache_perms: '0755'
debug: false
auto_fix_orientation: false
media:
enable_media_timestamp: false
unsupported_inline_types: { }
allowed_fallback_types: { }
auto_metadata_exif: false
upload_limit: 2097152
session:
@@ -144,10 +134,11 @@ session:
secure: false
httponly: true
split: true
path: null
gpm:
releases: stable
proxy_url: null
method: auto
verify_peer: true
official_gpm_only: true
strict_mode:
yaml_compat: true
twig_compat: true
@@ -0,0 +1,6 @@
---
title: fullvideo
image_align: left
---
[plugin:youtube](https://www.youtube.com/watch?v=OrspkA-bp3Q)
@@ -0,0 +1,6 @@
---
title: fullvideo
image_align: left
---
[plugin:youtube](https://www.youtube.com/watch?v=OrspkA-bp3Q)
@@ -0,0 +1,6 @@
---
title: fullvideo
image_align: left
---
[plugin:youtube](https://www.youtube.com/watch?v=OrspkA-bp3Q)
@@ -1,5 +0,0 @@
---
title: fullvideo
---
[plugin:youtube](https://www.youtube.com/watch?v=atTcasPy3JY)
@@ -0,0 +1,9 @@
---
title: sovaj
image_align: left
taxonomy:
tag:
- sovaj
---
[plugin:youtube](https://www.youtube.com/watch?v=Fbz00uIc_pU)
@@ -6,4 +6,4 @@ taxonomy:
- sovaj
---
[plugin:youtube](https://www.youtube.com/watch?v=atTcasPy3JY)
[plugin:youtube](https://www.youtube.com/watch?v=Fbz00uIc_pU)
@@ -0,0 +1,9 @@
---
title: sovaj
image_align: left
taxonomy:
tag:
- sovaj
---
[plugin:youtube](https://www.youtube.com/watch?v=Fbz00uIc_pU)
@@ -1,9 +0,0 @@
---
title: matriz
image_align: left
taxonomy:
tag:
- matriz
---
[plugin:youtube](https://www.youtube.com/watch?v=atTcasPy3JY)
@@ -0,0 +1,7 @@
---
title: VIDEOS
image_align: left
content:
items:
- '@self.modular'
---
@@ -0,0 +1,8 @@
---
title: VÍDEOS
image_align: left
content:
items:
- '@self.modular'
---
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 MiB

@@ -4,4 +4,4 @@ media_order: 'IMG_2636.JPG,IMG_2704.JPG,IMG_2706.JPG,IMG_2707.JPG,IMG_2730.JPG'
image_align: left
---
![](IMG_2636.JPG)![](IMG_2704.JPG)![](IMG_2706.JPG)![](IMG_2707.JPG)![](IMG_2730.JPG)
![](ANISSA-4.jpg)![](ANISSA-7.jpg)![](JuSana4.jpg)![](ANISSA-10.jpg)![](IMG_9373-10.jpg)
@@ -0,0 +1,7 @@
---
title: Sovaj
media_order: 'ANISSA-4.jpg,ANISSA-7.jpg,JuSana4.jpg,Anissa marge et Logo-6.jpg,Sans titre-2.jpg,ANISSA-10.jpg,IMG_9373-10.jpg'
image_align: left
---
![](ANISSA-4.jpg)![](ANISSA-7.jpg)![](JuSana4.jpg)![](ANISSA-10.jpg)![](IMG_9373-10.jpg)
@@ -0,0 +1,7 @@
---
title: Sovaj
media_order: 'IMG_2636.JPG,IMG_2704.JPG,IMG_2706.JPG,IMG_2707.JPG,IMG_2730.JPG'
image_align: left
---
![](ANISSA-4.jpg)![](ANISSA-7.jpg)![](JuSana4.jpg)![](ANISSA-10.jpg)![](IMG_9373-10.jpg)
@@ -0,0 +1,8 @@
---
title: Gallery
image_align: left
content:
items:
- '@self.modular'
---
@@ -0,0 +1,8 @@
---
title: FOTOS
image_align: left
content:
items:
- '@self.modular'
---
@@ -0,0 +1,22 @@
---
title: sovaj
image_align: left
taxonomy:
tag:
- sovaj
---
French, Algerian and Brazilian Artist, Anissá Bensalah sings on stage since her 15 years old : Sesc, Itamaratí, Palacio de Cristal, Candelária (Rio de Janeiro, Brazil), Teatro Nacional (Brasilia, Brazil), Salle Ibn Zeydoun (Algiers), Salle Gaveau, New Morning, Institut du Monde Arabe, festival Solidays, Théâtre des Gémeaux, Centre Culturel Goulbenkian, Maroquinerie, Sunside-Sunset, Petit Bain, Studio de l'Ermitage, Salle Gustave Eiffel (Paris, France)... Dock 40 (Lyon, France), Théâtre de la Mer (Sainte Maxime, France), Festival Jazz au Château (Cagnes sur Mer, France), Festival La Rue des Artistes (Saint Chamont)...
Born in Haïti, Anissá Bensalah moved at the age of five, with her parents, to Dakar (Senegal) where she immersed herself in the local culture. At the age of 11, she moved to Beirut (Lebanon) where she discovered the damage of war.
In 1998, she moved to Paris and studied musicology at the Paris VIII University. She also pursued  ten years of classical vocal technic at the National School of Music with Odile Pietti, Florence Katz and Mercedes Proteau, and she studied jazz and brazilian music with Laurent Coq (french pianist jazz), Mônica Passos (brazilian singer) in Paris, Diana Goulart and the famous Amelia Rabello (brazilian singers) in Rio de Janeiro.
Anissá Bensalah has worked and collaborated with some important figures of brazilian music : The pianist Jõao Donato who gave her a new composition _Equanto a gente namora_, the guitarist Nelson Faria, the pianist and composer Rodrigo Zaidan, the bassist Josias Pedrosa and the drumer Lucio Vieira with whom she recorded a 4 tracks extended play, _Eu sei e você sabe._
In october 2013, she has released her first original album _Matriz_, produced by Ovastand (France) and distributed by Musicast. This album is played by french instrumentists : Automne Lajeat (cello), Frédéric Antetomaso (guitars), Julien Matrot (trumpet), Gui Duvignau (double bass) et Nils Wekstein (drums & percussions).
In April 2014, she has a landmark encounter with Remy Kolpa Kopoul (aslo known as RKK), a key journalist-"connector" of the French music industry. He will give her all his trust and attention, opening many doors for her.
In 2015, she recorded a 4 tracks extended play, _Nejma_, with Amina Mezaache (flute), Frédéric Antetomaso (guitars), Ricardo Feijão (acoustic bass), Nils Wekstein and Jonathan Edo (drums & percussions).
Currently, Anissá Bensalah presents her second album _Sovaj_ (produced by Ovastand) in France and other countries. She sings on stage with Tim Campanella (drums), Philippe Monge (bass & keyboards) and Frédéric Antetomaso (guitars).
@@ -12,12 +12,12 @@ Née en Haïti, Anissá Bensalah part à 5 ans, avec ses parents, vivre à Dakar
Ce n'est qu'à ses 14 ans qu'elle arrive à Paris. Parallèlement à un cursus de musicologie à l'Université Paris VIII, elle y suit dix ans de formation en École Nationale de Musique auprès d'Odile Pietti, Florence Katz et Mercedes Proteau (classique), ainsi que Laurent Coq, Mônica Passos, Diana Goulart et Amélia Rabello (jazz et musiques brésiliennes).
Elle a travaillé et collaboré avec des noms de la musique brésilienne : João Donato qui lui a confié une composition inédite Enquanto a gente namora, Nelson Faria, Rodrigo Zaidan, Josias Pedrosa et Lucio Vieira avec qui elle a enregistré un EP 4 titres auto-produit Eu sei e você sabe.
Elle a travaillé et collaboré avec des noms de la musique brésilienne : João Donato qui lui a confié une composition inédite _Enquanto a gente namora_, Nelson Faria, Rodrigo Zaidan, Josias Pedrosa et Lucio Vieira avec qui elle a enregistré un EP 4 titres auto-produit _Eu sei e você sabe_.
En octobre 2013, elle sort son premier album Matriz, sous le label Ovastand et distribué par Musicast. Elle y est entourée de musiciens variées comme Automne Lajeat (violoncelle), Frédéric Antetomaso (guitare), Julien Matrot (trompette), Guillaume Duvignau (contrebasse) et Nils Wekstein (batterie-percussions).
En octobre 2013, elle sort son premier album _Matriz_, sous le label Ovastand et distribué par Musicast. Elle y est entourée de musiciens variées comme Automne Lajeat (violoncelle), Frédéric Antetomaso (guitare), Julien Matrot (trompette), Gui Duvignau (contrebasse) et Nils Wekstein (batterie-percussions).
En avril 2014, elle fait une rencontre professionnelle marquante : Rémy Kolpa Kopoul (surnommé RKK), journaliste-"connexionneur" incontrounable dans le milieu musical en France. Il lui accordera sa confiance et son écoute, et lui ouvrira plusieurs portes.
Fin 2015, elle enregistre un EP 4 titres, intitulé Nejma, avec Amina Mezaache (flûtes), Frédéric Antetomaso (guitares), Ricardo Feijão (basse acoustique), Nils Wekstein et Jonathan Edo (batterie-percussions).
Fin 2015, elle enregistre un EP 4 titres, intitulé _Nejma_, avec Amina Mezaache (flûtes), Frédéric Antetomaso (guitares), Ricardo Feijão (basse acoustique), Nils Wekstein et Jonathan Edo (batterie-percussions).
Actuellement, Anissá Bensalah présente sur scène son deuxième album Sovaj (signé chez Ovastand), en France et à l'étranger. Elle est accompagnée par Tim Campanella (batterie), Philippe Monge (synthé basse et claviers) et Frédéric Antetomaso (guitares).
Actuellement, Anissá Bensalah présente sur scène son deuxième album _Sovaj_ (signé chez Ovastand), en France et à l'étranger. Elle est accompagnée par Tim Campanella (batterie), Philippe Monge (synthé basse et claviers) et Frédéric Antetomaso (guitares).
@@ -0,0 +1,22 @@
---
title: sovaj
image_align: left
taxonomy:
tag:
- sovaj
---
Artista franco-argelina e brasileira, Anissá Bensalah se apresenta no palco como cantora desde seus 15 anos : Sala Ibn Zeydoun (Argélia), SESC (RJ), Palácio de Cristal (Petrópolis), Igreja da Candelária (RJ), Teatro Municipal de Cabo Frio, Salão Nobre do Itamaratí (RJ), Hotel Gloria (RJ), Sala Villa Lobos do Teatro Nacional (Brasilia), Salle Gaveau (Paris), New Morning (Paris), festival nacional francês Solidays (Paris-Vincennes), Institut du monde Arabe (Paris), Maroquinerie (Paris), Sunside-Sunset (Paris), Petit Bain (Paris), Studio de l'Ermitage (Paris), Salle Gustave Eiffel (Paris), Théâtre de la Mer (Sainte Maxime), Festival Jazz au Château (Cagnes sur Mer), Dock 40 (Lyon), Festival La Rue des Artistes (Saint Chamont), etc.
Nascida no Haïti, Anissá se despede de sua terra natal aos 5 anos de idade para viver em Dakar (Senegal), onde se impregna do rítmo e da cultura locais. Aos 11 anos, muda-se para o Líbano onde descobre uma terra devastada pela guerra. Com 14 anos, se estabelece em Paris. Paralelamente aos seus diplomas de musicologia na Universidade Paris VIII, segue durante 10 anos uma formação de canto no Conservatório Nacional de Música em Paris com Odile Pietti, Florence Katz e Mercedes Proteau (formação em canto lírico) e Laurent Cocq, Mônica Passos, Diana Goulart e Amélia Rabello (formação jazz, MPB e samba).
Entre 2008 e 2009, encontra com João Donato que lhe confia uma de suas composiçãos inéditas com letra de Thelma de Freitas, _Enquanto a gente namora_.
Com a colaboração de Diana Goulart (direção artística) e Nelson Faria (guitarra e arranjos), grava um EP com 4 músicas, _Eu sei e você sabe_, com a participação de Rodrigo Zaidan, Josías Pedrosa e Lucio Vieira.
Em outubro 2013, sai _Matriz_, o primeiro album de Anissá, lançado pela Ovastand (França) e distribuido pela Musicast. Ela é acompanhada por vários instrumentistas franceses : Automne Lajeat (cello), Frédéric Antetomaso (violao), Julien Matrot (trompete), Gui Duvignau (contrabaixo) et Nils Wekstein (bateria e percussões).
Em abril 2014, encountrou com o famoso jornalista francês e grande amador de música : Remy Kolpa Kopoul (conhecido como "RKK"). Conquistado pela música de Anissá, ele lhe abri varias portas no setor musical francês.
No final do ano 2015, grava um EP com 4 músicas, _Nejma_, junto com Amina Mezaache (flautas), Frédéric Antetomaso (violão), Ricardo Feijão (baixo acústico), Nils Wekstein e Jonathan Edo (bateria e percussões).
Atualmente, Anissá Bensalah apresenta, no palco, seu segundo disco _Sovaj_ (lançado pela Ovastand), na França e no exterior. É acompanhada por Tim Campanella (bateria), Philippe Monge (baixo e keyboard) e Frédéric Antetomaso (guitarra).
@@ -0,0 +1,7 @@
---
title: ABOUT
image_align: left
content:
items:
- '@self.modular'
---
@@ -5,3 +5,4 @@ content:
items:
- '@self.modular'
---
@@ -0,0 +1,8 @@
---
title: BIOGRAFÍA
image_align: left
content:
items:
- '@self.modular'
---
Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 475 KiB

@@ -1,4 +0,0 @@
---
title: Matriz
image_align: left
---
Binary file not shown.

After

Width:  |  Height:  |  Size: 534 KiB

@@ -0,0 +1,49 @@
---
title: sovaj
media_order: 'jazzradio-logo-copie_0_0_0.jpg,150px-montecarlodoualiya_2013.svg_0_0.png,fr2.jpg,frinter.jpg,logo_france_info_1_0.png,logo-lesinrocks_0.jpg,photohome.JPG'
image_align: left
taxonomy:
tag:
- white
---
![](cover_Matriz.jpg)
October 2013 - label Ovastand, distribution Musicast (France)
![](jazzradio-logo-copie_0_0_0.jpg)
live sur France 2
voir la vidéo - Lire l'article
Présentée par Thierry Beccaro et Myriam Serrat
Diffusée le 25-26 juillet 2014 à 00h
![](150px-montecarlodoualiya_2013.svg_0_0.png)
live sur France 2
voir la vidéo - Lire l'article
Présentée par Thierry Beccaro et Myriam Serrat
Diffusée le 25-26 juillet 2014 à 00h
![](fr2.jpg)
live sur France 2
voir la vidéo - Lire l'article
Présentée par Thierry Beccaro et Myriam Serrat
Diffusée le 25-26 juillet 2014 à 00h
![](frinter.jpg)
live sur France 2
voir la vidéo - Lire l'article
Présentée par Thierry Beccaro et Myriam Serrat
Diffusée le 25-26 juillet 2014 à 00h
![](logo_france_info_1_0.png)
live sur France 2
voir la vidéo - Lire l'article
Présentée par Thierry Beccaro et Myriam Serrat
Diffusée le 25-26 juillet 2014 à 00h
![](logo-lesinrocks_0.jpg)
live sur France 2
voir la vidéo - Lire l'article
Présentée par Thierry Beccaro et Myriam Serrat
Diffusée le 25-26 juillet 2014 à 00h
@@ -1,15 +1,16 @@
---
title: sovaj
media_order: 'jazzradio-logo-copie_0_0_0.jpg,150px-montecarlodoualiya_2013.svg_0_0.png,fr2.jpg,frinter.jpg,logo_france_info_1_0.png,logo-lesinrocks_0.jpg,photohome.JPG'
media_order: 'jazzradio-logo-copie_0_0_0.jpg,150px-montecarlodoualiya_2013.svg_0_0.png,fr2.jpg,frinter.jpg,logo_france_info_1_0.png,logo-lesinrocks_0.jpg,photohome.JPG,cover_Matriz.jpg'
image_align: left
taxonomy:
tag:
- white
---
![](photohome.JPG)
![](cover_Matriz.jpg)
octobre 2013 - label Ovastand, distribution Musicast
![](jazzradio-logo-copie_0_0_0.jpg)
![](fr2.jpg)
live sur France 2
voir la vidéo - Lire l'article
Présentée par Thierry Beccaro et Myriam Serrat
@@ -21,19 +22,12 @@ voir la vidéo - Lire l'article
Présentée par Thierry Beccaro et Myriam Serrat
Diffusée le 25-26 juillet 2014 à 00h
![](fr2.jpg)
live sur France 2
voir la vidéo - Lire l'article
Présentée par Thierry Beccaro et Myriam Serrat
Diffusée le 25-26 juillet 2014 à 00h
![](frinter.jpg)
live sur France 2
voir la vidéo - Lire l'article
Présentée par Thierry Beccaro et Myriam Serrat
Diffusée le 25-26 juillet 2014 à 00h
![](logo_france_info_1_0.png)
live sur France 2
voir la vidéo - Lire l'article
@@ -46,3 +40,9 @@ voir la vidéo - Lire l'article
Présentée par Thierry Beccaro et Myriam Serrat
Diffusée le 25-26 juillet 2014 à 00h
![](jazzradio-logo-copie_0_0_0.jpg)
live sur France 2
voir la vidéo - Lire l'article
Présentée par Thierry Beccaro et Myriam Serrat
Diffusée le 25-26 juillet 2014 à 00h
@@ -0,0 +1,49 @@
---
title: sovaj
media_order: 'jazzradio-logo-copie_0_0_0.jpg,150px-montecarlodoualiya_2013.svg_0_0.png,fr2.jpg,frinter.jpg,logo_france_info_1_0.png,logo-lesinrocks_0.jpg,photohome.JPG'
image_align: left
taxonomy:
tag:
- white
---
![](cover_Matriz.jpg)
Outubro 2013 - gravadora Ovastand, distribuidora Musicast (França)
![](jazzradio-logo-copie_0_0_0.jpg)
live sur France 2
voir la vidéo - Lire l'article
Présentée par Thierry Beccaro et Myriam Serrat
Diffusée le 25-26 juillet 2014 à 00h
![](150px-montecarlodoualiya_2013.svg_0_0.png)
live sur France 2
voir la vidéo - Lire l'article
Présentée par Thierry Beccaro et Myriam Serrat
Diffusée le 25-26 juillet 2014 à 00h
![](fr2.jpg)
live sur France 2
voir la vidéo - Lire l'article
Présentée par Thierry Beccaro et Myriam Serrat
Diffusée le 25-26 juillet 2014 à 00h
![](frinter.jpg)
live sur France 2
voir la vidéo - Lire l'article
Présentée par Thierry Beccaro et Myriam Serrat
Diffusée le 25-26 juillet 2014 à 00h
![](logo_france_info_1_0.png)
live sur France 2
voir la vidéo - Lire l'article
Présentée par Thierry Beccaro et Myriam Serrat
Diffusée le 25-26 juillet 2014 à 00h
![](logo-lesinrocks_0.jpg)
live sur France 2
voir la vidéo - Lire l'article
Présentée par Thierry Beccaro et Myriam Serrat
Diffusée le 25-26 juillet 2014 à 00h
@@ -0,0 +1,16 @@
---
title: Press
image_align: left
content:
items:
- '@self.modular'
---
_"Son timbre de voix chatoyant lui permet de passer avec un charme fou du portugais à
larabe. Avec son premier album « Matriz », Anissá Bensalah dresse un panorama afro
brésilien bien à elle."_
Remy Kolpa Kopoul, Radio Nova
_"Il nest pas donné à tout le monde davoir une voix qui évoque une présence « physique » à
la simple écoute dun disque. Anissá est de ces artistes là !"_
Nadia Khouri Dhager, Babelmed.net
@@ -6,7 +6,11 @@ content:
- '@self.modular'
---
« Elle chante le forró avec un cœur vaudou,
arrange une chanson arabe façon bossa
et invente le jazz andalou au pays du candomblé »
Anne Berthod, Télérama
_"Son timbre de voix chatoyant lui permet de passer avec un charme fou du portugais à
larabe. Avec son premier album « Matriz », Anissá Bensalah dresse un panorama afro
brésilien bien à elle."_
Remy Kolpa Kopoul, Radio Nova
_"Il nest pas donné à tout le monde davoir une voix qui évoque une présence « physique » à
la simple écoute dun disque. Anissá est de ces artistes là !"_
Nadia Khouri Dhager, Babelmed.net
@@ -0,0 +1,14 @@
---
title: IMPRENSA
image_align: left
content:
items:
- '@self.modular'
---
_«Seu timbre de voz vibrante e feiticeiro permite que ela transite, com um charme indescritivel, do português ao arabe. Com seu primeiro album " Matriz ", Anissá Bensalah nos faz descobrir o universo afro-brasileiro que é o seu.»_
Remy Kolpa Kopoul, Radio Nova (Paris, França)
_«Nem todo mundo é dotado de uma voz que evoca uma presença "fisica", através da simples escuta de um disco. Anissá é uma dessas artistas.»_
Nadia Khouri Dhager, Babelmed.net (Marseille, França)
@@ -0,0 +1,12 @@
---
title: sovaj
image_align: left
---
02/01/2019 - [Le Grillen](http://www.grillen.fr/) - Colmar (FR)
04/03/2019 - [Café de la Danse](http://www.cafedeladanse.com/) - Paris (FR)
@@ -3,15 +3,9 @@ title: sovaj
image_align: left
---
**Date de l'album sovaj**
**Vendredi 01 Février 2019**
**[Le Grillen](http://www.grillen.fr/)**
**Colmar (FR)**
03/04/2019 - [Café de la Danse](http://www.cafedeladanse.com/) - Paris (FR)
date(j/m/a) - nom de la salle - ville/pays - [billets](billets)
date(j/m/a) - nom de la salle - ville/pays - [billets](billets)
date(j/m/a) - nom de la salle - ville/pays - [billets](billets)
date(j/m/a) - nom de la salle - ville/pays - [billets](billets)
date(j/m/a) - nom de la salle - ville/pays - [billets](billets)
date(j/m/a) - nom de la salle - ville/pays - [billets](billets)
date(j/m/a) - nom de la salle - ville/pays - [billets](billets)
date(j/m/a) - nom de la salle - ville/pays - [billets](billets)
date(j/m/a) - nom de la salle - ville/pays - [billets](billets)
date(j/m/a) - nom de la salle - ville/pays - [billets](billets)
@@ -0,0 +1,12 @@
---
title: sovaj
image_align: left
---
01/02/2019 - [Le Grillen](http://www.grillen.fr/) - Colmar (FR)
03/04/2019 - [Café de la Danse](http://www.cafedeladanse.com/) - Paris (FR)
@@ -0,0 +1,8 @@
---
title: 'Tour dates'
image_align: left
content:
items:
- '@self.modular'
---
@@ -1,7 +1,8 @@
---
title: Prochaines dates
title: 'Prochaines dates'
image_align: left
content:
items:
- '@self.modular'
---
@@ -0,0 +1,8 @@
---
title: AGENDA
image_align: left
content:
items:
- '@self.modular'
---
+22
View File
@@ -0,0 +1,22 @@
---
title: Contact
image_align: left
---
**BOOKING**
Ovastand
Frederic
19, rue Edgar Quinet 92120 Montrouge (France)
[fred@ovastand.com](mailo:fred@ovastand.com)
+33 (0) 6 31 02 30 53
+33 (0) 9 86 41 51 44
**PRESS ATTACHÉ**
Accent Presse
Simon Vessyère
8, rue André Messager 75018 Paris (France)
[simon@accent-presse.com](mailo:simon@accent-presse.com)
+33 (0) 6 70 21 32 83
+33 (0) 1 42 57 92 84
+7 -5
View File
@@ -5,16 +5,18 @@ image_align: left
**BOOKING**
Ovastand
Frédéric Ovastand
Frédéric
19, rue Edgar Quinet 92120 Montrouge (France)
fred@ovastand.com
[fred@ovastand.com](mailto:fred@ovastand.com)
+33 (0) 6 31 02 30 53
+33 (0) 9 86 41 51 44
**ATTACHÉ DE PRESSE**
Accent Presse
Simon Vessyère
8, rue André Messager 75018 Paris (France)
simon@accent-presse.com
+33 (0) 1 42 57 92 84
+33 (0) 6 70 21 32 83
[simon@accent-presse.com](mailo:simon@accent-presse.com)
+33 (0) 6 70 21 32 83
+33 (0) 1 42 57 92 84
+24
View File
@@ -0,0 +1,24 @@
---
title: CONTATO
image_align: left
---
**CONTRATAÇÃO DE SHOWS**
Ovastand (nom cliquabe qui nous renvoie à www.ovastand.net)
Frederic
19, rue Edgar Quinet 92120 Montrouge (França)
[fred@ovastand.com](mailo:fred@ovastand.com)
+33 (0) 6 31 02 30 53
+33 (0) 9 86 41 51 44
**ASSESSORIA DE IMPRENSA**
Accent Presse (nom cliquabe qui nous renvoie à www.accent-presse.com)
Simon Vessyère
8, rue André Messager 75018 Paris (França)
[simon@accent-presse.com](mailo:simon@accent-presse.com)
+33 (0) 6 70 21 32 83
+33 (0) 1 42 57 92 84
+15
View File
@@ -0,0 +1,15 @@
---
title: Home
media_order: 'photohome.JPG,fb.svg,yt.svg'
content:
items: '@self.modular'
body_classes: 'title-center title-h1h2'
---
#sovaj
New album SOVAJ
Available in march 2019
[itune](itune) - [fnac](fnac) - [amazon](fnac)
[deezer](fnac) - [spotify](fnac)
[![](fb.svg)](www.facebook.com) [![](yt.svg)](www.youtube.com)
+3 -4
View File
@@ -2,15 +2,14 @@
title: Home
media_order: 'photohome.JPG,fb.svg,yt.svg'
content:
items:
- '@self.children'
- '@self.modular'
items: '@self.modular'
body_classes: 'title-center title-h1h2'
---
#sovaj
album sovaj est disponible sur :
Nouvel album SOVAJ
Disponible en mars 2019
[itune](itune) - [fnac](fnac) - [amazon](fnac)
[deezer](fnac) - [spotify](fnac)
[![](fb.svg)](www.facebook.com) [![](yt.svg)](www.youtube.com)
+15
View File
@@ -0,0 +1,15 @@
---
title: Home
media_order: 'photohome.JPG,fb.svg,yt.svg'
content:
items: '@self.children, @self.modular'
body_classes: 'title-center title-h1h2'
---
#sovaj
Novo álbum SOVAJ
Disponível em março 2019
[itune](itune) - [fnac](fnac) - [amazon](fnac)
[deezer](fnac) - [spotify](fnac)
[![](fb.svg)]([www.facebook.com](www.facebook.com/anissabensalah.officiel/)) [![](yt.svg)]([www.youtube.com](www.youtube.com/watch?v=OrspkA-bp3Q))
Binary file not shown.

After

Width:  |  Height:  |  Size: 284 KiB

+18
View File
@@ -0,0 +1,18 @@
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 4
# 2 space indentation
[*.yaml, *.yml]
indent_style = space
indent_size = 2
+14
View File
@@ -0,0 +1,14 @@
themes/grav/.sass-cache
.DS_Store
# Node Modules
**/node_modules/**
themes/grav/js/admin.js
themes/grav/js/vendor.js
themes/grav/js/*.map
.idea
tests/_output/*
tests/_support/_generated/*
tests/cache/*
tests/error.log
+140 -7
View File
@@ -1,3 +1,136 @@
# v1.8.14
## 11/12/2018
1. [](#bugfix)
* Fixed Grav core update potentially spinning forever because of an error which happens after a successful upgrade
* Saving in expert mode can cause `undefined index: header` error [#1537](https://github.com/getgrav/grav-plugin-admin/issues/1537)
# v1.8.13
## 11/05/2018
1. [](#new)
* Added new `|nested()` Twig filter to access array objects with dot notation syntax
1. [](#bugfix)
* Fixed issue with complex lists structure and nested dot-notation [admin#2236](https://github.com/getgrav/grav/issues/2236)
# v1.8.12
## 10/24/2018
1. [](#improved)
* Updated various lang strings
* Removed duplicate lang strings
1. [](#bugfix)
* Fix XSS checking when empty content [#1533](https://github.com/getgrav/grav-plugin-admin/issues/1533)
* Fix DirectInstall not working [#1535](https://github.com/getgrav/grav-plugin-admin/issues/1535)
# v1.8.11
## 10/08/2018
1. [](#improved)
* Change usage of basename where possible [#1480](https://github.com/getgrav/grav-plugin-admin/pull/1480)
* Improved filename validation (requires Grav 1.5.3)
* Updated various lang codes
1. [](#bugfix)
* File Uploads: Do not trust mimetype sent by the browser
* Fixed file extension detection
* Fix for HTML entities in page slug [#1524](https://github.com/getgrav/grav-plugin-admin/issues/1524)
* Fix for port in backup download links [#1521](https://github.com/getgrav/grav-plugin-admin/issues/1521)
# v1.8.10
## 10/01/2018
1. [](#new)
* IMPORTANT: Non `admin.super` users are now subject to XSS validation in Page content. Configurable via Configuration / Security
* New XSS content warnings and integration into page save
* Added new event `onAdminPage()` which allows plugins to customize `Page` object in `$event['page']`
1. [](#improved)
* Use `Url:post()` to get the `$_POST` variable (allows common security checks/filtering for the POST data)
* Requires Grav 1.5.2
1. [](#bugfix)
* Fixed redirect to correct URL after failed login
* Fixed issue in `filepicker` where missing images would cause a loop to try to load them
* Twig 2 compatibility fixes for macros
* Updated `composer.json` to better match Grav 1.5
* Remove `package-lock.json` as it was referencing an insecure JS package
# v1.8.9
## 08/23/2018
1. [](#improved)
* Make order field to use context, not data
* Switched to new Grav Yaml class to support Native + Fallback YAML libraries
* Minor fix for `file` thumbnails display
* Requires Grav 1.5.1
# v1.8.8
## 08/17/2018
1. [](#improved)
* Support URI Params and Query attributes in Login redirect
* Added support for textarea value type in `array` field
* Added some new lang strings for Grav 1.5.0
1. [](#bugfix)
* Support params and querystring in login redirect
* Added field name nesting with tab field
# v1.8.7
## 07/31/2018
1. [](#bugfix)
* Fix for deleting 'extra' media files [grav#2100](https://githubcom/getgrav/grav/issues/2100)
# v1.8.6
## 07/13/2018
1. [](#bugfix)
* Force `html` for markdown preview [grav#2066](https://github.com/getgrav/grav/issues/2066)
* Add missing `authorizeTask()` checks in controller [#1483](https://github.com/getgrav/grav/issues/1483)
* Add support for `force_ssl` to admin URLs [#1479](https://github.com/getgrav/grav-plugin-admin/issues/1479)
# v1.8.5
## 06/20/2018
1. [](#bugfix)
* Fixed broken folder attribute on filepicker [#1465](https://github.com/getgrav/grav-plugin-admin/issues/1465)
* Added translation for system.session.initialize
* Slight updates on new translation strings
# v1.8.4
## 06/11/2018
1. [](#improved)
* Including EXIF JS library in the modules dependencies to fix orientation when uploading images
1. [](#bugfix)
* Initialize session on setup [#1451](https://github.com/getgrav/grav-plugin-admin/issues/1451)
* Force a `null` order when empty in the post request
* Fixed some 2FA form styling issues
# v1.8.3
## 05/31/2018
1. [](#new)
* Added support for selectize plugins as options in the selectize field
1. [](#bugfix)
* Fixed deep linking in admin after login [#1456](https://github.com/getgrav/grav-plugin-admin/issues/1456)
* Fixed Undefined property: `stdClass::$image` in v1.8.2 [#1454](https://github.com/getgrav/grav-plugin-admin/issues/1454)
* Pass media order when calling `task:listmedia`
# v1.8.2
## 05/24/2018
1. [](#new)
* Added custom object support for filepicker field
* Don't allow saving of a user with no local account file
* Controls for `list` field were not in sync between top and bottom
1. [](#improved)
* More subtle `fieldset` styling
1. [](#bugfix)
* Check if `$object->blueprints()` exists in `onAdminAfterSave`
* When creating first user, check `admin.login` not `site.login`
* Fix admin login redirects for multisite setups
* Fixed issue with filepicker field where images wouldn't properly merge with the current value if in a page header
* Fixed media delete for streams
# v1.8.1
## 05/15/2018
@@ -18,7 +151,7 @@
1. [](#improved)
* Added support for JavaScript `bottom` block [#1425](https://github.com/getgrav/grav-plugin-admin/pull/1425)
* Added better typography styling for blockquote and markdown in `display` field
* Vendor updates
* Vendor updates
1. [](#bugfix)
* Added missing MarkdownExtra strings [#1385](https://github.com/getgrav/grav-plugin-admin/pull/1385)
* Updated `blueprints.yaml` with missing `step` attribute [#1415](https://github.com/getgrav/grav-plugin-admin/pull/1415)
@@ -46,7 +179,7 @@
* Implemented workaround for required text fields [#1390](https://github.com/getgrav/grav-plugin-admin/issues/1390)
* Fixed highlight color in Firefox [getgrav/grav#1949](https://github.com/getgrav/grav/issues/1949)
* Fix for bad redirect on saving simplesearch (possibly others)
# v1.7.2
## 03/21/2018
@@ -58,8 +191,8 @@
* Optimization to stop admin for looking for pages in disabled plugins
* Added configuration option to choose if you want to use new 'inline' preview or `new tab'
1. [](#bugfix)
* Fix redirect bug when changing admin route to `admin-*`
* Changed Twig `|count` to `|length` filter [#1391](https://github.com/getgrav/grav-plugin-admin/issues/1391)
* Fix redirect bug when changing admin route to `admin-*`
* Changed Twig `|count` to `|length` filter [#1391](https://github.com/getgrav/grav-plugin-admin/issues/1391)
* Fix for page preview when `HTTP_REFERRER` is not set [grav#1930](https://github.com/getgrav/grav/issues/1930)
# v1.7.1
@@ -94,7 +227,7 @@
* Fixed issue with default lang translation in admin [#1361](https://github.com/getgrav/grav-plugin-admin/issues/1361)
* Typos in `Tools` -> `Direct Install` page [#1345](https://github.com/getgrav/grav-plugin-admin/issues/1345)
* Fixed bug with frontmatter being killed when in `Expert Mode` [#1354](https://github.com/getgrav/grav-plugin-admin/issues/1354)
# v1.7.0-rc.3
## 02/15/2018
@@ -119,7 +252,7 @@
* Tidied Tools panel and added translations
* Tooltip and new icon for site preview
* Updated JS library dependencies
* Changed CodeMirror editor to use sans-serif font for readability
* Changed CodeMirror editor to use sans-serif font for readability
1. [](#bugfix)
* Fixed z-index issue in fullscreen mode [#1317](https://github.com/getgrav/grav-plugin-admin/issues/1317)
@@ -215,7 +348,7 @@
* Fixed fields not working in Microsoft Edge with Selectize.js [#1222](https://github.com/getgrav/grav-plugin-admin/pull/1222)
* Replaced a left-over "is empty" check [#1232](https://github.com/getgrav/grav-plugin-admin/pull/1232)
* Fixed headers drop-down in editor to align properly
# v1.6.0
## 09/07/2017
+31 -4
View File
@@ -144,6 +144,7 @@ class AdminPlugin extends Plugin
// Only activate admin if we're inside the admin path.
if ($this->isAdminPath()) {
$this->grav['session']->init();
$this->active = true;
// Set cache based on admin_cache option
@@ -249,7 +250,7 @@ class AdminPlugin extends Plugin
unset($this->grav['user']);
$this->grav['user'] = $user;
$user->authenticated = true;
$user->authorized = $user->authorize('site.login');
$user->authorized = $user->authorize('admin.login');
$messages = $this->grav['messages'];
$messages->add($this->grav['language']->translate('PLUGIN_ADMIN.LOGIN_LOGGED_IN'), 'info');
@@ -316,6 +317,16 @@ class AdminPlugin extends Plugin
*/
public function onPagesInitialized()
{
$config = $this->config;
// Force SSL with redirect if required
if ($config->get('system.force_ssl')) {
if (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] !== 'on') {
$url = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
$this->grav->redirect($url);
}
}
$this->session = $this->grav['session'];
// Set original route for the home page.
@@ -349,7 +360,10 @@ class AdminPlugin extends Plugin
}
// Make local copy of POST.
$post = !empty($_POST) ? $_POST : [];
$post = $this->grav['uri']->post();
// Initialize Page Types
Pages::types();
// Handle tasks.
$this->admin->task = $task = !empty($post['task']) ? $post['task'] : $this->uri->param('task');
@@ -373,7 +387,17 @@ class AdminPlugin extends Plugin
$page = new Page;
$page->expires(0);
// First look in the pages provided by the Admin plugin itself
if ($this->grav['user']->authorize('admin.login')) {
$event = new Event(['page' => $page]);
$event = $this->grav->fireEvent('onAdminPage', $event);
$page = $event['page'];
if ($page->slug()) {
return $page;
}
}
// Look in the pages provided by the Admin plugin itself
if (file_exists(__DIR__ . "/pages/admin/{$self->template}.md")) {
$page->init(new \SplFileInfo(__DIR__ . "/pages/admin/{$self->template}.md"));
$page->slug(basename($self->template));
@@ -456,7 +480,9 @@ class AdminPlugin extends Plugin
$twig->twig_vars['location'] = $this->template;
$twig->twig_vars['base_url_relative_frontend'] = $twig->twig_vars['base_url_relative'] ?: '/';
$twig->twig_vars['admin_route'] = trim($this->admin_route, '/');
$twig->twig_vars['current_route'] = '/' . $twig->twig_vars['admin_route'] . '/' . $this->template . '/' . $this->route;
$twig->twig_vars['base_url_relative'] = $twig->twig_vars['base_url_simple'] . '/' . $twig->twig_vars['admin_route'];
$twig->twig_vars['current_url'] = rtrim($twig->twig_vars['base_url_relative'] . '/' . $this->template . '/' . $this->route, '/');
$theme_url = '/' . ltrim($this->grav['locator']->findResource('plugin://admin/themes/' . $this->theme,
false), '/');
$twig->twig_vars['theme_url'] = $theme_url;
@@ -762,7 +788,8 @@ class AdminPlugin extends Plugin
{
// Special case to redirect after changing the admin route to avoid 'breaking'
$obj = $event['object'];
if (null !== $obj) {
if (null !== $obj && method_exists($obj, 'blueprints')) {
$blueprint = $obj->blueprints()->getFilename();
if ($blueprint === 'admin/blueprints' && isset($obj->route) && $this->admin_route !== $obj->route) {
+2 -2
View File
@@ -1,5 +1,5 @@
name: Admin Panel
version: 1.8.1
version: 1.8.14
description: Adds an advanced administration panel to manage your site
icon: empire
author:
@@ -13,7 +13,7 @@ docs: https://github.com/getgrav/grav-plugin-admin/blob/develop/README.md
license: MIT
dependencies:
- { name: grav, version: '>=1.4.5' }
- { name: grav, version: '>=1.5.3' }
- { name: form, version: '>=2.14.0' }
- { name: login, version: '>=2.7.0' }
- { name: email, version: '>=2.7.0' }
@@ -18,6 +18,9 @@ form:
title: PLUGIN_ADMIN.CONTENT
fields:
xss_check:
type: xss
frontmatter:
classes: frontmatter
type: editor
@@ -2,10 +2,9 @@
namespace Grav\Plugin\Admin\Twig;
use Grav\Common\Grav;
use Grav\Common\Yaml;
use Grav\Common\Language\Language;
use Grav\Common\Page\Page;
use Symfony\Component\Yaml\Yaml;
use Symfony\Component\Yaml\Parser;
class AdminTwigExtension extends \Twig_Extension
{
@@ -32,6 +31,7 @@ class AdminTwigExtension extends \Twig_Extension
new \Twig_SimpleFilter('toYaml', [$this, 'toYamlFilter']),
new \Twig_SimpleFilter('fromYaml', [$this, 'fromYamlFilter']),
new \Twig_SimpleFilter('adminNicetime', [$this, 'adminNicetimeFilter']),
new \Twig_SimpleFilter('nested', [$this, 'nestedFilter']),
];
}
@@ -43,6 +43,23 @@ class AdminTwigExtension extends \Twig_Extension
];
}
public function nestedFilter($current, $name)
{
$path = explode('.', trim($name, '.'));
foreach ($path as $field) {
if (is_object($current) && isset($current->{$field})) {
$current = $current->{$field};
} elseif (is_array($current) && isset($current[$field])) {
$current = $current[$field];
} else {
return null;
}
}
return $current;
}
public function cloneFunc($obj)
{
return clone $obj;
@@ -81,7 +98,7 @@ class AdminTwigExtension extends \Twig_Extension
return $this->grav['admin']->translate($args, $lang);
}
public function toYamlFilter($value, $inline = true)
public function toYamlFilter($value, $inline = null)
{
return Yaml::dump($value, $inline);
@@ -89,8 +106,7 @@ class AdminTwigExtension extends \Twig_Extension
public function fromYamlFilter($value)
{
$yaml = new Parser();
return $yaml->parse($value);
return Yaml::parse($value);
}
public function adminNicetimeFilter($date, $long_strings = true)
+16 -14
View File
@@ -27,7 +27,7 @@ use RocketTheme\Toolbox\ResourceLocator\UniformResourceIterator;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use RocketTheme\Toolbox\Session\Message;
use RocketTheme\Toolbox\Session\Session;
use Symfony\Component\Yaml\Yaml;
use Grav\Common\Yaml;
use Composer\Semver\Semver;
use PicoFeed\Reader\Reader;
@@ -170,11 +170,11 @@ class Admin
/** @var \DirectoryIterator $directory */
foreach (new \DirectoryIterator($path) as $file) {
if ($file->isDir() || $file->isDot() || Utils::startsWith($file->getBasename(), '.')) {
if ($file->isDir() || $file->isDot() || Utils::startsWith($file->getFilename(), '.')) {
continue;
}
$lang = basename($file->getBasename(), '.yaml');
$lang = $file->getBasename('.yaml');
$languages[$lang] = LanguageCodes::getNativeName($lang);
@@ -202,7 +202,7 @@ class Admin
if ($file->isDir() || !preg_match('/^[^.].*.yaml$/', $file->getFilename())) {
continue;
}
$configurations[] = basename($file->getBasename(), '.yaml');
$configurations[] = $file->getBasename('.yaml');
}
return $configurations;
@@ -365,7 +365,7 @@ class Admin
$userKey = isset($credentials['username']) ? (string)$credentials['username'] : '';
$ipKey = Uri::ip();
$redirect = isset($post['redirect']) ? $post['redirect'] : $this->uri->route();
$redirect = isset($post['redirect']) ? $post['redirect'] : $this->base . $this->route;
// Check if the current IP has been used in failed login attempts.
$attempts = count($rateLimiter->getAttempts($ipKey, 'ip'));
@@ -392,11 +392,9 @@ class Admin
if ($user->authorized) {
$event->defMessage('PLUGIN_ADMIN.LOGIN_LOGGED_IN', 'info');
$event->defRedirect($redirect);
$event->defRedirect(isset($post['redirect']) ? $post['redirect'] : $redirect);
} else {
$this->session->redirect = $redirect;
$event->defRedirect($this->uri->route());
}
} else {
if ($user->authorized) {
@@ -406,7 +404,7 @@ class Admin
}
}
$event->defRedirect($this->uri->route());
$event->defRedirect($redirect);
$message = $event->getMessage();
if ($message) {
@@ -597,7 +595,8 @@ class Admin
}
if (!$post) {
$post = isset($_POST['data']) ? $_POST['data'] : [];
$post = $this->grav['uri']->post();
$post = isset($post['data']) ? $post['data'] : [];
}
// Check to see if a data type is plugin-provided, before looking into core ones
@@ -671,10 +670,10 @@ class Admin
$obj->file = $file;
$obj->page = $this->grav['pages']->get(dirname($obj->path));
$filename = pathinfo($obj->title)['filename'];
$filename = str_replace(['@3x', '@2x'], '', $filename);
if (isset(pathinfo($obj->title)['extension'])) {
$filename .= '.' . pathinfo($obj->title)['extension'];
$fileInfo = pathinfo($obj->title);
$filename = str_replace(['@3x', '@2x'], '', $fileInfo['filename']);
if (isset($fileInfo['extension'])) {
$filename .= '.' . $fileInfo['extension'];
}
if ($obj->page && isset($obj->page->media()[$filename])) {
@@ -1420,6 +1419,9 @@ class Admin
$path = "/{$path}";
}
// Fix for entities in path causing looping...
$path = urldecode($path);
$page = $path ? $pages->dispatch($path, true) : $pages->root();
if (!$page) {
@@ -2,9 +2,12 @@
namespace Grav\Plugin\Admin;
use Grav\Common\Config\Config;
use Grav\Common\Data\Data;
use Grav\Common\Filesystem\Folder;
use Grav\Common\Grav;
use Grav\Common\Media\Interfaces\MediaInterface;
use Grav\Common\Page\Media;
use Grav\Common\Page\Pages;
use Grav\Common\Utils;
use Grav\Common\Plugin;
use Grav\Common\Theme;
@@ -199,6 +202,29 @@ class AdminBaseController
$this->redirectCode = $code;
}
/**
* Sends JSON response and terminates the call.
*
* @param array $response
* @param int $code
* @return bool
*/
protected function sendJsonResponse(array $response, $code = 200)
{
// Make sure nothing extra gets written to the response.
while (ob_get_level()) {
ob_end_clean();
}
// JSON response.
http_response_code($code);
header('Content-Type: application/json');
header('Cache-Control: no-cache, no-store, must-revalidate');
echo json_encode($response);
exit();
}
/**
* Handles ajax upload for files.
* Stores in a flash object the temporary file and deals with potential file errors.
@@ -225,10 +251,10 @@ class AdminBaseController
$upload = $this->normalizeFiles($_FILES['data'], $settings->name);
$filename = trim($upload->file->name);
$filename = $upload->file->name;
// Handle bad filenames.
if (strtr($filename, "\t\n\r\0\x0b", '_____') !== $filename || rtrim($filename, '. ') !== $filename || preg_match('|\.php|', $filename)) {
if (!Utils::checkFilename($filename)) {
$this->admin->json_response = [
'status' => 'error',
'message' => sprintf($this->admin->translate('PLUGIN_ADMIN.FILEUPLOAD_UNABLE_TO_UPLOAD', null),
@@ -259,7 +285,7 @@ class AdminBaseController
}
// Handle errors and breaks without proceeding further
if ($upload->file->error != UPLOAD_ERR_OK) {
if ($upload->file->error !== UPLOAD_ERR_OK) {
$this->admin->json_response = [
'status' => 'error',
'message' => sprintf($this->admin->translate('PLUGIN_ADMIN.FILEUPLOAD_UNABLE_TO_UPLOAD', null),
@@ -285,6 +311,9 @@ class AdminBaseController
$accepted = false;
$errors = [];
// Do not trust mimetype sent by the browser
$mime = Utils::getMimeByFilename($upload->file->name);
foreach ((array)$settings->accept as $type) {
// Force acceptance of any file when star notation
if ($type === '*') {
@@ -293,15 +322,24 @@ class AdminBaseController
}
$isMime = strstr($type, '/');
$find = str_replace('*', '.*', $type);
$find = str_replace(['.', '*'], ['\.', '.*'], $type);
$match = preg_match('#' . $find . '$#', $isMime ? $upload->file->type : $upload->file->name);
if (!$match) {
$message = $isMime ? 'The MIME type "' . $upload->file->type . '"' : 'The File Extension';
$errors[] = $message . ' for the file "' . $upload->file->name . '" is not an accepted.';
$accepted |= false;
if ($isMime) {
$match = preg_match('#' . $find . '$#', $mime);
if (!$match) {
$errors[] = 'The MIME type "' . $mime . '" for the file "' . $upload->file->name . '" is not an accepted.';
} else {
$accepted = true;
break;
}
} else {
$accepted |= true;
$match = preg_match('#' . $find . '$#', $upload->file->name);
if (!$match) {
$errors[] = 'The File Extension for the file "' . $upload->file->name . '" is not an accepted.';
} else {
$accepted = true;
break;
}
}
}
@@ -366,7 +404,7 @@ class AdminBaseController
// Generate random name if required
if ($settings->random_name) { // TODO: document
$extension = pathinfo($upload->file->name)['extension'];
$extension = pathinfo($upload->file->name, PATHINFO_EXTENSION);
$upload->file->name = Utils::generateRandomString(15) . '.' . $extension;
}
@@ -717,9 +755,9 @@ class AdminBaseController
} else {
$new_data = $files;
}
if (isset($data['header'][$init_key])) {
if (isset($obj->header()->{$init_key})) {
$obj->modifyHeader($init_key,
array_replace_recursive([], $data['header'][$init_key], $new_data));
array_replace_recursive([], $obj->header()->{$init_key}, $new_data));
} else {
$obj->modifyHeader($init_key, $new_data);
}
@@ -743,30 +781,44 @@ class AdminBaseController
return false;
}
$data = $this->view === 'pages' ? $this->admin->page(true) : $this->prepareData([]);
$settings = $data->blueprints()->schema()->getProperty($this->post['name']);
$data = $this->view === 'pages' ? $this->admin->page(true) : $this->prepareData([]);
if (null === $data) {
return false;
}
if (method_exists($data, 'blueprints')) {
$settings = $data->blueprints()->schema()->getProperty($this->post['name']);
} elseif (method_exists($data, 'getBlueprint')) {
$settings = $data->getBlueprint()->schema()->getProperty($this->post['name']);
}
if (isset($settings['folder'])) {
$folder = $settings['folder'];
} else {
$folder = '@self';
$folder = 'self@';
}
// Do not use self@ outside of pages
if ($this->view !== 'pages' && in_array($folder, ['@self', 'self@', '@self@'])) {
$this->admin->json_response = [
'status' => 'error',
'message' => sprintf($this->admin->translate('PLUGIN_ADMIN.FILEUPLOAD_PREVENT_SELF', null), $folder)
];
if (!$data instanceof MediaInterface) {
$this->admin->json_response = [
'status' => 'error',
'message' => sprintf($this->admin->translate('PLUGIN_ADMIN.FILEUPLOAD_PREVENT_SELF', null), $folder)
];
return false;
return false;
}
$media = $data->getMedia();
} else {
// Set destination
$folder = Folder::getRelativePath(rtrim($folder, '/'));
$folder = $this->admin->getPagePathFromToken($folder);
$media = new Media($folder);
}
// Set destination
$folder = Folder::getRelativePath(rtrim($folder, '/'));
$folder = $this->admin->getPagePathFromToken($folder);
$media = new Media($folder);
$available_files = [];
$metadata = [];
$thumbs = [];
@@ -859,7 +911,29 @@ class AdminBaseController
$type = $uri->param('type');
$field = $uri->param('field');
$this->taskRemoveMedia();
// Get Blueprint
$settings = (object) $this->admin->blueprints($blueprint)->schema()->getProperty($field);
// Get destination
if ($this->grav['locator']->isStream($settings->destination)) {
$destination = $this->grav['locator']->findResource($settings->destination, false, true);
} else {
$destination = Folder::getRelativePath(rtrim($settings->destination, '/'));
$destination = $this->admin->getPagePathFromToken($destination);
}
// Not in path
if (!Utils::startsWith($path, $destination)) {
$this->admin->json_response = [
'status' => 'error',
'message' => 'Path not valid for this data type'
];
return false;
}
// Only remove files from correct destination...
$this->taskRemoveMedia($destination . '/' . basename($path));
if ($type === 'pages') {
$page = $this->admin->page(true, $proute);
@@ -876,9 +950,11 @@ class AdminBaseController
$page->save();
} else {
$blueprint_prefix = $type === 'config' ? '' : $type . '.';
$blueprint_name = str_replace(['config/', '/blueprints'], '', $blueprint);
$blueprint_field = $blueprint_prefix . $blueprint_name . '.' . $field;
$files = $this->grav['config']->get($blueprint_field);
if ($files) {
@@ -919,15 +995,17 @@ class AdminBaseController
*
* @return bool True if the action was performed
*/
public function taskRemoveMedia()
public function taskRemoveMedia($filename = null)
{
if (!$this->canEditMedia()) {
return false;
}
$filename = base64_decode($this->grav['uri']->param('route'));
if (!$filename) {
$filename = base64_decode($this->route);
if (is_null($filename)) {
$filename = base64_decode($this->grav['uri']->param('route'));
if (!$filename) {
$filename = base64_decode($this->route);
}
}
$file = File::instance($filename);
+199 -97
View File
@@ -14,17 +14,18 @@ use Grav\Common\Page\Medium\Medium;
use Grav\Common\Page\Page;
use Grav\Common\Page\Pages;
use Grav\Common\Page\Collection;
use Grav\Common\Security;
use Grav\Common\User\User;
use Grav\Common\Utils;
use Grav\Common\Backup\ZipBackup;
use Grav\Plugin\Admin\Twig\AdminTwigExtension;
use Grav\Plugin\Login\TwoFactorAuth\TwoFactorAuth;
use Grav\Common\Yaml;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\File\File;
use RocketTheme\Toolbox\File\JsonFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Yaml;
/**
* Class AdminController
@@ -465,7 +466,7 @@ class AdminController extends AdminBaseController
/**
* Handles updating Grav
*
* @return bool True if the action was performed
* @return bool False if user has no permissions.
*/
public function taskUpdategrav()
{
@@ -478,14 +479,14 @@ class AdminController extends AdminBaseController
$result = Gpm::selfupgrade();
if ($result) {
$this->admin->json_response = [
$json_response = [
'status' => 'success',
'type' => 'updategrav',
'version' => $version,
'message' => $this->admin->translate('PLUGIN_ADMIN.GRAV_WAS_SUCCESSFULLY_UPDATED_TO') . ' ' . $version
];
} else {
$this->admin->json_response = [
$json_response = [
'status' => 'error',
'type' => 'updategrav',
'version' => GRAV_VERSION,
@@ -493,7 +494,7 @@ class AdminController extends AdminBaseController
];
}
return true;
return $this->sendJsonResponse($json_response);
}
/**
@@ -611,14 +612,18 @@ class AdminController extends AdminBaseController
$reorder = true;
$data = (array)$this->data;
$this->grav['twig']->twig_vars['current_form_data'] = $data;
// Special handler for user data.
if ($this->view === 'user') {
if (!$this->grav['user']->exists()) {
$this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.NO_USER_EXISTS'),'error');
return false;
}
if (!$this->admin->authorize(['admin.super', 'admin.users'])) {
//not admin.super or admin.users
// no user file or not admin.super or admin.users
if ($this->prepareData($data)->username !== $this->grav['user']->username) {
$this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.INSUFFICIENT_PERMISSIONS_FOR_TASK') . ' save.',
'error');
$this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.INSUFFICIENT_PERMISSIONS_FOR_TASK') . ' save.','error');
return false;
}
}
@@ -643,13 +648,25 @@ class AdminController extends AdminBaseController
// Ensure route is prefixed with a forward slash.
$route = '/' . ltrim($route, '/');
// Check for valid frontmatter
if (isset($data['frontmatter']) && !$this->checkValidFrontmatter($data['frontmatter'])) {
$this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.INVALID_FRONTMATTER_COULD_NOT_SAVE'),
'error');
return false;
}
// XSS Checks for page content
$xss_whitelist = $this->grav['config']->get('security.xss_whitelist', 'admin.super');
if (!$this->admin->authorize($xss_whitelist)) {
$check_what = ['header' => isset($data['header']) ? $data['header'] : '', 'frontmatter' => isset($data['frontmatter']) ? $data['frontmatter'] : '', 'content' => isset($data['content']) ? $data['content'] : ''];
$results = Security::detectXssFromArray($check_what);
if (!empty($results)) {
$this->admin->setMessage('<i class="fa fa-ban"></i> ' . $this->admin->translate('PLUGIN_ADMIN.XSS_ONSAVE_ISSUE'),
'error');
return false;
}
}
$parent = $route && $route !== '/' && $route !== '.' && $route !== '/.' ? $pages->dispatch($route, true) : $pages->root();
$original_order = (int)trim($obj->order(), '.');
@@ -758,16 +775,8 @@ class AdminController extends AdminBaseController
public function checkValidFrontmatter($frontmatter)
{
try {
// Try native PECL YAML PHP extension first if available.
if (function_exists('yaml_parse')) {
$saved = @ini_get('yaml.decode_php');
@ini_set('yaml.decode_php', 0);
@yaml_parse("---\n" . $frontmatter . "\n...");
@ini_set('yaml.decode_php', $saved);
} else {
Yaml::parse($frontmatter);
}
} catch (ParseException $e) {
Yaml::parse($frontmatter);
} catch (\RuntimeException $e) {
return false;
}
@@ -874,6 +883,10 @@ class AdminController extends AdminBaseController
protected function taskGetNewsFeed()
{
if (!$this->authorizeTask('dashboard', ['admin.login', 'admin.super'])) {
return false;
}
$cache = $this->grav['cache'];
if ($this->post['refresh'] === 'true') {
@@ -910,11 +923,13 @@ class AdminController extends AdminBaseController
} catch (\Exception $e) {
$this->admin->json_response = ['status' => 'error', 'message' => $e->getMessage()];
return;
return false;
}
}
$this->admin->json_response = ['status' => 'success', 'feed_data' => $feed_data];
return true;
}
/**
@@ -922,6 +937,10 @@ class AdminController extends AdminBaseController
*/
protected function taskGetUpdates()
{
if (!$this->authorizeTask('dashboard', ['admin.login', 'admin.super'])) {
return false;
}
$data = $this->post;
$flush = (isset($data['flush']) && $data['flush'] == true) ? true : false;
@@ -954,12 +973,17 @@ class AdminController extends AdminBaseController
];
} else {
$this->admin->json_response = ['status' => 'error', 'message' => 'Cannot connect to the GPM'];
return false;
}
} catch (\Exception $e) {
$this->admin->json_response = ['status' => 'error', 'message' => $e->getMessage()];
return false;
}
return true;
}
/**
@@ -968,12 +992,16 @@ class AdminController extends AdminBaseController
*/
protected function taskGetNotifications()
{
if (!$this->authorizeTask('dashboard', ['admin.login', 'admin.super'])) {
return false;
}
$cache = $this->grav['cache'];
if (!(bool)$this->grav['config']->get('system.cache.enabled') || !$notifications = $cache->fetch('notifications')) {
//No notifications cache (first time)
$this->admin->json_response = ['status' => 'success', 'notifications' => [], 'need_update' => true];
return;
return true;
}
$need_update = false;
@@ -990,7 +1018,7 @@ class AdminController extends AdminBaseController
} catch (\Exception $e) {
$this->admin->json_response = ['status' => 'error', 'message' => $e->getMessage()];
return;
return false;
}
$this->admin->json_response = [
@@ -998,6 +1026,8 @@ class AdminController extends AdminBaseController
'notifications' => $notifications,
'need_update' => $need_update
];
return true;
}
/**
@@ -1007,6 +1037,10 @@ class AdminController extends AdminBaseController
*/
protected function taskProcessNotifications()
{
if (!$this->authorizeTask('notifications', ['admin.login', 'admin.super'])) {
return false;
}
$cache = $this->grav['cache'];
$data = $this->post;
@@ -1156,8 +1190,8 @@ class AdminController extends AdminBaseController
'status' => 'error',
'message' => $this->admin->translate('PLUGIN_ADMIN.INSUFFICIENT_PERMISSIONS_FOR_TASK')
];
echo json_encode($json_response);
exit;
return $this->sendJsonResponse($json_response, 403);
}
//check if there are packages that have this as a dependency. Abort and show which ones
@@ -1172,8 +1206,8 @@ class AdminController extends AdminBaseController
}
$json_response = ['status' => 'error', 'message' => $message];
echo json_encode($json_response);
exit;
return $this->sendJsonResponse($json_response, 200);
}
try {
@@ -1181,8 +1215,8 @@ class AdminController extends AdminBaseController
$result = Gpm::uninstall($package, []);
} catch (\Exception $e) {
$json_response = ['status' => 'error', 'message' => $e->getMessage()];
echo json_encode($json_response);
exit;
return $this->sendJsonResponse($json_response, 200);
}
if ($result) {
@@ -1191,16 +1225,16 @@ class AdminController extends AdminBaseController
'dependencies' => $dependencies,
'message' => $this->admin->translate(is_string($result) ? $result : 'PLUGIN_ADMIN.UNINSTALL_SUCCESSFUL')
];
echo json_encode($json_response);
exit;
return $this->sendJsonResponse($json_response, 200);
}
$json_response = [
'status' => 'error',
'message' => $this->admin->translate('PLUGIN_ADMIN.UNINSTALL_FAILED')
];
echo json_encode($json_response);
exit;
return $this->sendJsonResponse($json_response, 200);
}
/**
@@ -1215,6 +1249,15 @@ class AdminController extends AdminBaseController
$package_name = isset($data['package_name']) ? $data['package_name'] : '';
$current_version = isset($data['current_version']) ? $data['current_version'] : '';
if (!$this->authorizeTask('install ' . $type, ['admin.' . $type, 'admin.super'])) {
$json_response = [
'status' => 'error',
'message' => $this->admin->translate('PLUGIN_ADMIN.INSUFFICIENT_PERMISSIONS_FOR_TASK')
];
$this->sendJsonResponse($json_response, 403);
}
$url = "https://getgrav.org/download/{$type}s/$slug/$current_version";
$result = Gpm::directInstall($url);
@@ -1344,7 +1387,7 @@ class AdminController extends AdminBaseController
}
$download = urlencode(base64_encode($backup));
$url = rtrim($this->grav['uri']->rootUrl(true), '/') . '/' . trim($this->admin->base,
$url = rtrim($this->grav['uri']->rootUrl(false), '/') . '/' . trim($this->admin->base,
'/') . '/task' . $param_sep . 'backup/download' . $param_sep . $download . '/admin-nonce' . $param_sep . Utils::getNonce('admin-form');
$log->content([
@@ -1574,6 +1617,8 @@ class AdminController extends AdminBaseController
{
$this->uri = $this->uri ?: $this->grav['uri'];
$uri = $this->uri->post('uri');
$order = $this->uri->post('order') ?: null;
if ($uri) {
/** @var UniformResourceLocator $locator */
$locator = $this->grav['locator'];
@@ -1584,8 +1629,11 @@ class AdminController extends AdminBaseController
$media_path = $page ? $page->path() : null;
}
if ($order) {
$order = array_map('trim', explode(',', $order));
}
return $media_path ? new Media($media_path) : null;
return $media_path ? new Media($media_path, $order) : null;
}
/**
@@ -1646,6 +1694,19 @@ class AdminController extends AdminBaseController
return false;
}
$filename = $_FILES['file']['name'];
// Handle bad filenames.
if (!Utils::checkFilename($filename)) {
$this->admin->json_response = [
'status' => 'error',
'message' => sprintf($this->admin->translate('PLUGIN_ADMIN.FILEUPLOAD_UNABLE_TO_UPLOAD'),
$filename, 'Bad filename')
];
return false;
}
$grav_limit = $config->get('system.media.upload_limit', 0);
// You should also check filesize here.
if ($grav_limit > 0 && $_FILES['file']['size'] > $grav_limit) {
@@ -1659,18 +1720,13 @@ class AdminController extends AdminBaseController
// Check extension
$fileParts = pathinfo($_FILES['file']['name']);
$fileExt = '';
if (isset($fileParts['extension'])) {
$fileExt = strtolower($fileParts['extension']);
}
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
// If not a supported type, return
if (!$fileExt || !$config->get("media.types.{$fileExt}")) {
if (!$extension || !$config->get("media.types.{$extension}")) {
$this->admin->json_response = [
'status' => 'error',
'message' => $this->admin->translate('PLUGIN_ADMIN.UNSUPPORTED_FILE_TYPE') . ': ' . $fileExt
'message' => $this->admin->translate('PLUGIN_ADMIN.UNSUPPORTED_FILE_TYPE') . ': ' . $extension
];
return false;
@@ -1687,9 +1743,16 @@ class AdminController extends AdminBaseController
return false;
}
/** @var UniformResourceLocator $locator */
$locator = $this->grav['locator'];
$path = $media->path();
if ($locator->isStream($path)) {
$path = $locator->findResource($path, true, true);
}
// Upload it
if (!move_uploaded_file($_FILES['file']['tmp_name'],
sprintf('%s/%s', $media->path(), $_FILES['file']['name']))
sprintf('%s/%s', $path, $filename))
) {
$this->admin->json_response = [
'status' => 'error',
@@ -1701,13 +1764,12 @@ class AdminController extends AdminBaseController
// Add metadata if needed
$include_metadata = Grav::instance()['config']->get('system.media.auto_metadata_exif', false);
$filename = $fileParts['basename'];
$filename = str_replace(['@3x', '@2x'], '', $filename);
$basename = str_replace(['@3x', '@2x'], '', pathinfo($filename, PATHINFO_BASENAME));
$metadata = [];
if ($include_metadata && isset($media[$filename])) {
$img_metadata = $media[$filename]->metadata();
if ($include_metadata && isset($media[$basename])) {
$img_metadata = $media[$basename]->metadata();
if ($img_metadata) {
$metadata = $img_metadata;
}
@@ -1750,6 +1812,11 @@ class AdminController extends AdminBaseController
$filename = !empty($this->post['filename']) ? $this->post['filename'] : null;
// Handle bad filenames.
if (!Utils::checkFilename($filename)) {
$filename = null;
}
if (!$filename) {
$this->admin->json_response = [
'status' => 'error',
@@ -1759,7 +1826,13 @@ class AdminController extends AdminBaseController
return false;
}
/** @var UniformResourceLocator $locator */
$locator = $this->grav['locator'];
$targetPath = $media->path() . '/' . $filename;
if ($locator->isStream($targetPath)) {
$targetPath = $locator->findResource($targetPath, true, true);
}
$fileParts = pathinfo($filename);
$found = false;
@@ -1781,7 +1854,13 @@ class AdminController extends AdminBaseController
// Remove Extra Files
foreach (scandir($media->path(), SCANDIR_SORT_NONE) as $file) {
if (preg_match("/{$fileParts['filename']}@\d+x\.{$fileParts['extension']}(?:\.meta\.yaml)?$|{$filename}\.meta\.yaml$/", $file)) {
$result = unlink($media->path() . '/' . $file);
$targetPath = $media->path() . '/' . $file;
if ($locator->isStream($targetPath)) {
$targetPath = $locator->findResource($targetPath, true, true);
}
$result = unlink($targetPath);
if (!$result) {
$this->admin->json_response = [
@@ -1825,9 +1904,9 @@ class AdminController extends AdminBaseController
*/
protected function taskProcessMarkdown()
{
/*if (!$this->authorizeTask('process markdown', ['admin.pages', 'admin.super'])) {
return;
}*/
if (!$this->authorizeTask('process markdown', ['admin.pages', 'admin.super'])) {
return false;
}
try {
$page = $this->admin->page(true);
@@ -1843,6 +1922,7 @@ class AdminController extends AdminBaseController
$this->preparePage($page, true);
$page->header();
$page->templateFormat('html');
// Add theme template paths to Twig loader
$template_paths = $this->grav['locator']->findResources('theme://templates');
@@ -2153,6 +2233,10 @@ class AdminController extends AdminBaseController
*/
protected function taskSwitchlanguage()
{
if (!$this->authorizeTask('switch language', ['admin.pages', 'admin.super'])) {
return false;
}
$data = (array)$this->data;
if (isset($data['lang'])) {
@@ -2176,6 +2260,68 @@ class AdminController extends AdminBaseController
$admin_route = $this->admin->base;
$this->setRedirect('/' . $language . $admin_route . '/' . $redirect);
return true;
}
/**
* Handle direct install.
*/
protected function taskDirectInstall()
{
if (!$this->authorizeTask('install', ['admin.super'])) {
return false;
}
$file_path = isset($this->data['file_path']) ? $this->data['file_path'] : null ;
if (isset($_FILES['uploaded_file'])) {
// Check $_FILES['file']['error'] value.
switch ($_FILES['uploaded_file']['error']) {
case UPLOAD_ERR_OK:
break;
case UPLOAD_ERR_NO_FILE:
$this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.NO_FILES_SENT'), 'error');
return false;
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
$this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.EXCEEDED_FILESIZE_LIMIT'), 'error');
return false;
case UPLOAD_ERR_NO_TMP_DIR:
$this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.UPLOAD_ERR_NO_TMP_DIR'), 'error');
return false;
default:
$this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.UNKNOWN_ERRORS'), 'error');
return false;
}
$file_path = $_FILES['uploaded_file']['tmp_name'];
// Handle bad filenames.
if (!Utils::checkFilename(basename($file_path))) {
$this->admin->json_response = [
'status' => 'error',
'message' => $this->admin->translate('PLUGIN_ADMIN.UNKNOWN_ERRORS')
];
return false;
}
}
$result = Gpm::directInstall($file_path);
if ($result === true) {
$this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.INSTALLATION_SUCCESSFUL'), 'info');
} else {
$this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.INSTALLATION_FAILED') . ': ' . $result,
'error');
}
$this->setRedirect('/tools');
return true;
}
/**
@@ -2249,49 +2395,5 @@ class AdminController extends AdminBaseController
return $filename . '.md';
}
/**
* Handle direct install.
*/
protected function taskDirectInstall()
{
$file_path = isset($this->data['file_path']) ? $this->data['file_path'] : null ;
if (isset($_FILES['uploaded_file'])) {
// Check $_FILES['file']['error'] value.
switch ($_FILES['uploaded_file']['error']) {
case UPLOAD_ERR_OK:
break;
case UPLOAD_ERR_NO_FILE:
$this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.NO_FILES_SENT'), 'error');
return false;
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
$this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.EXCEEDED_FILESIZE_LIMIT'), 'error');
return false;
case UPLOAD_ERR_NO_TMP_DIR:
$this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.UPLOAD_ERR_NO_TMP_DIR'), 'error');
return false;
default:
$this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.UNKNOWN_ERRORS'), 'error');
return false;
}
$file_path = $_FILES['uploaded_file']['tmp_name'];
}
$result = Gpm::directInstall($file_path);
if ($result === true) {
$this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.INSTALLATION_SUCCESSFUL'), 'info');
} else {
$this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.INSTALLATION_FAILED') . ': ' . $result,
'error');
}
$this->setRedirect('/tools');
return true;
}
}
+24 -4
View File
@@ -1,15 +1,35 @@
{
"name": "grav-plugin-admin",
"type": "grav-plugin",
"description": "Admin plugin for Grav CMS",
"keywords": ["admin", "plugin", "manager", "panel"],
"homepage": "https://github.com/trilbymedia/grav-plugin-admin",
"license": "MIT",
"authors": [
{
"name": "Team Grav",
"email": "devs@getgrav.org",
"homepage": "http://getgrav.org",
"role": "Developer"
}
],
"require": {
"php": ">=5.6.4",
"composer/semver": "^1.4",
"fguillot/picofeed": "@stable"
},
"require-dev": {
"codeception/codeception": "^2.1",
"fzaninotto/faker": "^1.5",
"symfony/yaml": "~2.8",
"symfony/console": "~2.8",
"symfony/finder": "~2.8",
"symfony/event-dispatcher": "~2.8"
"symfony/yaml": "~3.4",
"symfony/console": "~3.4",
"symfony/finder": "~3.4",
"symfony/event-dispatcher": "~3.4"
},
"config": {
"platform": {
"php": "5.6.4"
}
},
"scripts": {
"test": "vendor/bin/codecept run unit",
+372 -478
View File
File diff suppressed because it is too large Load Diff
+15
View File
@@ -0,0 +1,15 @@
{
"project":"grav-plugin-admin",
"platforms":{
"grav":{
"nodes":{
"plugin":[
{
"source":"/",
"destination":"/user/plugins/admin"
}
]
}
}
}
}
+28 -2
View File
@@ -392,6 +392,10 @@ PLUGIN_ADMIN:
DISPLAY_ERRORS_HELP: "Display full backtrace-style error page"
LOG_ERRORS: "Log errors"
LOG_ERRORS_HELP: "Log errors to /logs folder"
LOG_HANDLER: "Log handler"
LOG_HANDLER_HELP: "Where to output the logs"
SYSLOG_FACILITY: "Syslog facility"
SYSLOG_FACILITY_HELP: "Syslog facility for output"
DEBUGGER: "Debugger"
DEBUGGER_HELP: "Enable Grav debugger and following settings"
DEBUG_TWIG: "Debug Twig"
@@ -441,6 +445,7 @@ PLUGIN_ADMIN:
PAGE_FILE: "Page Template"
PAGE_FILE_HELP: "Page template file name, and by default the display template for this page"
NO_USER_ACCOUNTS: "No user accounts found, please create one first..."
NO_USER_EXISTS: "No local user exists for this account, cannot save..."
REDIRECT_TRAILING_SLASH: "Redirect trailing slash"
REDIRECT_TRAILING_SLASH_HELP: "Perform a 301 redirect rather than transparently handling trailing slash URIs."
DEFAULT_DATE_FORMAT: "Page date format"
@@ -502,7 +507,6 @@ PLUGIN_ADMIN:
ADD_FOLDER: "Add Folder"
PROXY_URL: "Proxy URL"
PROXY_URL_HELP: "Enter the proxy HOST or IP and PORT"
INVALID_FRONTMATTER_COULD_NOT_SAVE: "Invalid frontmatter, could not save"
NOTHING_TO_SAVE: "Nothing to Save"
FILE_ERROR_ADD: "An error occurred while trying to add the file"
FILE_ERROR_UPLOAD: "An error occurred while trying to upload the file"
@@ -554,7 +558,6 @@ PLUGIN_ADMIN:
CANNOT_ADD_FILES_PAGE_NOT_SAVED: "NOTE: Page must be saved before you can upload files to it."
DROP_FILES_HERE_TO_UPLOAD: "Drop your files here or <strong>click in this area</strong>"
INSERT: "Insert"
METADATA: "Metadata"
UNDO: "Undo"
REDO: "Redo"
HEADERS: "Headers"
@@ -713,3 +716,26 @@ PLUGIN_ADMIN:
ROUTE_ALIASES: "Route Aliases"
OPEN_NEW_TAB: "Open in new tab"
PAGEMEDIA_RESIZER: "> The following settings apply to images uploaded through the page media. Resize Width / Height will automatically resize down and proportionally an image if exceeds the limits set in the inputs. Resolution min and max values define the size ranges for uploaded images. Set the fields to 0 to prevent any manipulation."
SESSION_INITIALIZE: "Initialize Session"
SESSION_INITIALIZE_HELP: "Makes Grav to start a session. This feature is needed to make any user interaction to work, such as login, forms etc. Admin plugin isn't affected by this setting."
STRICT_YAML_COMPAT: "YAML Compatibility"
STRICT_YAML_COMPAT_HELP: "Falls back to Symfony 2.4 YAML parser if Native or 3.4 parser fails"
STRICT_TWIG_COMPAT: "Twig Compatibility"
STRICT_TWIG_COMPAT_HELP: "Enables deprecated Twig autoescape setting. When disabled, |raw filter is required to output HTML as Twig will autoescape output"
SECURITY: "Security"
XSS_SECURITY: "XSS Security"
XSS_WHITELIST_PERMISSIONS: "Whitelist Permissions"
XSS_WHITELIST_PERMISSIONS_HELP: "Users with these permissions will skip the XSS rules when saving content"
XSS_ON_EVENTS: "On-events"
XSS_INVALID_PROTOCOLS: "Enable Invalid protocols"
XSS_MOZ_BINDINGS: "Moz bindings"
XSS_HTML_INLINE_STYLES: "HTML inline styles"
XSS_DANGEROUS_TAGS: "Dangerous HTML tags"
XSS_DANGEROUS_TAGS_LIST: "Dangerous HTML tags list"
XSS_ONSAVE_ISSUE: "Save failed: XSS issue detected..."
XSS_ISSUE: "<strong>NOTICE:</strong> Grav found potential XSS issues in <strong>%s</strong>"
UPLOADS_SECURITY: "Uploads Security"
UPLOADS_DANGEROUS_EXTENSIONS: "Dangerous Extensions"
UPLOADS_DANGEROUS_EXTENSIONS_HELP: "Block these extensions from being uploaded no matter the accepted MIME types"
@@ -28,6 +28,10 @@ class Template {
return this.container.find('[data-grav-array-mode="value_only"]:first').length || false;
}
isTextArea() {
return this.container.data('grav-array-textarea') || false;
}
shouldBeDisabled() {
// check for toggleables, if field is toggleable and it's not enabled, render disabled
let toggle = this.container.closest('.form-field').find('[data-grav-field="toggleable"] input[type="checkbox"]');
@@ -35,20 +39,22 @@ class Template {
}
getNewRow() {
let tpl = '';
let tpl = '';const value = this.isTextArea()
? `<textarea ${this.shouldBeDisabled() ? 'disabled="disabled"' : ''} data-grav-array-type="value" name="" placeholder="${this.getValuePlaceholder()}"></textarea>`
: `<input ${this.shouldBeDisabled() ? 'disabled="disabled"' : ''} data-grav-array-type="value" type="text" name="" value="" placeholder="${this.getValuePlaceholder()}" />`;
if (this.isValueOnly()) {
tpl += `
<div class="form-row array-field-value_only" data-grav-array-type="row">
<span data-grav-array-action="sort" class="fa fa-bars"></span>
<input ${this.shouldBeDisabled() ? 'disabled="disabled"' : ''} data-grav-array-type="value" type="text" value="" placeholder="${this.getValuePlaceholder()}" />
${value}
`;
} else {
tpl += `
<div class="form-row" data-grav-array-type="row">
<span data-grav-array-action="sort" class="fa fa-bars"></span>
<input ${this.shouldBeDisabled() ? 'disabled="disabled"' : ''} data-grav-array-type="key" type="text" value="" placeholder="${this.getKeyPlaceholder()}" />
<input ${this.shouldBeDisabled() ? 'disabled="disabled"' : ''} data-grav-array-type="value" type="text" name="" value="" placeholder="${this.getValuePlaceholder()}" />
${value}
`;
}
@@ -1,8 +1,8 @@
import $ from 'jquery';
import { config } from 'grav-config';
import { config, uri_params } from 'grav-config';
import request from '../../utils/request';
const insertTextAt = (string, index, text) => [string.slice(0, index), text, string.slice(index)].join('');
// const insertTextAt = (string, index, text) => [string.slice(0, index), text, string.slice(index)].join('');
export default class FilePickerField {
@@ -45,10 +45,11 @@ export default class FilePickerField {
let parent = field.closest('[data-grav-filepicker]');
let name = parent.data('name');
let value = parent.data('value');
let params = JSON.stringify(uri_params || '{}');
request(url, {
method: 'post',
body: { name }
body: { name, params }
}, (response) => {
if (typeof response.files === 'undefined') {
return;
@@ -78,14 +79,12 @@ export default class FilePickerField {
let renderOption = function renderOption(item, escape) {
let image = '';
if (imagesPreview && folder && (!item.status || item.status === 'available') && item.name.match(/\.(jpg|jpeg|png|gif)$/i)) {
const fallback2x = insertTextAt(`${config.base_url_relative}/../${folder}/${item.name}`, -4, '@2x');
const fallback3x = insertTextAt(`${config.base_url_relative}/../${folder}/${item.name}`, -4, '@3x');
// const fallback2x = insertTextAt(`${config.base_url_relative}/../${folder}/${item.name}`, -4, '@2x');
// const fallback3x = insertTextAt(`${config.base_url_relative}/../${folder}/${item.name}`, -4, '@3x');
const source = thumbs[item.name] || `${config.base_url_relative}/../${folder}/${item.name}`;
image = `
<img class="filepicker-field-image"
src="${source}"
onerror="if(this.src==='${fallback2x}'){this.src='${fallback3x}';this.onerror='';}else{this.src='${fallback2x}'}" />`;
// onerror="if(this.src==='${fallback2x}'){this.src='${fallback3x}';}else{this.src='${fallback2x}'}"
image = `<img class="filepicker-field-image" src="${source}" />`;
}
return `<div>
@@ -1,5 +1,6 @@
import $ from 'jquery';
import Dropzone from 'dropzone';
import EXIF from 'exif-js';
import request from '../../utils/request';
import { config, translations } from 'grav-config';
@@ -70,6 +71,8 @@ const DropzoneMediaConfig = {
</div>`.trim()
};
global.EXIF = EXIF;
const ACCEPT_FUNC = function(file, done, settings) {
const resolution = settings.resolution;
if (!resolution) return done();
@@ -162,7 +165,6 @@ export default class FilesField {
}
getURI() {
console.log(this.container.data('mediaUri'));
return this.container.data('mediaUri') || '';
}
View File
@@ -20,9 +20,8 @@ export default class SelectizeField {
let field = (isInput ? element : element.find('input, select'));
if (!field.length || field.get(0).selectize) { return; }
field.selectize($.extend({}, data, {
plugins: ['required-fix']
}));
const plugins = $.merge(data.plugins ? data.plugins : [], ['required-fix']);
field.selectize($.extend({}, data, { plugins }));
this.elements.push(field.data('selectize'));
}
+2 -3
View File
@@ -14,9 +14,8 @@ import request from './utils/request';
import './utils/2fa';
// bootstrap jQuery extensions
import 'bootstrap/js/transition';
import 'bootstrap/js/dropdown';
import 'bootstrap/js/collapse';
import './utils/bootstrap-transition';
import './utils/bootstrap-collapse';
// tabs memory
import './utils/tabs-memory';
@@ -74,7 +74,8 @@ export default class PageMedia extends FilesField {
}
fetchMedia() {
const body = { uri: this.getURI() };
const order = this.container.closest('.form-field').find('[name="data[header][media_order]"]').val();
const body = { uri: this.getURI(), order };
let url = this.urls.fetch;
request(url, { method: 'post', body }, (response) => {
@@ -0,0 +1,210 @@
import jQuery from 'jquery';
/* ========================================================================
* Bootstrap: collapse.js v3.4.0
* http://getbootstrap.com/javascript/#collapse
* ========================================================================
* Copyright 2011-2016 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
/* jshint latedef: false */
+(function($) {
'use strict';
// COLLAPSE PUBLIC CLASS DEFINITION
// ================================
var Collapse = function(element, options) {
this.$element = $(element);
this.options = $.extend({}, Collapse.DEFAULTS, options);
this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' +
'[data-toggle="collapse"][data-target="#' + element.id + '"]');
this.transitioning = null;
if (this.options.parent) {
this.$parent = this.getParent();
} else {
this.addAriaAndCollapsedClass(this.$element, this.$trigger);
}
if (this.options.toggle) this.toggle();
};
Collapse.VERSION = '3.4.0';
Collapse.TRANSITION_DURATION = 350;
Collapse.DEFAULTS = {
toggle: true
};
Collapse.prototype.dimension = function() {
var hasWidth = this.$element.hasClass('width');
return hasWidth ? 'width' : 'height';
};
Collapse.prototype.show = function() {
if (this.transitioning || this.$element.hasClass('in')) return;
var activesData;
var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing');
if (actives && actives.length) {
activesData = actives.data('bs.collapse');
if (activesData && activesData.transitioning) return;
}
var startEvent = $.Event('show.bs.collapse');
this.$element.trigger(startEvent);
if (startEvent.isDefaultPrevented()) return;
if (actives && actives.length) {
Plugin.call(actives, 'hide');
activesData || actives.data('bs.collapse', null);
}
var dimension = this.dimension();
this.$element
.removeClass('collapse')
.addClass('collapsing')[dimension](0)
.attr('aria-expanded', true);
this.$trigger
.removeClass('collapsed')
.attr('aria-expanded', true);
this.transitioning = 1;
var complete = function() {
this.$element
.removeClass('collapsing')
.addClass('collapse in')[dimension]('');
this.transitioning = 0;
this.$element
.trigger('shown.bs.collapse');
};
if (!$.support.transition) return complete.call(this);
var scrollSize = $.camelCase(['scroll', dimension].join('-'));
this.$element
.one('bsTransitionEnd', $.proxy(complete, this))
.emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]);
};
Collapse.prototype.hide = function() {
if (this.transitioning || !this.$element.hasClass('in')) return;
var startEvent = $.Event('hide.bs.collapse');
this.$element.trigger(startEvent);
if (startEvent.isDefaultPrevented()) return;
var dimension = this.dimension();
this.$element[dimension](this.$element[dimension]())[0].offsetHeight;
this.$element
.addClass('collapsing')
.removeClass('collapse in')
.attr('aria-expanded', false);
this.$trigger
.addClass('collapsed')
.attr('aria-expanded', false);
this.transitioning = 1;
var complete = function() {
this.transitioning = 0;
this.$element
.removeClass('collapsing')
.addClass('collapse')
.trigger('hidden.bs.collapse');
};
if (!$.support.transition) return complete.call(this);
this.$element[dimension](0)
.one('bsTransitionEnd', $.proxy(complete, this))
.emulateTransitionEnd(Collapse.TRANSITION_DURATION);
};
Collapse.prototype.toggle = function() {
this[this.$element.hasClass('in') ? 'hide' : 'show']();
};
Collapse.prototype.getParent = function() {
return $(this.options.parent)
.find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]')
.each($.proxy(function(i, element) {
var $element = $(element);
this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element);
}, this))
.end();
};
Collapse.prototype.addAriaAndCollapsedClass = function($element, $trigger) {
var isOpen = $element.hasClass('in');
$element.attr('aria-expanded', isOpen);
$trigger
.toggleClass('collapsed', !isOpen)
.attr('aria-expanded', isOpen);
};
function getTargetFromTrigger($trigger) {
var href;
var target = $trigger.attr('data-target') ||
(href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, ''); // strip for ie7
return $(target);
}
// COLLAPSE PLUGIN DEFINITION
// ==========================
function Plugin(option) {
return this.each(function() {
var $this = $(this);
var data = $this.data('bs.collapse');
var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option === 'object' && option);
if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false;
if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)));
if (typeof option === 'string') data[option]();
});
}
var old = $.fn.collapse;
$.fn.collapse = Plugin;
$.fn.collapse.Constructor = Collapse;
// COLLAPSE NO CONFLICT
// ====================
$.fn.collapse.noConflict = function() {
$.fn.collapse = old;
return this;
};
// COLLAPSE DATA-API
// =================
$(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function(e) {
var $this = $(this);
if (!$this.attr('data-target')) e.preventDefault();
var $target = getTargetFromTrigger($this);
var data = $target.data('bs.collapse');
var option = data ? 'toggle' : $this.data();
Plugin.call($target, option);
});
}(jQuery));
@@ -0,0 +1,52 @@
import jQuery from 'jquery';
+(function($) {
'use strict';
// CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
// ============================================================
function transitionEnd() {
var el = document.createElement('bootstrap');
var transEndEventNames = {
WebkitTransition: 'webkitTransitionEnd',
MozTransition: 'transitionend',
OTransition: 'oTransitionEnd otransitionend',
transition: 'transitionend'
};
for (var name in transEndEventNames) {
if (el.style[name] !== undefined) {
return { end: transEndEventNames[name] };
}
}
return false; // explicit for ie8 ( ._.)
}
// http://blog.alexmaccaw.com/css-transitions
$.fn.emulateTransitionEnd = function(duration) {
var called = false;
var $el = this;
$(this).one('bsTransitionEnd', function() { called = true; });
var callback = function() { if (!called) $($el).trigger($.support.transition.end); };
setTimeout(callback, duration);
return this;
};
$(function() {
$.support.transition = transitionEnd();
if (!$.support.transition) return;
$.event.special.bsTransitionEnd = {
bindType: $.support.transition.end,
delegateType: $.support.transition.end,
handle: function(e) {
if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments);
}
};
});
}(jQuery));
+1 -1
View File
@@ -1,3 +1,3 @@
@import url("https://fonts.googleapis.com/css?family=Roboto:300,400,500|Inconsolata:400,700|&subset=latin-ext");body,h5,h6,.badge,.note,.grav-mdeditor-preview,input,select,textarea,button,.selectize-input{font-family:"Roboto","Helvetica","Tahoma","Geneva","Arial",sans-serif}h1,h2,h3,h4,.form-tabs>label,.label{font-family:"Josefin Slab","Helvetica","Tahoma","Geneva","Arial",sans-serif}code,kbd,pre,samp,body .CodeMirror{font-family:"Inconsolata","Monaco","Consolas","Lucida Console",monospace !important}
/*# sourceMappingURL=../css-compiled/fonts.css.map */
/*# sourceMappingURL=fonts.css.map */
@@ -1,11 +1 @@
{
"version": 3,
"file": "../scss/fonts.css",
"sources": [
"../scss/fonts.scss",
"../hdr0",
"../scss/configuration/fonts/_support.scss"
],
"mappings": "AE+DA,OAAO,CAAC,uGAAI,CAtBR,AAAA,IAAI,CAAE,AAAA,EAAE,CAAE,AAAA,EAAE,CACZ,AAAA,MAAM,CAAE,AAAA,KAAK,CAAE,AAAA,sBAAsB,CACrC,AAAA,KAAK,CAAE,AAAA,MAAM,CAAE,AAAA,QAAQ,CAAE,AAAA,MAAM,CAAE,AAAA,gBAAgB,AAAC,CAC9C,WAAW,CAAE,QAAU,CAAE,WAAW,CAAE,QAAQ,CAAE,QAAQ,CAAE,OAAO,CAAE,UAAU,CAChF,AAID,AAAA,EAAE,CAAE,AAAA,EAAE,CAAE,AAAA,EAAE,CAAE,AAAA,EAAE,CACd,AAAa,UAAH,CAAG,KAAK,CAAE,AAAA,MAAM,AAAC,CACvB,WAAW,CAAE,cAAU,CAAE,WAAW,CAAE,QAAQ,CAAE,QAAQ,CAAE,OAAO,CAAE,UAAU,CAChF,AAID,AAAA,IAAI,CAAE,AAAA,GAAG,CAAE,AAAA,GAAG,CAAE,AAAA,IAAI,CACpB,AAAK,IAAD,CAAC,WAAW,AAAC,CACb,WAAW,CAAE,aAAU,CAAE,QAAQ,CAAE,UAAU,CAAE,gBAAgB,CAAE,SAAS,CAAC,UAAU,CACxF",
"names": []
}
{"version":3,"file":"fonts.css","sources":["fonts.scss","configuration/fonts/_support.scss"],"sourcesContent":["$fonts-header: 'Josefin Slab' !default;\n$fonts-default: 'Roboto' !default;\n$fonts-mono: 'Inconsolata' !default;\n\n$font-definitions: (\n Josefin+Slab: 400,\n Roboto: '300,400,500',\n Inconsolata: '400,700'\n);\n\n@import \"configuration/fonts/support\";\n\n\n\n\n","@function str-replace($string, $search, $replace: '') {\n $index: str-index($string, $search);\n\n @if $index {\n @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);\n }\n\n @return $string;\n}\n\n@function admin-font-faces($fonts) {\n $url: \"https://fonts.googleapis.com/css?family=\";\n $nb: 0;\n\n @each $fontname, $weights in $fonts {\n\n @if $fontname == $fonts-default or\n $fontname == $fonts-header or\n $fontname == $fonts-mono {\n\n $nb: $nb + 1;\n $nb-word: 0;\n\n $fontname: str-replace(\"#{$fontname}\", \" \", \"+\");\n\n $url: $url + $fontname;\n\n @if $weights != null {\n $url: $url + \":\" + $weights;\n }\n\n @if $nb < 3 {\n $url: $url + \"|\";\n }\n }\n }\n\n @return $url + \"&subset=latin-ext\";\n}\n\n@mixin body-fonts($font) {\n body, h5, h6,\n .badge, .note, .grav-mdeditor-preview,\n input, select, textarea, button, .selectize-input {\n font-family: \"#{$font}\", \"Helvetica\", \"Tahoma\", \"Geneva\", \"Arial\", sans-serif;\n }\n}\n\n@mixin header-fonts($font) {\n h1, h2, h3, h4,\n .form-tabs > label, .label {\n font-family: \"#{$font}\", \"Helvetica\", \"Tahoma\", \"Geneva\", \"Arial\", sans-serif;\n }\n}\n\n@mixin mono-fonts($font) {\n code, kbd, pre, samp,\n body .CodeMirror {\n font-family: \"#{$font}\", \"Monaco\", \"Consolas\", \"Lucida Console\", monospace !important;\n }\n}\n$font-url: admin-font-faces($font-definitions);\n\n@import url(\"#{$font-url}\");\n\n@include body-fonts($fonts-default);\n\n@include header-fonts($fonts-header);\n\n@include mono-fonts($fonts-mono);\n\n\n\n\n\n"],"names":[],"mappings":"AC+DA,OAAO,CAAC,uGAAI,CAtBR,AAAA,IAAI,CAAE,EAAE,CAAE,EAAE,CACZ,MAAM,CAAE,KAAK,CAAE,sBAAsB,CACrC,KAAK,CAAE,MAAM,CAAE,QAAQ,CAAE,MAAM,CAAE,gBAAgB,AAAC,CAC9C,WAAW,CAAE,QAAU,CAAE,WAAW,CAAE,QAAQ,CAAE,QAAQ,CAAE,OAAO,CAAE,UAAU,CAChF,AAID,AAAA,EAAE,CAAE,EAAE,CAAE,EAAE,CAAE,EAAE,CACd,UAAU,CAAG,KAAK,CAAE,MAAM,AAAC,CACvB,WAAW,CAAE,cAAU,CAAE,WAAW,CAAE,QAAQ,CAAE,QAAQ,CAAE,OAAO,CAAE,UAAU,CAChF,AAID,AAAA,IAAI,CAAE,GAAG,CAAE,GAAG,CAAE,IAAI,CACpB,IAAI,CAAC,WAAW,AAAC,CACb,WAAW,CAAE,aAAU,CAAE,QAAQ,CAAE,UAAU,CAAE,gBAAgB,CAAE,SAAS,CAAC,UAAU,CACxF"}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,3 +1,3 @@
body,h5,h6,.badge,.note,.grav-mdeditor-preview,input,select,textarea,button,.selectize-input,h1,h2,h3,h4,#admin-menu li,.form-tabs>label,.label,body .CodeMirror pre{font-family:"Helvetica Neue", "Helvetica", "Tahoma", "Geneva", "Arial", sans-serif}code,kbd,pre,samp{font-family:"Monaco", "Consolas", "Lucida Console", monospace}
/*# sourceMappingURL=../css-compiled/simple-fonts.css.map */
/*# sourceMappingURL=simple-fonts.css.map */
@@ -1,10 +1 @@
{
"version": 3,
"file": "../scss/simple-fonts.css",
"sources": [
"../scss/simple-fonts.scss",
"../hdr0"
],
"mappings": "AAAA,AAAA,IAAI,CAAE,AAAA,EAAE,CAAE,AAAA,EAAE,CACZ,AAAA,MAAM,CAAE,AAAA,KAAK,CAAE,AAAA,sBAAsB,CACrC,AAAA,KAAK,CAAE,AAAA,MAAM,CAAE,AAAA,QAAQ,CAAE,AAAA,MAAM,CAAE,AAAA,gBAAgB,CACjD,AAAA,EAAE,CAAE,AAAA,EAAE,CAAE,AAAA,EAAE,CAAE,AAAA,EAAE,CACd,AAAY,WAAD,CAAC,EAAE,CAAE,AAAa,UAAH,CAAG,KAAK,CAAE,AAAA,MAAM,CAC1C,AAAiB,IAAb,CAAC,WAAW,CAAC,GAAG,AAAC,CACjB,WAAW,CAAE,sEAAuE,CACvF,AACD,AAAA,IAAI,CAAE,AAAA,GAAG,CAAE,AAAA,GAAG,CAAE,AAAA,IAAI,AAAE,CAClB,WAAW,CAAE,iDAAkD,CAClE",
"names": []
}
{"version":3,"file":"simple-fonts.css","sources":["simple-fonts.scss"],"sourcesContent":["body, h5, h6,\n.badge, .note, .grav-mdeditor-preview,\ninput, select, textarea, button, .selectize-input,\nh1, h2, h3, h4,\n#admin-menu li, .form-tabs > label, .label,\nbody .CodeMirror pre {\n font-family: \"Helvetica Neue\", \"Helvetica\", \"Tahoma\", \"Geneva\", \"Arial\", sans-serif;\n}\ncode, kbd, pre, samp {\n font-family: \"Monaco\", \"Consolas\", \"Lucida Console\", monospace;\n}\n\n"],"names":[],"mappings":"AAAA,AAAA,IAAI,CAAE,EAAE,CAAE,EAAE,CACZ,MAAM,CAAE,KAAK,CAAE,sBAAsB,CACrC,KAAK,CAAE,MAAM,CAAE,QAAQ,CAAE,MAAM,CAAE,gBAAgB,CACjD,EAAE,CAAE,EAAE,CAAE,EAAE,CAAE,EAAE,CACd,WAAW,CAAC,EAAE,CAAE,UAAU,CAAG,KAAK,CAAE,MAAM,CAC1C,IAAI,CAAC,WAAW,CAAC,GAAG,AAAC,CACjB,WAAW,CAAE,sEAAsE,CACtF,AACD,AAAA,IAAI,CAAE,GAAG,CAAE,GAAG,CAAE,IAAI,AAAE,CAClB,WAAW,CAAE,iDAAiD,CACjE"}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
View File
View File
File diff suppressed because one or more lines are too long
+80
View File
@@ -0,0 +1,80 @@
(function($) {
$(function() {
/**
* polyfill for html5 form attr
*/
// detect if browser supports this
var sampleElement = $('[form]').get(0);
var isIE11 = !(window.ActiveXObject) && "ActiveXObject" in window;
if (sampleElement && window.HTMLFormElement && sampleElement.form instanceof HTMLFormElement && !isIE11) {
// browser supports it, no need to fix
return;
}
/**
* Append a field to a form
*
*/
$.fn.appendField = function(data) {
// for form only
if (!this.is('form')) return;
// wrap data
if (!$.isArray(data) && data.name && data.value) {
data = [data];
}
var $form = this;
// attach new params
$.each(data, function(i, item) {
$('<input/>')
.attr('type', 'hidden')
.attr('name', item.name)
.val(item.value).appendTo($form);
});
return $form;
};
/**
* Find all input fields with form attribute point to jQuery object
*
*/
$('form[id]').submit(function(e) {
// serialize data
var data = $('[form=' + this.id + ']').serializeArray();
// append data to form
$(this).appendField(data);
}).each(function() {
var form = this,
$fields = $('[form=' + this.id + ']');
$fields.filter('button, input')
.filter('[type=reset],[type=submit],[type=image],button')
.click(function() {
var type = this.type.toLowerCase();
if (type === 'reset') {
// reset form
form.reset();
// for elements outside form
$fields.each(function() {
this.value = this.defaultValue;
this.checked = this.defaultChecked;
}).filter('select').each(function() {
$(this).find('option').each(function() {
this.selected = this.defaultSelected;
});
});
} else {
$(form).appendField({
name: this.name,
value: this.value
}).submit();
}
});
});
});
})(jQuery);
File diff suppressed because one or more lines are too long
+2 -1
View File
@@ -13,7 +13,6 @@
"author": "RocketTheme, LLC",
"license": "MIT",
"dependencies": {
"bootstrap": "^3.3.7",
"chartist": "0.11.0",
"codemirror": "^5.30.0",
"cookies-js": "^1.2.3",
@@ -22,11 +21,13 @@
"eonasdan-bootstrap-datetimepicker": "^4.17.47",
"es6-map": "^0.1.5",
"es6-promise": "^4.1.1",
"exif-js": "^2.3.0",
"gemini-scrollbar": "^1.5.2",
"immutable": "^3.8.1",
"immutablediff": "^0.4.4",
"js-yaml": "^3.10.0",
"mout": "^1.0.0",
"popper.js": "^1.14.4",
"rangetouch": "^1.0.5",
"remodal": "^1.1.1",
"selectize": "^0.12.4",
View File
@@ -251,7 +251,7 @@ tr {
&:focus, &:hover {
color: $button-text;
background-color: $button-bg;
background-color: darken($button-bg, 5%);
}
}
@@ -298,7 +298,7 @@ tr {
}
}
.instructions {
.instructions, p {
color: rgba($nav-text,0.8);
}
@@ -306,6 +306,10 @@ tr {
background: darken($nav-bg, 5%) url(../images/logo.svg) 50% 50% no-repeat;
}
h5 {
color: $nav-text;
}
form {
input {
background: lighten($nav-bg, 3%);
@@ -674,6 +678,10 @@ form {
input {
color: $form-field-text;
}
a.remove {
color: $page-bg !important;
}
}
}
}
@@ -909,16 +917,16 @@ form {
}
.form-fieldset {
background-color: #f7f7f7;
border: 2px solid #e1e1e1;
background-color: darken($content-bg, 1%);
border: 1px solid $form-border;
}
.form-fieldset--label {
background-color: #f3f3f3;
background-color: darken($content-bg, 4%);
&:hover,
.form-fieldset input:checked + & {
background-color: #eee;
background-color: darken($content-bg, 6%);
}
}
#admin-main {

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