diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c02c70e --- /dev/null +++ b/LICENSE @@ -0,0 +1,515 @@ + +CeCILL-B FREE SOFTWARE LICENSE AGREEMENT + + + Notice + +This Agreement is a Free Software license agreement that is the result +of discussions between its authors in order to ensure compliance with +the two main principles guiding its drafting: + + * firstly, compliance with the principles governing the distribution + of Free Software: access to source code, broad rights granted to + users, + * secondly, the election of a governing law, French law, with which + it is conformant, both as regards the law of torts and + intellectual property law, and the protection that it offers to + both authors and holders of the economic rights over software. + +The authors of the CeCILL-B (for Ce[a] C[nrs] I[nria] L[ogiciel] L[ibre]) +license are: + +Commissariat à l'Energie Atomique - CEA, a public scientific, technical +and industrial research establishment, having its principal place of +business at 25 rue Leblanc, immeuble Le Ponant D, 75015 Paris, France. + +Centre National de la Recherche Scientifique - CNRS, a public scientific +and technological establishment, having its principal place of business +at 3 rue Michel-Ange, 75794 Paris cedex 16, France. + +Institut National de Recherche en Informatique et en Automatique - +INRIA, a public scientific and technological establishment, having its +principal place of business at Domaine de Voluceau, Rocquencourt, BP +105, 78153 Le Chesnay cedex, France. + + + Preamble + +This Agreement is an open source software license intended to give users +significant freedom to modify and redistribute the software licensed +hereunder. + +The exercising of this freedom is conditional upon a strong obligation +of giving credits for everybody that distributes a software +incorporating a software ruled by the current license so as all +contributions to be properly identified and acknowledged. + +In consideration of access to the source code and the rights to copy, +modify and redistribute granted by the license, users are provided only +with a limited warranty and the software's author, the holder of the +economic rights, and the successive licensors only have limited liability. + +In this respect, the risks associated with loading, using, modifying +and/or developing or reproducing the software by the user are brought to +the user's attention, given its Free Software status, which may make it +complicated to use, with the result that its use is reserved for +developers and experienced professionals having in-depth computer +knowledge. Users are therefore encouraged to load and test the +suitability of the software as regards their requirements in conditions +enabling the security of their systems and/or data to be ensured and, +more generally, to use and operate it in the same conditions of +security. This Agreement may be freely reproduced and published, +provided it is not altered, and that no provisions are either added or +removed herefrom. + +This Agreement may apply to any or all software for which the holder of +the economic rights decides to submit the use thereof to its provisions. + + + Article 1 - DEFINITIONS + +For the purpose of this Agreement, when the following expressions +commence with a capital letter, they shall have the following meaning: + +Agreement: means this license agreement, and its possible subsequent +versions and annexes. + +Software: means the software in its Object Code and/or Source Code form +and, where applicable, its documentation, "as is" when the Licensee +accepts the Agreement. + +Initial Software: means the Software in its Source Code and possibly its +Object Code form and, where applicable, its documentation, "as is" when +it is first distributed under the terms and conditions of the Agreement. + +Modified Software: means the Software modified by at least one +Contribution. + +Source Code: means all the Software's instructions and program lines to +which access is required so as to modify the Software. + +Object Code: means the binary files originating from the compilation of +the Source Code. + +Holder: means the holder(s) of the economic rights over the Initial +Software. + +Licensee: means the Software user(s) having accepted the Agreement. + +Contributor: means a Licensee having made at least one Contribution. + +Licensor: means the Holder, or any other individual or legal entity, who +distributes the Software under the Agreement. + +Contribution: means any or all modifications, corrections, translations, +adaptations and/or new functions integrated into the Software by any or +all Contributors, as well as any or all Internal Modules. + +Module: means a set of sources files including their documentation that +enables supplementary functions or services in addition to those offered +by the Software. + +External Module: means any or all Modules, not derived from the +Software, so that this Module and the Software run in separate address +spaces, with one calling the other when they are run. + +Internal Module: means any or all Module, connected to the Software so +that they both execute in the same address space. + +Parties: mean both the Licensee and the Licensor. + +These expressions may be used both in singular and plural form. + + + Article 2 - PURPOSE + +The purpose of the Agreement is the grant by the Licensor to the +Licensee of a non-exclusive, transferable and worldwide license for the +Software as set forth in Article 5 hereinafter for the whole term of the +protection granted by the rights over said Software. + + + Article 3 - ACCEPTANCE + +3.1 The Licensee shall be deemed as having accepted the terms and +conditions of this Agreement upon the occurrence of the first of the +following events: + + * (i) loading the Software by any or all means, notably, by + downloading from a remote server, or by loading from a physical + medium; + * (ii) the first time the Licensee exercises any of the rights + granted hereunder. + +3.2 One copy of the Agreement, containing a notice relating to the +characteristics of the Software, to the limited warranty, and to the +fact that its use is restricted to experienced users has been provided +to the Licensee prior to its acceptance as set forth in Article 3.1 +hereinabove, and the Licensee hereby acknowledges that it has read and +understood it. + + + Article 4 - EFFECTIVE DATE AND TERM + + + 4.1 EFFECTIVE DATE + +The Agreement shall become effective on the date when it is accepted by +the Licensee as set forth in Article 3.1. + + + 4.2 TERM + +The Agreement shall remain in force for the entire legal term of +protection of the economic rights over the Software. + + + Article 5 - SCOPE OF RIGHTS GRANTED + +The Licensor hereby grants to the Licensee, who accepts, the following +rights over the Software for any or all use, and for the term of the +Agreement, on the basis of the terms and conditions set forth hereinafter. + +Besides, if the Licensor owns or comes to own one or more patents +protecting all or part of the functions of the Software or of its +components, the Licensor undertakes not to enforce the rights granted by +these patents against successive Licensees using, exploiting or +modifying the Software. If these patents are transferred, the Licensor +undertakes to have the transferees subscribe to the obligations set +forth in this paragraph. + + + 5.1 RIGHT OF USE + +The Licensee is authorized to use the Software, without any limitation +as to its fields of application, with it being hereinafter specified +that this comprises: + + 1. permanent or temporary reproduction of all or part of the Software + by any or all means and in any or all form. + + 2. loading, displaying, running, or storing the Software on any or + all medium. + + 3. entitlement to observe, study or test its operation so as to + determine the ideas and principles behind any or all constituent + elements of said Software. This shall apply when the Licensee + carries out any or all loading, displaying, running, transmission + or storage operation as regards the Software, that it is entitled + to carry out hereunder. + + + 5.2 ENTITLEMENT TO MAKE CONTRIBUTIONS + +The right to make Contributions includes the right to translate, adapt, +arrange, or make any or all modifications to the Software, and the right +to reproduce the resulting software. + +The Licensee is authorized to make any or all Contributions to the +Software provided that it includes an explicit notice that it is the +author of said Contribution and indicates the date of the creation thereof. + + + 5.3 RIGHT OF DISTRIBUTION + +In particular, the right of distribution includes the right to publish, +transmit and communicate the Software to the general public on any or +all medium, and by any or all means, and the right to market, either in +consideration of a fee, or free of charge, one or more copies of the +Software by any means. + +The Licensee is further authorized to distribute copies of the modified +or unmodified Software to third parties according to the terms and +conditions set forth hereinafter. + + + 5.3.1 DISTRIBUTION OF SOFTWARE WITHOUT MODIFICATION + +The Licensee is authorized to distribute true copies of the Software in +Source Code or Object Code form, provided that said distribution +complies with all the provisions of the Agreement and is accompanied by: + + 1. a copy of the Agreement, + + 2. a notice relating to the limitation of both the Licensor's + warranty and liability as set forth in Articles 8 and 9, + +and that, in the event that only the Object Code of the Software is +redistributed, the Licensee allows effective access to the full Source +Code of the Software at a minimum during the entire period of its +distribution of the Software, it being understood that the additional +cost of acquiring the Source Code shall not exceed the cost of +transferring the data. + + + 5.3.2 DISTRIBUTION OF MODIFIED SOFTWARE + +If the Licensee makes any Contribution to the Software, the resulting +Modified Software may be distributed under a license agreement other +than this Agreement subject to compliance with the provisions of Article +5.3.4. + + + 5.3.3 DISTRIBUTION OF EXTERNAL MODULES + +When the Licensee has developed an External Module, the terms and +conditions of this Agreement do not apply to said External Module, that +may be distributed under a separate license agreement. + + + 5.3.4 CREDITS + +Any Licensee who may distribute a Modified Software hereby expressly +agrees to: + + 1. indicate in the related documentation that it is based on the + Software licensed hereunder, and reproduce the intellectual + property notice for the Software, + + 2. ensure that written indications of the Software intended use, + intellectual property notice and license hereunder are included in + easily accessible format from the Modified Software interface, + + 3. mention, on a freely accessible website describing the Modified + Software, at least throughout the distribution term thereof, that + it is based on the Software licensed hereunder, and reproduce the + Software intellectual property notice, + + 4. where it is distributed to a third party that may distribute a + Modified Software without having to make its source code + available, make its best efforts to ensure that said third party + agrees to comply with the obligations set forth in this Article . + +If the Software, whether or not modified, is distributed with an +External Module designed for use in connection with the Software, the +Licensee shall submit said External Module to the foregoing obligations. + + + 5.3.5 COMPATIBILITY WITH THE CeCILL AND CeCILL-C LICENSES + +Where a Modified Software contains a Contribution subject to the CeCILL +license, the provisions set forth in Article 5.3.4 shall be optional. + +A Modified Software may be distributed under the CeCILL-C license. In +such a case the provisions set forth in Article 5.3.4 shall be optional. + + + Article 6 - INTELLECTUAL PROPERTY + + + 6.1 OVER THE INITIAL SOFTWARE + +The Holder owns the economic rights over the Initial Software. Any or +all use of the Initial Software is subject to compliance with the terms +and conditions under which the Holder has elected to distribute its work +and no one shall be entitled to modify the terms and conditions for the +distribution of said Initial Software. + +The Holder undertakes that the Initial Software will remain ruled at +least by this Agreement, for the duration set forth in Article 4.2. + + + 6.2 OVER THE CONTRIBUTIONS + +The Licensee who develops a Contribution is the owner of the +intellectual property rights over this Contribution as defined by +applicable law. + + + 6.3 OVER THE EXTERNAL MODULES + +The Licensee who develops an External Module is the owner of the +intellectual property rights over this External Module as defined by +applicable law and is free to choose the type of agreement that shall +govern its distribution. + + + 6.4 JOINT PROVISIONS + +The Licensee expressly undertakes: + + 1. not to remove, or modify, in any manner, the intellectual property + notices attached to the Software; + + 2. to reproduce said notices, in an identical manner, in the copies + of the Software modified or not. + +The Licensee undertakes not to directly or indirectly infringe the +intellectual property rights of the Holder and/or Contributors on the +Software and to take, where applicable, vis-à-vis its staff, any and all +measures required to ensure respect of said intellectual property rights +of the Holder and/or Contributors. + + + Article 7 - RELATED SERVICES + +7.1 Under no circumstances shall the Agreement oblige the Licensor to +provide technical assistance or maintenance services for the Software. + +However, the Licensor is entitled to offer this type of services. The +terms and conditions of such technical assistance, and/or such +maintenance, shall be set forth in a separate instrument. Only the +Licensor offering said maintenance and/or technical assistance services +shall incur liability therefor. + +7.2 Similarly, any Licensor is entitled to offer to its licensees, under +its sole responsibility, a warranty, that shall only be binding upon +itself, for the redistribution of the Software and/or the Modified +Software, under terms and conditions that it is free to decide. Said +warranty, and the financial terms and conditions of its application, +shall be subject of a separate instrument executed between the Licensor +and the Licensee. + + + Article 8 - LIABILITY + +8.1 Subject to the provisions of Article 8.2, the Licensee shall be +entitled to claim compensation for any direct loss it may have suffered +from the Software as a result of a fault on the part of the relevant +Licensor, subject to providing evidence thereof. + +8.2 The Licensor's liability is limited to the commitments made under +this Agreement and shall not be incurred as a result of in particular: +(i) loss due the Licensee's total or partial failure to fulfill its +obligations, (ii) direct or consequential loss that is suffered by the +Licensee due to the use or performance of the Software, and (iii) more +generally, any consequential loss. In particular the Parties expressly +agree that any or all pecuniary or business loss (i.e. loss of data, +loss of profits, operating loss, loss of customers or orders, +opportunity cost, any disturbance to business activities) or any or all +legal proceedings instituted against the Licensee by a third party, +shall constitute consequential loss and shall not provide entitlement to +any or all compensation from the Licensor. + + + Article 9 - WARRANTY + +9.1 The Licensee acknowledges that the scientific and technical +state-of-the-art when the Software was distributed did not enable all +possible uses to be tested and verified, nor for the presence of +possible defects to be detected. In this respect, the Licensee's +attention has been drawn to the risks associated with loading, using, +modifying and/or developing and reproducing the Software which are +reserved for experienced users. + +The Licensee shall be responsible for verifying, by any or all means, +the suitability of the product for its requirements, its good working +order, and for ensuring that it shall not cause damage to either persons +or properties. + +9.2 The Licensor hereby represents, in good faith, that it is entitled +to grant all the rights over the Software (including in particular the +rights set forth in Article 5). + +9.3 The Licensee acknowledges that the Software is supplied "as is" by +the Licensor without any other express or tacit warranty, other than +that provided for in Article 9.2 and, in particular, without any warranty +as to its commercial value, its secured, safe, innovative or relevant +nature. + +Specifically, the Licensor does not warrant that the Software is free +from any error, that it will operate without interruption, that it will +be compatible with the Licensee's own equipment and software +configuration, nor that it will meet the Licensee's requirements. + +9.4 The Licensor does not either expressly or tacitly warrant that the +Software does not infringe any third party intellectual property right +relating to a patent, software or any other property right. Therefore, +the Licensor disclaims any and all liability towards the Licensee +arising out of any or all proceedings for infringement that may be +instituted in respect of the use, modification and redistribution of the +Software. Nevertheless, should such proceedings be instituted against +the Licensee, the Licensor shall provide it with technical and legal +assistance for its defense. Such technical and legal assistance shall be +decided on a case-by-case basis between the relevant Licensor and the +Licensee pursuant to a memorandum of understanding. The Licensor +disclaims any and all liability as regards the Licensee's use of the +name of the Software. No warranty is given as regards the existence of +prior rights over the name of the Software or as regards the existence +of a trademark. + + + Article 10 - TERMINATION + +10.1 In the event of a breach by the Licensee of its obligations +hereunder, the Licensor may automatically terminate this Agreement +thirty (30) days after notice has been sent to the Licensee and has +remained ineffective. + +10.2 A Licensee whose Agreement is terminated shall no longer be +authorized to use, modify or distribute the Software. However, any +licenses that it may have granted prior to termination of the Agreement +shall remain valid subject to their having been granted in compliance +with the terms and conditions hereof. + + + Article 11 - MISCELLANEOUS + + + 11.1 EXCUSABLE EVENTS + +Neither Party shall be liable for any or all delay, or failure to +perform the Agreement, that may be attributable to an event of force +majeure, an act of God or an outside cause, such as defective +functioning or interruptions of the electricity or telecommunications +networks, network paralysis following a virus attack, intervention by +government authorities, natural disasters, water damage, earthquakes, +fire, explosions, strikes and labor unrest, war, etc. + +11.2 Any failure by either Party, on one or more occasions, to invoke +one or more of the provisions hereof, shall under no circumstances be +interpreted as being a waiver by the interested Party of its right to +invoke said provision(s) subsequently. + +11.3 The Agreement cancels and replaces any or all previous agreements, +whether written or oral, between the Parties and having the same +purpose, and constitutes the entirety of the agreement between said +Parties concerning said purpose. No supplement or modification to the +terms and conditions hereof shall be effective as between the Parties +unless it is made in writing and signed by their duly authorized +representatives. + +11.4 In the event that one or more of the provisions hereof were to +conflict with a current or future applicable act or legislative text, +said act or legislative text shall prevail, and the Parties shall make +the necessary amendments so as to comply with said act or legislative +text. All other provisions shall remain effective. Similarly, invalidity +of a provision of the Agreement, for any reason whatsoever, shall not +cause the Agreement as a whole to be invalid. + + + 11.5 LANGUAGE + +The Agreement is drafted in both French and English and both versions +are deemed authentic. + + + Article 12 - NEW VERSIONS OF THE AGREEMENT + +12.1 Any person is authorized to duplicate and distribute copies of this +Agreement. + +12.2 So as to ensure coherence, the wording of this Agreement is +protected and may only be modified by the authors of the License, who +reserve the right to periodically publish updates or new versions of the +Agreement, each with a separate number. These subsequent versions may +address new issues encountered by Free Software. + +12.3 Any Software distributed under a given version of the Agreement may +only be subsequently distributed under the same version of the Agreement +or a subsequent version. + + + Article 13 - GOVERNING LAW AND JURISDICTION + +13.1 The Agreement is governed by French law. The Parties agree to +endeavor to seek an amicable solution to any disagreements or disputes +that may arise during the performance of the Agreement. + +13.2 Failing an amicable solution within two (2) months as from their +occurrence, and unless emergency proceedings are necessary, the +disagreements or disputes shall be referred to the Paris Courts having +jurisdiction, by the more diligent Party. + + +Version 1.0 dated 2006-09-05. diff --git a/README.md b/README.md index 0bfbbe3..388502e 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,41 @@ -# KazV2 +# kaz -Ensemble des services de KAZ \ No newline at end of file +[Kaz](https://kaz.bzh/) est un CHATONS du Morbihan. Nous proposons ici un moyen de le répliquer dans d'autres lieux. Il y a des éléments de configuration à définir avant d'initialiser ce simulateur. + + +Il est possible de simuler notre CHATONS dans une VirtualBox pour mettre au point nos différents services (voir [kaz-vagrant](https://git.kaz.bzh/KAZ/kaz-vagrant)). + +## Pré-requis + +Dans la suite, on imagine que vous disposer d'une machine sous Linux (par exemple dernière version de [Debian](https://fr.wikipedia.org/wiki/Debian)) avec un minimum de paquets. + +```bash +DEBIAN_FRONTEND=noninteractive apt-get -y upgrade +DEBIAN_FRONTEND=noninteractive apt-get -y dist-upgrade +DEBIAN_FRONTEND=noninteractive apt-get install -y apg curl git unzip rsync net-tools libnss3-tools +DEBIAN_FRONTEND=noninteractive apt-get remove --purge -y exim4-base exim4-config exim4-daemon-light +``` + +## Installation + +* Télécharger le dépôt kaz-vagrant ou utilisez la commande : +```bash +git clone https://git.kaz.bzh/KAZ/kaz.git # pour essayer +git clone git+ssh://git@git.kaz.bzh:2202/KAZ/kaz.git # pour contribuer +cd kaz/ +``` +* Personalisez votre simulateur avec la commande (au besoin ajustez la mémoire et les cpus utilisés dans Vagrantfile) : +```bash +./bin/init.sh +``` + +Pour terminer la création et tous les dockers, il faut lancer la commande +```bash +./bin/install.sh web jirafeau ethercalc etherpad postfix roundcube proxy framadate paheko dokuwiki > >(tee stdout.log) 2> >(tee stderr.log >&2) +``` + +* Une fois la commande de création de l'univers, il faut patienter... + +* Après il faut patienter encore un peu si on a pas la fibre ... + +## Utilisation diff --git a/bin/.applyTemplate-completion.bash b/bin/.applyTemplate-completion.bash new file mode 100755 index 0000000..e3c1097 --- /dev/null +++ b/bin/.applyTemplate-completion.bash @@ -0,0 +1,30 @@ +#/usr/bin/env bash + +_applyTemplate_completions () { + declare -a options + OPTIONS=("-help" "-timestamp") + COMPREPLY=() + + local CUR OPTIONS_COUNT=0 + CUR=${COMP_WORDS[COMP_CWORD]} + + # compte les options déjà utilisé et les retire de la liste des possible + for ITEM in ${COMP_WORDS[@]:1} + do + if [[ " ${OPTIONS[*]} " =~ " ${ITEM} " ]] ; then + let "OPTIONS_COUNT++" + OPTIONS=(${OPTIONS[@]/${ITEM}}) + else + break + fi + done + + # si la position est dans des options "ou juste après" + ((COMP_CWORD <= OPTIONS_COUNT+1)) && [[ "${CUR}" =~ ^- ]] && COMPREPLY=( $(compgen -W "${OPTIONS[*]}" -- "${CUR}" ) ) && return 0 + + # si la position est après des options ou que l'on ne commence pas par "-" on cherche des fichiers + ((COMP_CWORD <= OPTIONS_COUNT+2)) && COMPREPLY=($(compgen -f -- "${CUR}")) && return 0 + + return 0 +} +complete -F _applyTemplate_completions applyTemplate.sh diff --git a/bin/.commonFunctions.sh b/bin/.commonFunctions.sh new file mode 100755 index 0000000..0e5c6c0 --- /dev/null +++ b/bin/.commonFunctions.sh @@ -0,0 +1,382 @@ +# commun fonctions for KAZ + +#TODO; toutes les fonctions ci-dessous devraient être commentées + +#KI : françois +#KOI : tout un tas de trucs utiles pour la gestion de l'infra kaz (à mettre dans chaque script) +#KAN : +# maj le 27/01/2024 by FAB: recherche de tous les srv kaz dispo (via le DNS) +# maj le 15/04/2024 by FAB: correction getPahekoOrgaList + +# https://wiki.bash-hackers.org/scripting/terminalcodes +BOLD='' +RED='' +GREEN='' +YELLOW='' +BLUE='' +MAGENTA='' +CYAN='' +NC='' # No Color +NL=' +' + +######################################## +setKazVars () { + # KAZ_ROOT must be set + if [ -z "${KAZ_ROOT}" ]; then + printKazError "\n\n *** KAZ_ROOT not defined! ***\n" + exit + fi + export KAZ_KEY_DIR="${KAZ_ROOT}/secret" + export KAZ_BIN_DIR="${KAZ_ROOT}/bin" + export KAZ_CONF_DIR="${KAZ_ROOT}/config" + export KAZ_CONF_PROXY_DIR="${KAZ_CONF_DIR}/proxy" + export KAZ_COMP_DIR="${KAZ_ROOT}/dockers" + export KAZ_STATE_DIR="${KAZ_ROOT}/state" + + export KAZ_GIT_DIR="${KAZ_ROOT}/git" + export KAZ_DNLD_DIR="${KAZ_ROOT}/download" + export KAZ_DNLD_PAHEKO_DIR="${KAZ_DNLD_DIR}/paheko" + + export APPLY_TMPL=${KAZ_BIN_DIR}/applyTemplate.sh + export DOCKERS_ENV="${KAZ_CONF_DIR}/dockers.env" + + export DOCK_LIB="/var/lib/docker" + export DOCK_VOL="${DOCK_LIB}/volumes" + export DOCK_VOL_PAHEKO_ORGA="${DOCK_LIB}/volumes/paheko_assoUsers/_data/" + + export NAS_VOL="/mnt/disk-nas1/docker/volumes/" +} + +######################################## + +printKazMsg () { + # $1 msg + echo -e "${CYAN}${BOLD}$1${NC}" +} + +printKazError () { + # $1 msb + echo -e "${RED}${BOLD}$1${NC}" +} + +######################################## +checkContinue () { + local rep + while : ; do + read -p "Do you want to continue? [yes]" rep + case "${rep}" in + ""|[yYoO]* ) + break + ;; + [Nn]* ) + exit + ;; + * ) + echo "Please answer yes or no." + ;; + esac + done +} + +checkDockerRunning () { + # $1 docker name + # $2 service name + if ! [[ "$(docker ps -f "name=$1" | grep -w "$1")" ]]; then + printKazError "$2 not running... abort" + return 1 + fi + return 0 +} + +######################################## +testValidIp () { + # $1 ip + local ip=$1 + local stat=1 + + if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then + OIFS=$IFS + IFS='.' + ip=($ip) + IFS=$OIFS + [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] + stat=$? + fi + return $stat +} + +######################################## +getValInFile () { + # $1 filename + # $2 varname + grep "^\s*$2\s*=" $1 2>/dev/null | head -1 | sed "s%^\s*$2\s*=\(.*\)$%\1%" +} + +getList () { + # $1 filename + (cat "$1"|sort; echo) | sed -e "s/\(.*\)[ \t]*#.*$/\1/" -e "s/^[ \t]*\(.*\)$/\1/" -e "/^$/d" +} + +getPahekoPluginList () { + ls "${KAZ_DNLD_PAHEKO_DIR}" | grep -v "paheko-" +} + +getPahekoOrgaList () { +# ls "${DOCK_VOL_PAHEKO_ORGA}" + find ${DOCK_VOL_PAHEKO_ORGA} -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | sort +} + +getAvailableComposes () { + ls "${KAZ_COMP_DIR}" | grep -v -- "^.*-orga$" +} + +getAvailableOrgas () { + +#KI : Fab +#KOI : donne la liste de toutes les orgas pour un serveur donné sinon serveur courant +#KAN : 27/01/2024 + +#en entrée +SITE_DST="$1" + + if [ -n "${SITE_DST}" ];then + ssh -p 2201 root@${SITE_DST}.${domain} "ls \"${KAZ_COMP_DIR}\" | grep -- \"^.*-orga$\"" + else + ls "${KAZ_COMP_DIR}" | grep -- "^.*-orga$" + fi + +} + +getAvailableServices () { + local service + for service in paheko cloud collabora agora wiki wp; do + echo "${service}" + done +} + +######################################## +filterInList () { + # $* ref list filter + # stdin candidats + local compose + while read compose ; do + if [[ " $* " =~ " ${compose} " ]]; then + echo ${compose} + fi + done | sort -u +} + +filterNotInList () { + # $* ref list filter + # stdin candidats + local compose + while read compose ; do + if [[ ! " $* " =~ " ${compose} " ]]; then + echo ${compose} + fi + done | sort -u +} + +filterAvailableComposes () { + # $* candidats + local AVAILABLE_COMPOSES=$(getAvailableComposes;getAvailableOrgas) + if [ $# -eq 0 ] ; then + echo ${AVAILABLE_COMPOSES} + fi + local compose + for compose in $* + do + compose=${compose%/} + if [[ ! "${NL}${AVAILABLE_COMPOSES}${NL}" =~ "${NL}${compose}${NL}" ]]; then + local subst="" + for item in ${AVAILABLE_COMPOSES}; do + [[ "${item}" =~ "${compose}" ]] && echo ${item} && subst="${subst} ${item}" + done + if [ -z "${subst}" ] ; then + echo "${RED}${BOLD}Unknown compose: ${compose} not in "${AVAILABLE_COMPOSES}"${NC}" >&2 + #exit 1 + else + echo "${BLUE}${BOLD}substitute compose: ${compose} => "${subst}"${NC}" >&2 + fi + else + echo "${compose}" + fi + done | sort -u +} + +######################################## +serviceOnInOrga () { + # $1 orga name + # $2 service name + # default value + local composeFile="${KAZ_COMP_DIR}/$1-orga/docker-compose.yml" + if [[ ! -f "${composeFile}" ]] + then + echo "$3" + else + grep -q "$2" "${composeFile}" 2>/dev/null && echo on || echo off + fi +} + +######################################## +waitUrl () { + # $1 URL to waitfor + # $2 timeout en secondes (optional) + starttime=$(date +%s) + if [[ $(curl --connect-timeout 2 -s -D - "$1" -o /dev/null 2>/dev/null | head -n1) != *[23]0[0-9]* ]]; then + printKazMsg "service not available ($1). Please wait..." + echo curl --connect-timeout 2 -s -D - "$1" -o /dev/null \| head -n1 + while [[ $(curl --connect-timeout 2 -s -D - "$1" -o /dev/null 2>/dev/null | head -n1) != *[23]0[0-9]* ]] + do + sleep 5 + if [ $# -gt 1 ]; then + actualtime=$(date +%s) + delta=$(($actualtime-$starttime)) + [[ $2 -lt $delta ]] && return 1 + fi + done + fi + return 0 +} + +######################################## +waitContainerHealthy () { + # $1 ContainerName + # $2 timeout en secondes (optional) + + healthy="false" + starttime=$(date +%s) + running="false" + [[ $(docker ps -f name="$1" | grep -w "$1") ]] && running="true" + [[ $running == "true" && $(docker inspect -f {{.State.Health.Status}} "$1") == "healthy" ]] && healthy="true" + if [[ ! $running == "true" || ! $healthy == "true" ]]; then + printKazMsg "Docker not healthy ($1). Please wait..." + while [[ ! $running == "true" || ! $healthy == "true" ]] + do + sleep 5 + if [ $# -gt 1 ]; then + actualtime=$(date +%s) + delta=$(($actualtime-$starttime)) + [[ $2 -lt $delta ]] && printKazMsg "Docker not healthy ($1)... abort..." && return 1 + fi + [[ ! $running == "true" ]] && [[ $(docker ps -f name="$1" | grep -w "$1") ]] && running="true" + [[ $running == "true" && $(docker inspect -f {{.State.Health.Status}} "$1") == "healthy" ]] && healthy="true" + done + fi + return 0 +} +######################################## +waitContainerRunning () { + # $1 ContainerName + # $2 timeout en secondes (optional) + + starttime=$(date +%s) + running="false" + [[ $(docker ps -f name="$1" | grep -w "$1") ]] && running="true" + if [[ ! $running == "true" ]]; then + printKazMsg "Docker not running ($1). Please wait..." + while [[ ! $running == "true" ]] + do + sleep 5 + if [ $# -gt 1 ]; then + actualtime=$(date +%s) + delta=$(($actualtime-$starttime)) + [[ $2 -lt $delta ]] && printKazMsg "Docker did not start ($1)... abort..." && return 1 + fi + [[ ! $running == "true" ]] && [[ $(docker ps -f name="$1" | grep -w "$1") ]] && running="true" + done + fi + return 0 +} + +######################################## +downloadFile () { + # $1 URL to download + # $2 new filename (optional) + if [ $# -lt 1 ] || [ $# -gt 2 ]; then + printKazError "downloadFile: bad arg number" + return + fi + URL=$1 + if [ -z "$2" ]; then + FILENAME="$(basename $1)" + else + FILENAME="$2" + fi + + if [ ! -f "${FILENAME}" ]; then + printKazMsg " - load ${URL}" + curl -L -o "${FILENAME}" "${URL}" + else + TMP="${FILENAME}.tmp" + rm -f "${TMP}" + curl -L -o "${TMP}" "${URL}" + if ! cmp -s "${TMP}" "${FILENAME}" 2>/dev/null; then + mv "${TMP}" "${FILENAME}" + else + rm -f "${TMP}" + fi + fi +} + +unzipInDir () { + # $1 zipfile + # $2 destDir + + if [ $# -ne 2 ]; then + printKazError "unzipInDir: bad arg number" + return + fi + if ! [[ $1 == *.zip ]]; then + printKazError "unzipInDir: $1 is not a zip file" + return + fi + if ! [[ -d $2 ]]; then + printKazError "$2 is not destination dir" + return + fi + + destName="$2/$(basename "${1%.zip}")" + if [[ -d "${destName}" ]]; then + printKazError "${destName} already exist" + return + fi + + tmpDir=$2/tmp-$$ + trap 'rm -rf "${tmpDir}"' EXIT + unzip "$1" -d "${tmpDir}" + srcDir=$(ls -1 "${tmpDir}") + case $(wc -l <<< $srcDir) in + 0) + printKazError "empty zip file : $1" + rmdir "${tmpDir}" + return + ;; + 1) + mv "${tmpDir}/${srcDir}" "${destName}" + rmdir "${tmpDir}" + ;; + *) + printKazError "zip file $1 is not a tree (${srcDir})" + return + ;; + esac +} +######################################## + +get_Serveurs_Kaz () { + +#KI : Fab +#KOI : donne la liste de tous les serveurs kaz sous le format srv1;srv2;srv3;.... en intérogeant le DNS +#KAN : 27/01/2024 + + liste=`dig -t TXT srv.kaz.bzh +short` + #on nettoie + liste=$(echo "$liste" | sed 's/\;/ /g') + liste=$(echo "$liste" | sed 's/\"//g') + #renvoi srv1 srv2 srv3 .... + echo ${liste} +} +######################################## + diff --git a/bin/.container-completion.bash b/bin/.container-completion.bash new file mode 100755 index 0000000..6871a4d --- /dev/null +++ b/bin/.container-completion.bash @@ -0,0 +1,66 @@ +#/usr/bin/env bash + +_container_completions () { + KAZ_ROOT=$(cd "$(dirname ${COMP_WORDS[0]})"/..; pwd) + COMPREPLY=() + . "${KAZ_ROOT}/bin/.commonFunctions.sh" + setKazVars + + local cword=${COMP_CWORD} cur=${COMP_WORDS[COMP_CWORD]} card=${#COMP_WORDS[@]} i w skip=0 + for ((i=1 ; i/dev/null) + COMPREPLY=($(compgen -W "${available_orga}" -- "${cur}")) + ;; + esac + return 0 +} +complete -F _gestContainers_completion gestContainers.sh diff --git a/bin/.kazDockerNet-completion.bash b/bin/.kazDockerNet-completion.bash new file mode 100755 index 0000000..c309cbd --- /dev/null +++ b/bin/.kazDockerNet-completion.bash @@ -0,0 +1,51 @@ +#/usr/bin/env bash + +_kazDockerNet_completion () { + KAZ_ROOT=$(cd "$(dirname ${COMP_WORDS[0]})"/..; pwd) + COMPREPLY=() + . "${KAZ_ROOT}/bin/.commonFunctions.sh" + setKazVars + + local cword=${COMP_CWORD} cur=${COMP_WORDS[COMP_CWORD]} card=${#COMP_WORDS[@]} i w skip=0 + for ((i=1 ; i/dev/null) + local used=$("${KAZ_BIN_DIR}/kazDockerNet.sh" "list" | grep "name:" | sed -e "s%\bname:\s*%%" -e "s%\bbridge\b\s*%%" -e "s%Net\b%%g") + local proposal item + for item in ${available_args} ; do + [[ " ${names} " =~ " ${item} " ]] || [[ " ${used} " =~ " ${item} " ]] || proposal="${proposal} ${item}" + done + COMPREPLY=($(compgen -W "${proposal}" -- "${cur}")) + ;; + esac + ;; + esac + return 0 +} +complete -F _kazDockerNet_completion kazDockerNet.sh diff --git a/bin/.kazList-completion.bash b/bin/.kazList-completion.bash new file mode 100755 index 0000000..ffc1530 --- /dev/null +++ b/bin/.kazList-completion.bash @@ -0,0 +1,83 @@ +#/usr/bin/env bash + +_kazList_completions () { + #KAZ_ROOT=$(cd "$(dirname ${COMP_WORDS[0]})"/..; pwd) + COMPREPLY=() + local cword=${COMP_CWORD} cur=${COMP_WORDS[COMP_CWORD]} card=${#COMP_WORDS[@]} i w skip=0 + for ((i=1 ; i> /dev/pts/1) + + case "${cur}" in + -*) + COMPREPLY=($(compgen -W "-h --help" -- "${cur}")) + ;; + *) + local cmd_available="compose service" + local opt_available="available validate enable disable status" + case "${arg_pos}" in + 1) + # $1 of kazList.sh + COMPREPLY=($(compgen -W "${cmd_available}" -- "${cur}")) + ;; + 2) + # $2 of kazList.sh + COMPREPLY=($(compgen -W "${opt_available}" -- "${cur}")) + ;; + *) + # $3-* of kazList.sh + [[ " ${cmd_available} " =~ " ${cmd} " ]] || return 0 + # select set of names + local names_set="${opt}" + local available_args + case "${cmd}" in + service) + case "${names_set}" in + available|validate) + return 0 + ;; + *) + available_args=$("${COMP_WORDS[0]}" "compose" "enable" "orga" 2>/dev/null) + ;; + esac + ;; + compose) + case "${names_set}" in + validate|enable|disable) + ;; + *) + names_set="available" + ;; + esac + available_args=$("${COMP_WORDS[0]}" "${cmd}" "${names_set}") + ;; + esac + # remove previous selected target + local proposal item + for item in ${available_args} ; do + [[ " ${names} " =~ " ${item} " ]] || proposal="${proposal} ${item}" + done + COMPREPLY=($(compgen -W "${proposal}" -- "${cur}")) + ;; + esac + esac + return 0 +} + +complete -F _kazList_completions kazList.sh diff --git a/bin/.mvOrga2Nas-completion.bash b/bin/.mvOrga2Nas-completion.bash new file mode 100755 index 0000000..d961e25 --- /dev/null +++ b/bin/.mvOrga2Nas-completion.bash @@ -0,0 +1,39 @@ +#/usr/bin/env bash + +_mv_orga_nas_completion () { + KAZ_ROOT=$(cd "$(dirname ${COMP_WORDS[0]})"/..; pwd) + COMPREPLY=() + . "${KAZ_ROOT}/bin/.commonFunctions.sh" + setKazVars + local cword=${COMP_CWORD} cur=${COMP_WORDS[COMP_CWORD]} card=${#COMP_WORDS[@]} i w skip=0 + for ((i=1 ; i/dev/null | sed "s/-orga\b//g") + local proposal= item + for item in ${available_orga} ; do + [[ " ${names} " =~ " ${item} " ]] || proposal="${proposal} ${item}" + done + COMPREPLY=($(compgen -W "${proposal}" -- "${cur}")) + ;; + esac + return 0 +} +complete -F _mv_orga_nas_completion mvOrga2Nas.sh diff --git a/bin/.orga-gen-completion.bash b/bin/.orga-gen-completion.bash new file mode 100755 index 0000000..9d611bb --- /dev/null +++ b/bin/.orga-gen-completion.bash @@ -0,0 +1,63 @@ +#/usr/bin/env bash + +_orga_gen_completion () { + KAZ_ROOT=$(cd "$(dirname ${COMP_WORDS[0]})"/../..; pwd) + ORGA_DIR=$(cd "$(dirname ${COMP_WORDS[0]})"; basename $(pwd)) + COMPREPLY=() + . "${KAZ_ROOT}/bin/.commonFunctions.sh" + setKazVars + local cword=${COMP_CWORD} cur=${COMP_WORDS[COMP_CWORD]} card=${#COMP_WORDS[@]} i w skip=0 + for ((i=1 ; i/dev/null | tr ' ' '\n' | sed "s/\(..*\)/-\1/") + for item in ${available_services} ; do + [[ " ${rmOpt} " =~ " ${item} " ]] || proposal="${proposal} ${item}" + done + COMPREPLY=( $(compgen -W "${proposal}" -- "${cur}" ) ) + ;; + '+'*) + local available_services item proposal= listOpt="available" + [ -n "${names}" ] && listOpt="disable ${names}" + [[ "${ORGA_DIR}" = "orgaTmpl" ]] || listOpt="disable ${ORGA_DIR%-orga}" + available_services=$("${KAZ_LIST}" service ${listOpt} 2>/dev/null | tr ' ' '\n' | sed "s/\(..*\)/+\1/") + for item in ${available_services} ; do + [[ " ${addOpt} " =~ " ${item} " ]] || proposal="${proposal} ${item}" + done + COMPREPLY=( $(compgen -W "${proposal}" -- "${cur}" ) ) + ;; + *) + [[ "${ORGA_DIR}" = "orgaTmpl" ]] || return 0; + local available_orga=$("${KAZ_LIST}" "compose" "enable" "orga" 2>/dev/null | sed "s/-orga\b//g") + local proposal= item + for item in ${available_orga} ; do + [[ " ${names} " =~ " ${item} " ]] || proposal="${proposal} ${item}" + done + COMPREPLY=($(compgen -W "${proposal}" -- "${cur}")) + ;; + esac + return 0 +} +complete -F _orga_gen_completion orga-gen.sh diff --git a/bin/.updateLook-completion.bash b/bin/.updateLook-completion.bash new file mode 100755 index 0000000..ef0bfcd --- /dev/null +++ b/bin/.updateLook-completion.bash @@ -0,0 +1,11 @@ +#/usr/bin/env bash + +_update_look_nas_completion () { + COMPREPLY=() + local cur=${COMP_WORDS[COMP_CWORD]} + local THEMES=$(cd "$(dirname ${COMP_WORDS[0]})"/look ; ls -F -- '.' | grep '/$' | sed 's%/%%' | tr '\n' ' ' | sed 's% $%%') + + COMPREPLY=($(compgen -W "${THEMES}" -- "${cur}")) + return 0 +} +complete -F _update_look_nas_completion updateLook.sh diff --git a/bin/applyTemplate.sh b/bin/applyTemplate.sh new file mode 100755 index 0000000..9a7f145 --- /dev/null +++ b/bin/applyTemplate.sh @@ -0,0 +1,102 @@ +#!/bin/bash + +# Met à jour la configuration de ${CONF} en fonction du modèle ${TMPL} +# Viariables misent à jour : +# - __DOMAIN__ +# Il est possible de prendre en considération ou d'occulter des blocks. +# Le début du block est repéré par une ligne contenant {{XXX +# La fin du block est repéré par une ligne contenant }} +# L'affiche est fonction de XXX +# XXX = on => affichage systématique +# XXX = off => masquage systématique +# XXX = compose => affichage si la variable d'environnement proxy_compose à la valeur on + +KAZ_ROOT=$(cd "$(dirname $0)/.."; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +. "${DOCKERS_ENV}" +. "${KAZ_KEY_DIR}/SetAllPass.sh" + +usage () { + echo $(basename "$0") " [-h] [-help] [-timestamp] template dst" + echo " -h" + echo " -help Display this help." + echo " -timestamp produce timestamp comment." +} + +TIMESTAMP="" + +case "$1" in + '-h' | '-help' ) + usage + shift + exit;; + '-time' | '-timestamp' ) + TIMESTAMP=YES + shift;; +esac + +# no more export in .env +PROXY_VARS=$(set | grep "proxy_.*=") +for var in ${PROXY_VARS} +do + export ${var} +done + +( + # $1 = template + # $2 = target + if [ "${TIMESTAMP}" == "YES" ]; then + echo "# Generated by $(pwd)$(basename $0)" + echo "# à partir du modèle $1" + echo "#" $(date "+%x %X") + echo + fi + + sed \ + -e "/^[ \t]*$/d"\ + -e "/^[ ]*#.*$/d"\ + -e "s|__CACHET_HOST__|${cachetHost}|g"\ + -e "s|__CALC_HOST__|${calcHost}|g"\ + -e "s|__CLOUD_HOST__|${cloudHost}|g"\ + -e "s|__DATE_HOST__|${dateHost}|g"\ + -e "s|__DOKUWIKI_HOST__|${dokuwikiHost}|g"\ + -e "s|__DOMAIN__|${domain}|g"\ + -e "s|__FILE_HOST__|${fileHost}|g"\ + -e "s|__PAHEKO_API_PASSWORD__|${paheko_API_PASSWORD}|g"\ + -e "s|__PAHEKO_API_USER__|${paheko_API_USER}|g"\ + -e "s|__PAHEKO_HOST__|${pahekoHost}|g"\ + -e "s|__GIT_HOST__|${gitHost}|g"\ + -e "s|__GRAV_HOST__|${gravHost}|g"\ + -e "s|__HTTP_PROTO__|${httpProto}|g"\ + -e "s|__LDAP_HOST__|${ldapHost}|g"\ + -e "s|__LDAPUI_HOST__|${ldapUIHost}|g"\ + -e "s|__MATTER_HOST__|${matterHost}|g"\ + -e "s|__OFFICE_HOST__|${officeHost}|g"\ + -e "s|__PAD_HOST__|${padHost}|g"\ + -e "s|__QUOTAS_HOST__|${quotasHost}|g"\ + -e "s|__SMTP_HOST__|${smtpHost}|g"\ + -e "s|__SYMPADB__|${sympaDBName}|g"\ + -e "s|__SYMPA_HOST__|${sympaHost}|g"\ + -e "s|__SYMPA_MYSQL_DATABASE__|${sympa_MYSQL_DATABASE}|g"\ + -e "s|__SYMPA_MYSQL_PASSWORD__|${sympa_MYSQL_PASSWORD}|g"\ + -e "s|__SYMPA_MYSQL_USER__|${sympa_MYSQL_USER}|g"\ + -e "s|__VIGILO_HOST__|${vigiloHost}|g"\ + -e "s|__WEBMAIL_HOST__|${webmailHost}|g"\ + -e "s|__CASTOPOD_HOST__|${castopodHost}|g"\ + -e "s|__IMAPSYNC_HOST__|${imapsyncHost}|g"\ + -e "s|__YAKFORMS_HOST__|${yakformsHost}|g"\ + -e "s|__WORDPRESS_HOST__|${wordpressHost}|g"\ + -e "s|__MOBILIZON_HOST__|${mobilizonHost}|g"\ + -e "s|__API_HOST__|${apiHost}|g"\ + -e "s|__VAULTWARDEN_HOST__|${vaultwardenHost}|g"\ + -e "s|__DOMAIN_SYMPA__|${domain_sympa}|g"\ + $1 | awk ' + BEGIN {cp=1} + /}}/ {cp=1 ; next}; + /{{on/ {cp=1; next}; + /{{off/ {cp=0; next}; + match($0, /{{[a-zA-Z0-9_]+/) {cp=(ENVIRON["proxy_" substr($0,RSTART+2,RLENGTH)] == "on"); next}; + {if (cp) print $0};' +) > $2 diff --git a/bin/checkEnvFiles.sh b/bin/checkEnvFiles.sh new file mode 100755 index 0000000..382d390 --- /dev/null +++ b/bin/checkEnvFiles.sh @@ -0,0 +1,315 @@ +#!/bin/bash + +export KAZ_ROOT=$(cd "$(dirname $0)/.."; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +RUN_PASS_DIR="secret" +TMPL_PASS_DIR="secret.tmpl" +RUN_PASS_FILE="${RUN_PASS_DIR}/SetAllPass.sh" +TMPL_PASS_FILE="${TMPL_PASS_DIR}/SetAllPass.sh" +NEED_GEN= + +######################################## + +usage () { + echo "Usage: $0 [-n] [-h]" + echo " -h help" + exit 1 +} + +case "$1" in + '-h' | '-help' ) + usage + ;; +esac + +[ "$#" -eq 0 ] || usage + +######################################## +# check system + +for prg in kompare; do + if ! type "${prg}" > /dev/null; then + printKazError "$0 need ${prg}" + echo "please run \"apt-get install ${prg}\"" + exit + fi +done + +cd "${KAZ_ROOT}" +######################################## +# get lvalues in script +getVars () { + # $1 : filename + grep "^[^#]*=" $1 | sed 's/\([^=]*\).*/\1/' | sort -u +} + +# get lvalues in script +getSettedVars () { + # $1 : filename + grep "^[^#]*=..*" $1 | grep -v '^[^#]*=".*--clean_val--.*"' | grep -v '^[^#]*="${' | sort -u +} + +getVarFormVal () { + # $1 searched value + # $2 filename + grep "^[^#]*=$1" $2 | sed 's/\s*\([^=]*\).*/\1/' +} + +######################################## +# synchronized SetAllPass.sh (find missing lvalues) +updatePassFile () { + # $1 : ref filename + # $2 : target filename + + REF_FILE="$1" + TARGET_FILE="$2" + NEED_UPDATE= + while : ; do + declare -a listRef listTarget missing + listRef=($(getVars "${REF_FILE}")) + listTarget=($(getVars "${TARGET_FILE}")) + missing=($(comm -23 <(printf "%s\n" ${listRef[@]}) <(printf "%s\n" ${listTarget[@]}))) + if [ -n "${missing}" ]; then + echo "missing vars in ${YELLOW}${BOLD}${TARGET_FILE}${NC}:${RED}${BOLD}" ${missing[@]} "${NC}" + read -p "Do you want to add them? [y/n]: " yn + case $yn in + ""|[Yy]*) + emacs "${REF_FILE}" "${TARGET_FILE}" + NEED_UPDATE=true + break + ;; + [Nn]*) + break + ;; + esac + else + break + fi + done +} + +updatePassFile "${TMPL_PASS_FILE}" "${RUN_PASS_FILE}" +[ -n "${NEED_UPDATE}" ] && NEED_GEN=true +updatePassFile "${RUN_PASS_FILE}" "${TMPL_PASS_FILE}" + +######################################## +# check empty pass in TMPL_PASS_FILE +declare -a settedVars +settedVars=($(getSettedVars "${TMPL_PASS_FILE}")) +if [ -n "${settedVars}" ]; then + echo "unclear password in ${YELLOW}${BOLD}${TMPL_PASS_FILE}${NC}:${BLUE}${BOLD}" + for var in ${settedVars[@]}; do + echo -e "\t${var}" + done + echo "${NC}" + read -p "Do you want to clear them? [y/n]: " yn + case $yn in + ""|[Yy]*) + emacs "${TMPL_PASS_FILE}" + ;; + esac +fi + +######################################## +# check new files env-* +createMissingEnv () { + # $1 : ref dir + # $2 : target dir + REF_DIR="$1" + TARGET_DIR="$2" + NEED_UPDATE= + + declare -a listRef listTarget missing + listRef=($(cd "${REF_DIR}"; ls -1 env-* | grep -v '~$')) + listTarget=($(cd "${TARGET_DIR}"; ls -1 env-* | grep -v '~$')) + missing=($(comm -23 <(printf "%s\n" ${listRef[@]}) <(printf "%s\n" ${listTarget[@]}))) + for envFile in ${missing[@]}; do + read -p "Do you want to create ${GREEN}${BOLD}${TARGET_DIR}/${envFile}${NC}? [y/n]: " yn + case $yn in + ""|[Yy]*) + cp "${REF_DIR}/${envFile}" "${TARGET_DIR}/${envFile}" + NEED_UPDATE=true + ;; + esac + done +} + +createMissingEnv "${RUN_PASS_DIR}" "${TMPL_PASS_DIR}" +[ -n "${NEED_UPDATE}" ] && NEED_GEN=true +createMissingEnv "${TMPL_PASS_DIR}" "${RUN_PASS_DIR}" +[ -n "${NEED_UPDATE}" ] && NEED_GEN=true + +######################################## +# check missing values in env-* between RUN and TMPL +declare -a listTmpl listRun listCommonFiles +listTmplFiles=($(cd "${TMPL_PASS_DIR}"; ls -1 env-* | grep -v '~$')) +listRunFiles=($(cd "${RUN_PASS_DIR}"; ls -1 env-* | grep -v '~$')) +listCommonFiles=($(comm -3 <(printf "%s\n" ${listTmplFiles[@]}) <(printf "%s\n" ${listRunFiles[@]}))) +for envFile in ${listCommonFiles[@]}; do + while : ; do + TMPL_FILE="${TMPL_PASS_DIR}/${envFile}" + RUN_FILE="${RUN_PASS_DIR}/${envFile}" + declare -a listRef list2Target missingInRun missingInTmpl + listTmplVars=($(getVars "${TMPL_FILE}")) + listRunVars=($(getVars "${RUN_FILE}")) + missingInTmpl=($(comm -23 <(printf "%s\n" ${listTmplVars[@]}) <(printf "%s\n" ${listRunVars[@]}))) + missingInRun=($(comm -13 <(printf "%s\n" ${listTmplVars[@]}) <(printf "%s\n" ${listRunVars[@]}))) + if [ -n "${missingInTmpl}" ] || [ -n "${missingInRun}" ]; then + [ -n "${missingInTmpl}" ] && + echo "missing vars in ${YELLOW}${BOLD}${TMPL_FILE}${NC}:${RED}${BOLD}" ${missingInTmpl[@]} "${NC}" + [ -n "${missingInRun}" ] && + echo "missing vars in ${YELLOW}${BOLD}${RUN_FILE}${NC}:${RED}${BOLD}" ${missingInRun[@]} "${NC}" + read -p "Do you want to add them? [y/n]: " yn + case $yn in + ""|[Yy]*) + emacs "${TMPL_FILE}" "${RUN_FILE}" + [ -n "${missingInTmpl}" ] && NEED_GEN=true + break + ;; + [Nn]*) + break + ;; + esac + else + break + fi + done +done + +######################################## +# check empty pass in env-* +for envFile in $(ls -1 "${TMPL_PASS_DIR}/"env-* | grep -v '~$'); do + settedVars=($(getSettedVars "${envFile}")) + if [ -n "${settedVars}" ]; then + echo "unclear password in ${GREEN}${BOLD}${envFile}${NC}:${BLUE}${BOLD}" + for var in ${settedVars[@]}; do + echo -e "\t${var}" + done + echo "${NC}" + read -p "Do you want to clear them? [y/n]: " yn + case $yn in + ""|[Yy]*) + emacs "${envFile}" + ;; + esac + fi +done + +######################################## +# check extention in dockers.env +declare -a missing +missing=($(for DIR in "${RUN_PASS_DIR}" "${TMPL_PASS_DIR}"; do + for envFile in $(ls -1 "${DIR}/"env-* | grep -v '~$'); do + val="${envFile#*env-}" + varName=$(getVarFormVal "${val}" "${DOCKERS_ENV}") + if [ -z "${varName}" ]; then + echo "${val}" + fi + done + done | sort -u)) +if [ -n "${missing}" ]; then + echo "missing def in ${GREEN}${BOLD}${DOCKERS_ENV}${NC}:${BLUE}${BOLD}" + for var in ${missing[@]}; do + echo -e "\t${var}" + done + echo "${NC}" + read -p "Do you want to add them? [y/n]: " yn + case $yn in + ""|[Yy]*) + emacs "${DOCKERS_ENV}" + ;; + esac +fi + +######################################## +# check env-* in updateDockerPassword.sh +missing=($(for DIR in "${RUN_PASS_DIR}" "${TMPL_PASS_DIR}"; do + for envFile in $(ls -1 "${DIR}/"env-* | grep -v '~$'); do + val="${envFile#*env-}" + varName=$(getVarFormVal "${val}" "${DOCKERS_ENV}") + [ -z "${varName}" ] && continue + prefixe=$(grep "^\s*updateEnv.*${varName}" "${KAZ_BIN_DIR}/updateDockerPassword.sh" | + sed 's/\s*updateEnv[^"]*"\([^"]*\)".*/\1/' | sort -u) + if [ -z "${prefixe}" ]; then + echo "${envFile#*/}_(\${KAZ_KEY_DIR}/env-\${"${varName}"})" + fi + done + done | sort -u)) +if [ -n "${missing}" ]; then + echo "missing update in ${GREEN}${BOLD}${KAZ_BIN_DIR}/updateDockerPassword.sh${NC}:${BLUE}${BOLD}" + for var in ${missing[@]}; do + echo -e "\t${var}" + done + echo "${NC}" + read -p "Do you want to add them? [y/n]: " yn + case $yn in + ""|[Yy]*) + emacs "${KAZ_BIN_DIR}/updateDockerPassword.sh" + ;; + esac +fi + +######################################## +# synchronized SetAllPass.sh and env-* +updateEnvFiles () { + # $1 secret dir + DIR=$1 + listRef=($(getVars "${DIR}/SetAllPass.sh")) + missing=($(for envFile in $(ls -1 "${DIR}/"env-* | grep -v '~$'); do + val="${envFile#*env-}" + varName=$(getVarFormVal "${val}" "${DOCKERS_ENV}") + [ -z "${varName}" ] && continue + prefixe=$(grep "^\s*updateEnv.*${varName}" "${KAZ_BIN_DIR}/updateDockerPassword.sh" | + sed 's/\s*updateEnv[^"]*"\([^"]*\)".*/\1/' | sort -u) + [ -z "${prefixe}" ] && continue + listVarsInEnv=($(getVars "${envFile}")) + for var in ${listVarsInEnv[@]}; do + [[ ! " ${listRef[@]} " =~ " ${prefixe}_${var} " ]] && echo "${prefixe}_${var}" + done + # XXX doit exister dans SetAllPass.sh avec le prefixe + done)) + if [ -n "${missing}" ]; then + echo "missing update in ${GREEN}${BOLD}${DIR}/SetAllPass.sh${NC}:${BLUE}${BOLD}" + for var in ${missing[@]}; do + echo -e "\t${var}" + done + echo "${NC}" + read -p "Do you want to add them? [y/n]: " yn + case $yn in + ""|[Yy]*) + emacs "${DIR}/SetAllPass.sh" + ;; + esac + fi +} + +updateEnvFiles "${RUN_PASS_DIR}" +updateEnvFiles "${TMPL_PASS_DIR}" + +# XXX chercher les variables non utilisées dans les SetAllPass.sh + +if [ -n "${NEED_GEN}" ]; then + while : ; do + read -p "Do you want to generate blank values? [y/n]: " yn + case $yn in + ""|[Yy]*) + "${KAZ_BIN_DIR}/secretGen.sh" + break + ;; + [Nn]*) + break + ;; + esac + done +fi + + +# XXX config/dockers.tmpl.env + + +# XXX ! vérifier init pour dockers.env + + + diff --git a/bin/checkEnvPassword.sh b/bin/checkEnvPassword.sh new file mode 100755 index 0000000..0a3f5f5 --- /dev/null +++ b/bin/checkEnvPassword.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +KAZ_ROOT=$(cd $(dirname $0)/..; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +for filename in "${KAZ_KEY_DIR}/"env-*Serv "${KAZ_KEY_DIR}/"env-*DB; do + if grep -q "^[^#=]*=\s*$" "${filename}" 2>/dev/null; then + echo "${filename}" + fi +done diff --git a/bin/configKaz.sh b/bin/configKaz.sh new file mode 100755 index 0000000..0668a4d --- /dev/null +++ b/bin/configKaz.sh @@ -0,0 +1,24 @@ +#!/bin/sh -e + +. /usr/share/debconf/confmodule +db_version 2.0 + +if [ "$1" = "fset" ]; then + db_fset kaz/mode seen false + db_fset kaz/domain seen false + db_go +fi +if [ "$1" = "reset" ]; then + db_reset kaz/mode + db_reset kaz/domain + db_go +fi + +#db_set kaz/domain test + + +db_title "a b c" + +db_input critical kaz/mode +db_input critical kaz/domain +db_go diff --git a/bin/configKaz.sh.templates b/bin/configKaz.sh.templates new file mode 100755 index 0000000..ec5a07c --- /dev/null +++ b/bin/configKaz.sh.templates @@ -0,0 +1,11 @@ +Template: kaz/mode +Type: select +Choices: prod, dev, local +Default: local +Description: Mode + +Template: kaz/domain +Type: string +Description: domain name +Default: kaz.bzh + diff --git a/bin/container.sh b/bin/container.sh new file mode 100755 index 0000000..dac3d2c --- /dev/null +++ b/bin/container.sh @@ -0,0 +1,319 @@ +#!/bin/bash + +# En cas d'absence de postfix, il faut lancer : +# docker network create postfix_mailNet + +# démare/arrête un compose +# sauvegarde la base de données d'un compose +# met à jours les paramètres de configuration du mandataire (proxy) + +KAZ_ROOT=$(cd "$(dirname $0)/.."; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +cd "${KAZ_BIN_DIR}" +PATH_SAUVE="/home/sauve/" +export SIMU="" + +declare -a availableComposesNoNeedMail availableMailComposes availableComposesNeedMail availableProxyComposes availableOrga +availableComposesNoNeedMail=($(getList "${KAZ_CONF_DIR}/container-withoutMail.list")) +availableMailComposes=($(getList "${KAZ_CONF_DIR}/container-mail.list")) +availableComposesNeedMail=($(getList "${KAZ_CONF_DIR}/container-withMail.list")) +availableProxyComposes=($(getList "${KAZ_CONF_DIR}/container-proxy.list")) +availableOrga=($(getList "${KAZ_CONF_DIR}/container-orga.list")) +availableComposesNeedMail+=( "${availableOrga[@]}" ) + +knownedComposes+=( ${availableMailComposes[@]} ) +knownedComposes+=( ${availableProxyComposes[@]} ) +knownedComposes+=( ${availableComposesNoNeedMail[@]} ) +knownedComposes+=( ${availableComposesNeedMail[@]} ) + +usage () { + echo "Usage: $0 [-n] {status|start|stop|save} [compose]..." + echo " -n : simulation" + echo " status : docker-compose status (default all compose available)" + echo " start : start composes (default all compose validate)" + echo " stop : stop composes (default all compose enable)" + echo " save : save all known database" + echo " [compose] : in ${knownedComposes[@]}" + exit 1 +} + +doCompose () { + # $1 dans ("up -d" "down") + # $2 nom du répertoire du compose + echo "compose: $1 $2" + ${SIMU} cd "${KAZ_COMP_DIR}/$2" + if [ ! -h .env ] ; then + echo "create .env in $2" + ${SIMU} ln -fs ../../config/dockers.env .env + fi + ${SIMU} docker-compose $1 + + if [ "$2" = "cachet" ] && [ "$1" != "down" ]; then + NEW_KEY=$(cd "${KAZ_COMP_DIR}/$2" ; docker-compose logs | grep APP_KEY=base64: | sed "s/^.*'APP_KEY=\(base64:[^']*\)'.*$/\1/" | tail -1) + if [ -n "${NEW_KEY}" ]; then + printKazMsg "cachet key change" + # change key + ${SIMU} sed -i \ + -e 's%^\(\s*cachet_APP_KEY=\).*$%\1"'"${NEW_KEY}"'"%' \ + "${KAZ_KEY_DIR}/SetAllPass.sh" + ${SIMU} "${KAZ_BIN_DIR}/secretGen.sh" + # restart + ${SIMU} docker-compose $1 + fi + fi +} + +doComposes () { + # $1 dans ("up -d" "down") + # $2+ nom des répertoires des composes + cmd=$1 + shift + for compose in $@ ; do + doCompose "${cmd}" ${compose} + done +} + +updateProxy () { + # $1 dans ("on" "off") + # $2 nom des répertoires des composes + cmd=$1 + shift + echo "update proxy ${cmd}: $@" + date=$(date "+%x %X") + for compose in $@ ; do + composeFlag=${compose//-/_} + entry="proxy_${composeFlag}=" + newline="${entry}${cmd} # update by $(basename $0) at ${date}" + if ! grep -q "proxy_${composeFlag}=" "${DOCKERS_ENV}" 2> /dev/null ; then + if [[ -n "${SIMU}" ]] ; then + echo "${newline} >> ${DOCKERS_ENV}" + else + echo "${newline}" >> "${DOCKERS_ENV}" + fi + else + ${SIMU} sed -i \ + -e "s|${entry}.*|${newline}|g" \ + "${DOCKERS_ENV}" + fi + done + for item in "${availableProxyComposes[@]}"; do + ${SIMU} ${KAZ_COMP_DIR}/${item}/proxy-gen.sh + done +} + +saveDB () { + #attention, soucis avec l'option "-ti" qui ne semble pas rendre la main avec docker exec + + containerName=$1 + userName=$2 + userPass=$3 + dbName=$4 + backName=$5 + if [[ -n "${SIMU}" ]] ; then + ${SIMU} "docker exec ${containerName} mysqldump --user=${userName} --password=${userPass} ${dbName} | gzip > $PATH_SAUVE${backName}.sql.gz" + else + docker exec ${containerName} mysqldump --user=${userName} --password=${userPass} ${dbName} | gzip > $PATH_SAUVE${backName}.sql.gz + fi +} + +declare -a enableComposesNoNeedMail enableMailComposes enableComposesNeedMail enableProxyComposes + +enableComposesNoNeedMail=() +enableMailComposes=() +enableComposesNeedMail=() +enableProxyComposes=() + +startComposes () { + ./kazDockerNet.sh add ${enableComposesNoNeedMail[@]} ${enableProxyComposes[@]} ${enableMailComposes[@]} ${enableComposesNeedMail[@]} + [ ${#enableComposesNeedMail[@]} -ne 0 ] && [[ ! "${enableMailComposes[@]}" =~ "postfix" ]] && ./kazDockerNet.sh add postfix + [[ "${enableComposesNeedMail[@]}" =~ "paheko" ]] && ${SIMU} ${KAZ_COMP_DIR}/paheko/paheko-gen.sh + doComposes "up -d" ${enableComposesNoNeedMail[@]} + doComposes "up -d" ${enableMailComposes[@]} + doComposes "up -d" ${enableComposesNeedMail[@]} + updateProxy "on" ${enableComposesNoNeedMail[@]} ${enableComposesNeedMail[@]} + doComposes "up -d" ${enableProxyComposes[@]} + for item in "${enableProxyComposes[@]}"; do + ${SIMU} ${KAZ_COMP_DIR}/${item}/reload.sh + done + if grep -q "^.s*proxy_web.s*=.s*on" "${DOCKERS_ENV}" 2> /dev/null ; then + ${SIMU} ${KAZ_COMP_DIR}/web/web-gen.sh + fi +} + +stopComposes () { + updateProxy "off" ${enableComposesNoNeedMail[@]} ${enableComposesNeedMail[@]} + doComposes "down" ${enableProxyComposes[@]} + doComposes "down" ${enableComposesNeedMail[@]} + doComposes "down" ${enableMailComposes[@]} + doComposes "down" ${enableComposesNoNeedMail[@]} + if grep -q "^.s*proxy_web.s*=.s*on" "${DOCKERS_ENV}" 2> /dev/null ; then + ${SIMU} ${KAZ_COMP_DIR}/web/web-gen.sh + fi +} + +statusComposes () { + ${KAZ_ROOT}/bin/kazList.sh compose status ${enableMailComposes[@]} ${enableProxyComposes[@]} ${enableComposesNoNeedMail[@]} ${enableComposesNeedMail[@]} +} + +saveComposes () { + . "${DOCKERS_ENV}" + . "${KAZ_ROOT}/secret/SetAllPass.sh" + + savedComposes+=( ${enableMailComposes[@]} ) + savedComposes+=( ${enableProxyComposes[@]} ) + savedComposes+=( ${enableComposesNoNeedMail[@]} ) + savedComposes+=( ${enableComposesNeedMail[@]} ) + + for compose in ${savedComposes[@]} + do + case "${compose}" in + jirafeau) + # rien à faire (fichiers) + ;; + ethercalc) + #inutile car le backup de /var/lib/docker/volumes/ethercalc_calcDB/_data/dump.rdb est suffisant + ;; + #grav) + # ??? + #;; + #postfix) + sympa) + echo "save sympa" + saveDB ${sympaDBName} "${sympa_MYSQL_USER}" "${sympa_MYSQL_PASSWORD}" "${sympa_MYSQL_DATABASE}" sympa + ;; + web) + # rien à faire (fichiers) + ;; + etherpad) + echo "save pad" + saveDB ${etherpadDBName} "${etherpad_MYSQL_USER}" "${etherpad_MYSQL_PASSWORD}" "${etherpad_MYSQL_DATABASE}" etherpad + ;; + framadate) + echo "save date" + saveDB ${framadateDBName} "${framadate_MYSQL_USER}" "${framadate_MYSQL_PASSWORD}" "${framadate_MYSQL_DATABASE}" framadate + ;; + cloud) + echo "save cloud" + saveDB ${nextcloudDBName} "${nextcloud_MYSQL_USER}" "${nextcloud_MYSQL_PASSWORD}" "${nextcloud_MYSQL_DATABASE}" nextcloud + ;; + paheko) + # rien à faire (fichiers) + ;; + mattermost) + echo "save mattermost" + saveDB ${mattermostDBName} "${mattermost_MYSQL_USER}" "${mattermost_MYSQL_PASSWORD}" "${mattermost_MYSQL_DATABASE}" mattermost + ;; + dokuwiki) + # rien à faire (fichiers) + ;; + *-orga) + ORGA=${compose%-orga} + echo "save ${ORGA}" + if grep -q "cloud:" "${KAZ_COMP_DIR}/${compose}/docker-compose.yml" 2> /dev/null ; then + echo " => cloud" + saveDB "${ORGA}-DB" "${nextcloud_MYSQL_USER}" "${nextcloud_MYSQL_PASSWORD}" "${nextcloud_MYSQL_DATABASE}" "${ORGA}-cloud" + fi + if grep -q "agora:" "${KAZ_COMP_DIR}/${compose}/docker-compose.yml" 2> /dev/null ; then + echo " => mattermost" + saveDB "${ORGA}-DB" "${mattermost_MYSQL_USER}" "${mattermost_MYSQL_PASSWORD}" "${mattermost_MYSQL_DATABASE}" "${ORGA}-mattermost" + fi + if grep -q "wordpress:" "${KAZ_COMP_DIR}/${compose}/docker-compose.yml" 2> /dev/null ; then + echo " => wordpress" + saveDB "${ORGA}-DB" "${wp_MYSQL_USER}" "${wp_MYSQL_PASSWORD}" "${wp_MYSQL_DATABASE}" "${ORGA}-wordpress" + fi + ;; + esac + done +} + +if [ "$#" -eq 0 ] ; then + usage +fi + +if [ "$1" == "-h" ] ; then + usage + shift +fi + +if [ "$1" == "-n" ] ; then + export SIMU=echo + shift +fi + +DCK_CMD="" +SAVE_CMD="" +case "$1" in + start) + DCK_CMD="startComposes" + shift + ;; + + stop) + DCK_CMD="stopComposes" + shift + ;; + + save) + SAVE_CMD="saveComposes" + shift + ;; + + status) + DCK_CMD="statusComposes" + shift + ;; + *) + usage + ;; +esac + +if [ $# -eq 0 ] ; then + enableComposesNoNeedMail=("${availableComposesNoNeedMail[@]}") + enableMailComposes=("${availableMailComposes[@]}") + enableComposesNeedMail=("${availableComposesNeedMail[@]}") + enableProxyComposes=("${availableProxyComposes[@]}") +else + if [ "${DCK_CMD}" = "startComposes" ] ; then + enableProxyComposes=("${availableProxyComposes[@]}") + fi +fi + +for compose in $* +do + compose=${compose%/} + if [[ ! " ${knownedComposes[@]} " =~ " ${compose} " ]]; then + declare -a subst + subst=() + for item in "${knownedComposes[@]}"; do + [[ "${item}" =~ "${compose}" ]] && subst+=(${item}) + done + if [ "${subst}" = "" ] ; then + echo + echo "Unknown compose: ${RED}${BOLD}${compose}${NC} not in ${YELLOW}${BOLD}${knownedComposes[*]}${NC}" + echo + exit 1 + else + echo "substitute compose: ${YELLOW}${BOLD}${compose} => ${subst[@]}${NC}" + fi + fi + for item in "${availableMailComposes[@]}"; do + [[ "${item}" =~ "${compose}" ]] && enableMailComposes+=("${item}") + done + for item in "${availableProxyComposes[@]}"; do + [[ "${item}" =~ "${compose}" ]] && enableProxyComposes=("${item}") + done + for item in "${availableComposesNoNeedMail[@]}"; do + [[ "${item}" =~ "${compose}" ]] && enableComposesNoNeedMail+=("${item}") + done + for item in "${availableComposesNeedMail[@]}"; do + [[ "${item}" =~ "${compose}" ]] && enableComposesNeedMail+=("${item}") + done +done + +[[ ! -z "${DCK_CMD}" ]] && "${DCK_CMD}" && exit 0 + +[[ ! -z "${SAVE_CMD}" ]] && "${SAVE_CMD}" && exit 0 + +exit 1 diff --git a/bin/createEmptyPasswd.sh b/bin/createEmptyPasswd.sh new file mode 100755 index 0000000..cb8e694 --- /dev/null +++ b/bin/createEmptyPasswd.sh @@ -0,0 +1,104 @@ +#!/bin/bash + +cd $(dirname $0)/.. + +mkdir -p emptySecret +rsync -aHAX --info=progress2 --delete secret/ emptySecret/ + +cd emptySecret/ + +. ../config/dockers.env +. ./SetAllPass.sh + +# pour mise au point +# SIMU=echo + +cleanEnvDB(){ + # $1 = prefix + # $2 = envName + # $3 = containerName of DB + rootPass="--root_password--" + dbName="--database_name--" + userName="--user_name--" + userPass="--user_password--" + + ${SIMU} sed -i \ + -e "s/MYSQL_ROOT_PASSWORD=.*/MYSQL_ROOT_PASSWORD=${rootPass}/g" \ + -e "s/MYSQL_DATABASE=.*/MYSQL_DATABASE=${dbName}/g" \ + -e "s/MYSQL_USER=.*/MYSQL_USER=${userName}/g" \ + -e "s/MYSQL_PASSWORD=.*/MYSQL_PASSWORD=${userPass}/g" \ + "$2" +} + +cleanEnv(){ + # $1 = prefix + # $2 = envName + for varName in $(grep "^[a-zA-Z_]*=" $2 | sed "s/^\([^=]*\)=.*/\1/g") + do + srcName="$1_${varName}" + srcVal="--clean_val--" + ${SIMU} sed -i \ + -e "s~^[ ]*${varName}=.*$~${varName}=${srcVal}~" \ + "$2" + done +} + +cleanPasswd(){ + ${SIMU} sed -i \ + -e 's/^\([# ]*[^#= ]*\)=".[^{][^"]*"/\1="--clean_val--"/g' \ + ./SetAllPass.sh +} + +#################### +# main + +# read -r -p "Do you want to remove all password? [Y/n] " input + +# case $input in +# [yY][eE][sS]|[yY]) +# echo "Remove all password" +# ;; +# [nN][oO]|[nN]) +# echo "Abort" +# ;; +# *) +# echo "Invalid input..." +# exit 1 +# ;; +# esac + +cleanPasswd + +cleanEnvDB "etherpad" "./env-${etherpadDBName}" "${etherpadDBName}" +cleanEnvDB "framadate" "./env-${framadateDBName}" "${framadateDBName}" +cleanEnvDB "git" "./env-${gitDBName}" "${gitDBName}" +cleanEnvDB "mattermost" "./env-${mattermostDBName}" "${mattermostDBName}" +cleanEnvDB "nextcloud" "./env-${nextcloudDBName}" "${nextcloudDBName}" +cleanEnvDB "roundcube" "./env-${roundcubeDBName}" "${roundcubeDBName}" +cleanEnvDB "sso" "./env-${ssoDBName}" "${ssoDBName}" +cleanEnvDB "sympa" "./env-${sympaDBName}" "${sympaDBName}" +cleanEnvDB "vigilo" "./env-${vigiloDBName}" "${vigiloDBName}" +cleanEnvDB "wp" "./env-${wordpressDBName}" "${wordpressDBName}" + +cleanEnv "etherpad" "./env-${etherpadServName}" +cleanEnv "gandi" "./env-gandi" +cleanEnv "jirafeau" "./env-${jirafeauServName}" +cleanEnv "mattermost" "./env-${mattermostServName}" +cleanEnv "nextcloud" "./env-${nextcloudServName}" +cleanEnv "office" "./env-${officeServName}" +cleanEnv "roundcube" "./env-${roundcubeServName}" +cleanEnv "sso" "./env-${ssoServName}" +cleanEnv "vigilo" "./env-${vigiloServName}" +cleanEnv "wp" "./env-${wordpressServName}" + +cat > allow_admin_ip < no orga + CREATE_ORGA= + shift;; + '-v' | '-V' ) + echo "${PRG}, root : ${KAZ_ROOT}, on domain : ${URL_SITE}" + exit + ;; + *) + usage + echo "${PRG}: ${RED}unknown parameter${NC}" + shift + exit;; + esac +done + +################################## +# Inventaire des comptes à créer # +################################## + +# la recherche des comptes à créé avec la commande : +# bin/interoPaheko.sh + +# création d'un fichier vide +# TODO : même code ici et dans interoPaheko.sh => risque de divergence +if [ ! -s "${FILE}" ];then + echo "${RED}" + echo "ERREUR : le fichier ${FILE} n'existait pas" + echo "Il vient d'être créé. Vous pouvez le compléter." + echo "${NC}" + cat > "${FILE}" < "${i}" && chmod +x "${i}" +done + +echo "numero,nom,quota_disque,action_auto" > "${TEMP_PAHEKO}" +echo "curl \"https://${paheko_API_USER}:${paheko_API_PASSWORD}@kaz-paheko.kaz.bzh/api/user/import\" -T \"${TEMP_PAHEKO}\"" >> "${CMD_PAHEKO}" + +#echo "récupération des login postfix... " +## on stocke les emails et les alias KAZ déjà créés +#( +# ${SETUP_MAIL} email list +# ${SETUP_MAIL} alias list +#) | cut -d ' ' -f 2 | grep @ | sort > "${TFILE_EMAIL}" +# did on supprime le ^M en fin de fichier pour pas faire planter les grep +#dos2unix "${TFILE_EMAIL}" + +echo "on récupère tous les emails (secours/alias/kaz) sur le ldap" +FILE_LDIF=/home/sauve/ldap.ldif +/kaz/bin/ldap/ldap_sauve.sh +gunzip ${FILE_LDIF}.gz -f +grep -aEiorh '([[:alnum:]]+([._-][[:alnum:]]+)*@[[:alnum:]]+([._-][[:alnum:]]+)*\.[[:alpha:]]{2,6})' ${FILE_LDIF} | sort -u > ${TFILE_EMAIL} + +echo "récupération des login mattermost... " +docker exec -ti mattermostServ bin/mmctl user list --all | grep ":.*(" | cut -d ':' -f 2 | cut -d ' ' -f 2 | sort > "${TFILE_MM}" + +dos2unix "${TFILE_MM}" +echo "done" + +# se connecter à l'agora pour ensuite pouvoir passer toutes les commandes mmctl +echo "docker exec -ti mattermostServ bin/mmctl auth login ${httpProto}://${URL_AGORA} --name local-server --username ${mattermost_user} --password ${mattermost_pass}" | tee -a "${CMD_INIT}" + +# vérif des emails +regex="^(([A-Za-z0-9]+((\.|\-|\_|\+)?[A-Za-z0-9]?)*[A-Za-z0-9]+)|[A-Za-z0-9]+)@(([A-Za-z0-9]+)+((\.|\-|\_)?([A-Za-z0-9]+)+)*)+\.([A-Za-z]{2,})+$" +function validator { + if ! [[ "$1" =~ ${regex} ]]; then + # printf "* %-48s \e[1;31m[fail]\e[m\n" "${1}" + ( + echo + echo "ERREUR : le paramètre ${RED}${BOLD}$1 n'est pas un email valide${NC} - on stoppe tout - aucun utilisateur de créé" + echo + ) | tee -a "${LOG}" + exit 1 + fi +} + +###################################### +# Boucle lecture des comptes à créer # +###################################### +echo -e "$(date '+%Y-%m-%d %H:%M:%S') : ${PRG} - sauvegarde des utilisateurs à créer" | tee "${LOG}" +cat "${FILE}" >> "${LOG}" + +LDAP_IP=$(docker inspect -f '{{.NetworkSettings.Networks.ldapNet.IPAddress}}' ldapServ) +ALL_ORGA= + +while read ligne; do + + # | xargs permet de faire un trim + NOM=$(awk -F ";" '{print $1}' <<< "${ligne}" | xargs) + PRENOM=$(awk -F ";" '{print $2}' <<< "${ligne}" | xargs) + + declare -A tab_email + tab_email[EMAIL_SOUHAITE]=$(awk -F ";" '{print $3}' <<< "${ligne}" | xargs) + tab_email[EMAIL_SECOURS]=$(awk -F ";" '{print $4}' <<< "${ligne}" | xargs) + + ORGA=$(awk -F ";" '{print $5}' <<< "${ligne}" | xargs) + ORGA=${ORGA,,} + + declare -A service + service[ADMIN_ORGA]=$(awk -F ";" '{print $6}' <<< "${ligne}" | xargs) + service[NC_ORGA]=$(awk -F ";" '{print $7}' <<< "${ligne}" | xargs) + service[PAHEKO_ORGA]=$(awk -F ";" '{print $8}' <<< "${ligne}" | xargs) + service[WP_ORGA]=$(awk -F ";" '{print $9}' <<< "${ligne}" | xargs) + service[AGORA_ORGA]=$(awk -F ";" '{print $10}' <<< "${ligne}" | xargs) + service[WIKI_ORGA]=$(awk -F ";" '{print $11}' <<< "${ligne}" | xargs) + service[NC_BASE]=$(awk -F ";" '{print $12}' <<< "${ligne}" | xargs) + + GROUPE_NC_BASE=$(awk -F ";" '{print $13}' <<< "${ligne}" | xargs) + GROUPE_NC_BASE="${GROUPE_NC_BASE,,}" + EQUIPE_AGORA=$(awk -F ";" '{print $14}' <<< "${ligne}" | xargs) + EQUIPE_AGORA=${EQUIPE_AGORA,,} + QUOTA=$(awk -F ";" '{print $15}' <<< "${ligne}" | xargs) + PASSWORD=$(awk -F ";" '{print $16}' <<< "${ligne}" | xargs) + + IDENT_KAZ=$(unaccent utf8 "${PRENOM,,}.${NOM,,}") + EMAIL_SOUHAITE=${tab_email[EMAIL_SOUHAITE]} + EMAIL_SECOURS=${tab_email[EMAIL_SECOURS]} + + echo -e "${NL}***************************** traitement de ${ligne}" | tee -a "${LOG}" + + ########################### + # Vérification des champs # + ########################### + + for k in "${!tab_email[@]}"; do + validator "${tab_email[${k}]}" + done + + # vérif des champs O/N + for k in "${!service[@]}"; do + if [ "${service[${k}]}" != "O" -a "${service[${k}]}" != "N" ]; then + ( + echo "${RED}" + echo "${k} : ${service[${k}]}" + echo "ERREUR : le paramètre ${k} accepte O ou N - on stoppe tout - aucun utilisateur de créé" + echo "${NC}" + ) | tee -a "${LOG}" + exit 1 + fi + done + + # taille ORGA et EQUIPE_AGORA + TAILLE_MAX="23" + if [ "${#ORGA}" -gt "${TAILLE_MAX}" ]; then + ( + echo "${RED}" + echo "ERREUR : le paramètre ORGA est trop grand : ${ORGA} , taille max : ${TAILLE_MAX} - on stoppe tout - aucun utilisateur de créé" + echo "${NC}" + ) | tee -a "${LOG}" + exit 1 + fi + if [ "${#ORGA}" -gt "0" ]; then + if [[ "${ORGA}" =~ ^[[:alnum:]-]+$ ]]; then + echo "ok" + else + ( + echo "${RED}" + echo "ERREUR : le paramètre ORGA ne contient pas les caractères autorisés : ${ORGA} - on stoppe tout - aucun utilisateur de créé" + echo "${NC}" + ) | tee -a "${LOG}" + exit 1 + fi + fi + + if [ "${#EQUIPE_AGORA}" -gt "${TAILLE_MAX}" ]; then + ( + echo "${RED}" + echo "ERREUR : le paramètre EQUIPE_AGORA est trop grand : ${EQUIPE_AGORA} , taille max : ${TAILLE_MAX} - on stoppe tout - aucun utilisateur de créé" + echo "${NC}" + ) | tee -a "${LOG}" + exit 1 + fi + + # vérif quota est entier + if ! [[ "${QUOTA}" =~ ^[[:digit:]]+$ ]]; then + ( + echo + echo "ERREUR : ${RED}${BOLD}QUOTA n'est pas numérique : ${QUOTA}${NC} - on stoppe tout - aucun utilisateur de créé" + ) | tee -a "${LOG}" + fi + + #################################################### + # cree un mdp acceptable par postfix/nc/mattermost # + #################################################### + if [ -z ${PASSWORD} ]; then + PASSWORD=_`apg -n 1 -m 10 -M NCL -d`_ + fi + SEND_MSG_CREATE= + if [ -n "${ORGA}" -a -z "${CREATE_ORGA}" ]; then + # skeep orga + continue + fi + + #################################################################### + # TODO: Test de l'identKAZ du ldap, il faut l'unicité. si KO, STOP # + #################################################################### + + ################################### + # Création du compe de messagerie # + ################################### + # le mail existe t-il déjà ? + if grep -q "^${EMAIL_SOUHAITE}$" "${TFILE_EMAIL}"; then + echo "${EMAIL_SOUHAITE} existe déjà" | tee -a "${LOG}" + else + SEND_MSG_CREATE=true + echo "${EMAIL_SOUHAITE} n'existe pas" | tee -a "${LOG}" + echo "${SETUP_MAIL} email add ${EMAIL_SOUHAITE} ${PASSWORD}" | tee -a "${CMD_LOGIN}" + echo "${SETUP_MAIL} quota set ${EMAIL_SOUHAITE} ${QUOTA}G" | tee -a "${CMD_LOGIN}" + # LDAP, à tester + user=$(echo ${EMAIL_SOUHAITE} | awk -F '@' '{print $1}') + domain=$(echo ${EMAIL_SOUHAITE} | awk -F '@' '{print $2}') + pass=$(mkpasswd -m sha512crypt ${PASSWORD}) + echo "echo -e '\n\ndn: cn=${EMAIL_SOUHAITE},ou=users,${ldap_root}\n\ +changeType: add\n\ +objectclass: inetOrgPerson\n\ +objectClass: PostfixBookMailAccount\n\ +objectClass: nextcloudAccount\n\ +objectClass: kaznaute\n\ +sn: ${PRENOM} ${NOM}\n\ +mail: ${EMAIL_SOUHAITE}\n\ +mailEnabled: TRUE\n\ +mailGidNumber: 5000\n\ +mailHomeDirectory: /var/mail/${domain}/${user}/\n\ +mailQuota: ${QUOTA}G\n\ +mailStorageDirectory: maildir:/var/mail/${domain}/${user}/\n\ +mailUidNumber: 5000\n\ +mailDeSecours: ${EMAIL_SECOURS}\n\ +identifiantKaz: ${IDENT_KAZ}\n\ +quota: ${QUOTA}\n\ +nextcloudEnabled: TRUE\n\ +nextcloudQuota: ${QUOTA} GB\n\ +mobilizonEnabled: TRUE\n\ +agoraEnabled: TRUE\n\ +userPassword: {CRYPT}${pass}\n\n' | ldapmodify -c -H ldap://${LDAP_IP} -D \"cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}\" -x -w ${ldap_LDAP_ADMIN_PASSWORD}" | tee -a "${CMD_LOGIN}" + fi +#userPassword: {CRYPT}\$6\$${pass}\n\n\" | ldapmodify -c -H ldap://${LDAP_IP} -D \"cn=${ldap_LDAP_CONFIG_ADMIN_USERNAME},${ldap_root}\" -x -w ${ldap_LDAP_CONFIG_ADMIN_PASSWORD}" | tee -a "${CMD_LOGIN}" + + CREATE_ORGA_SERVICES="" + + ############# + # NEXTCLOUD # + ############# + # on recalcul l'url de NC + if [ "${ORGA}" != "" -a "${service[NC_ORGA]}" == "O" ]; then + URL_NC="${ORGA}-${cloudHost}.${domain}" + + # si le cloud de l'orga n'est pas up alors on le créé + nb=$(docker ps | grep "${ORGA}-${cloudHost}" | wc -l) + if [ "${nb}" == "0" ];then + echo " * +cloud +collabora ${ORGA}" + CREATE_ORGA_SERVICES="${CREATE_ORGA_SERVICES} +cloud +collabora" + # installe les plugins initiaux dans "/kaz/bin/gestClouds.sh" + fi + + NB_SERVICES_DEDIES=$((NB_SERVICES_DEDIES+1)) + + else + URL_NC="${cloudHost}.${domain}" + NB_SERVICES_BASE=$((NB_SERVICES_BASE+1)) + fi + + MESSAGE_MAIL_ORGA_1="${MESSAGE_MAIL_ORGA_1}${NL}* un bureau virtuel pour stocker des fichiers/calendriers/contacts et partager avec vos connaissances : ${httpProto}://${URL_NC}" + + # le user existe t-il déjà sur NC ? + curl -o "${TEMP_USER_NC}" -X GET -H 'OCS-APIRequest:true' "${httpProto}://admin:${nextcloud_NEXTCLOUD_ADMIN_PASSWORD}@${URL_NC}/ocs/v1.php/cloud/users?search=${IDENT_KAZ}" + if grep -q "${IDENT_KAZ}" "${TEMP_USER_NC}"; then + echo "${IDENT_KAZ} existe déjà sur ${URL_NC}" | tee -a "${LOG}" + else + + # on créé l'utilisateur sur NC sauf si c'est le NC général, on ne créé jamais l'utilisateur7 + if [ ${URL_NC} != "${cloudHost}.${domain}" ]; then + + echo "curl -X POST -H 'OCS-APIRequest:true' ${httpProto}://admin:${nextcloud_NEXTCLOUD_ADMIN_PASSWORD}@${URL_NC}/ocs/v1.php/cloud/users \ +-d userid='${IDENT_KAZ}' \ +-d displayName='${PRENOM} ${NOM}' \ +-d password='${PASSWORD}' \ +-d email='${EMAIL_SOUHAITE}' \ +-d quota='${QUOTA}GB' \ +-d language='fr' \ +" | tee -a "${CMD_INIT}" + + fi + + # s'il est admin de son orga, on le met admin + if [ "${service[ADMIN_ORGA]}" == "O" -a "${ORGA}" != "" -a "${service[NC_ORGA]}" == "O" ]; then + echo "curl -X POST -H 'OCS-APIRequest:true' ${httpProto}://${nextcloud_NEXTCLOUD_ADMIN_USER}:${nextcloud_NEXTCLOUD_ADMIN_PASSWORD}@${URL_NC}/ocs/v1.php/cloud/users/${IDENT_KAZ}/groups -d groupid='admin'" | tee -a "${CMD_INIT}" + fi + + # faut-il mettre le user NC dans un groupe particulier sur le NC de base ? + if [ "${GROUPE_NC_BASE}" != "" -a "${service[NC_BASE]}" == "O" ]; then + # le groupe existe t-il déjà ? + curl -o "${TEMP_GROUP_NC}" -X GET -H 'OCS-APIRequest:true' "${httpProto}://admin:${nextcloud_NEXTCLOUD_ADMIN_PASSWORD}@${URL_NC}/ocs/v1.php/cloud/groups?search=${GROUPE_NC_BASE}" + nb=$(grep "${GROUPE_NC_BASE}" "${TEMP_GROUP_NC}" | wc -l) + if [ "${nb}" == "0" ];then + echo "curl -X POST -H 'OCS-APIRequest:true' ${httpProto}://admin:${nextcloud_NEXTCLOUD_ADMIN_PASSWORD}@${URL_NC}/ocs/v1.php/cloud/groups -d groupid=${GROUPE_NC_BASE}" | tee -a "${CMD_INIT}" + fi + # puis attacher le user au groupe + echo "curl -X POST -H 'OCS-APIRequest:true' ${httpProto}://admin:${nextcloud_NEXTCLOUD_ADMIN_PASSWORD}@${URL_NC}/ocs/v1.php/cloud/users/${IDENT_KAZ}/groups -d groupid=${GROUPE_NC_BASE}" | tee -a "${CMD_INIT}" + fi + fi + + ############# + # WORDPRESS # + ############# + + # TODO : pour l'utilisation de l'api : https://www.hostinger.com/tutorials/wordpress-rest-api + + if [ "${ORGA}" != "" -a "${service[WP_ORGA]}" == "O" ]; then + + URL_WP_ORGA="${ORGA}-${wordpressHost}.${domain}" + + # si le wp de l'orga n'est pas up alors on le créé + nb=$(docker ps | grep "${ORGA}-${wordpressHost}" | wc -l) + if [ "${nb}" == "0" ];then + echo " * +wp ${ORGA}" + CREATE_ORGA_SERVICES="${CREATE_ORGA_SERVICES} +wp" + fi + + NB_SERVICES_DEDIES=$((NB_SERVICES_DEDIES+1)) + MESSAGE_MAIL_ORGA_1="${MESSAGE_MAIL_ORGA_1}${NL}* un site web de type wordpress : ${httpProto}://${URL_WP_ORGA}/wp-admin/" + + # TODO : vérif existance user + # # le user existe t-il déjà sur le wp ? + # curl -o "${TEMP_USER_WP}" -X GET "${httpProto}://${wp_WORDPRESS_ADMIN_USER}:${wp_WORDPRESS_ADMIN_PASSWORD}@${URL_WP_ORGA}/ocs/v1.php/cloud/users?search=${IDENT_KAZ}" + # nb_user_wp_orga=$(grep "${IDENT_KAZ}" "${TEMP_USER_WP}" | wc -l) + # if [ "${nb_user_wp_orga}" != "0" ];then + # ( + # echo "${RED}" + # echo "ERREUR : ${IDENT_KAZ} existe déjà sur ${URL_WP_ORGA} - on stoppe tout - aucun utilisateur de créé" + # echo "${NC}" + # ) | tee -a "${LOG}" + # + # # ROLLBACK - on vire le user de NC + # if [ "${nb_user_nc_orga}" != "0" ];then + # ( + # echo "${RED}" + # echo "ERREUR : ${IDENT_KAZ} existe déjà sur ${URL_NC} - on stoppe tout - aucun utilisateur de créé" + # echo "${NC}" + # ) | tee -a "${LOG}" + # + # # on supprime l'utilisateur sur NC. + # echo "curl -X DELETE -H 'OCS-APIRequest:true' ${httpProto}://admin:${nextcloud_NEXTCLOUD_ADMIN_PASSWORD}@${URL_NC}/ocs/v1.php/cloud/users \ + # -d userid='${IDENT_KAZ}' \ + # " | tee -a "${CMD_INIT}" + # fi + # + # exit 1 + # fi + + # TODO : créer le user et le mettre admin si nécessaire + # if [ "${service[ADMIN_ORGA]}" == "O" ]; then + # : + # else + # : + # fi + fi + + ############ + # PAHEKO # + ############ + + + if [ "${ORGA}" != "" -a "${service[PAHEKO_ORGA]}" == "O" ]; then + + URL_PAHEKO_ORGA="${ORGA}-${pahekoHost}.${domain}" + + # il n'y a pas de docker spécifique paheko (je cree toujours paheko) + echo " * +paheko ${ORGA}" + CREATE_ORGA_SERVICES="${CREATE_ORGA_SERVICES} +paheko" + + NB_SERVICES_DEDIES=$((NB_SERVICES_DEDIES+1)) + MESSAGE_MAIL_ORGA_1="${MESSAGE_MAIL_ORGA_1}${NL}* un service de gestion adhérents/clients : ${httpProto}://${URL_PAHEKO_ORGA}" + + if [ "${service[ADMIN_ORGA]}" == "O" ]; then + MESSAGE_MAIL_ORGA_1="${MESSAGE_MAIL_ORGA_1} (l'installation est à terminer en vous rendant sur le site)" + fi + fi + + + ############ + # DOKUWIKI # + ############ + + if [ "${ORGA}" != "" -a "${service[WIKI_ORGA]}" == "O" ]; then + + URL_WIKI_ORGA="${ORGA}-${dokuwikiHost}.${domain}" + + # si le wiki de l'orga n'est pas up alors on le créé + nb=$(docker ps | grep "${ORGA}-${dokuwikiHost}" | wc -l) + if [ "${nb}" == "0" ];then + echo " * +wiki ${ORGA}" + CREATE_ORGA_SERVICES="${CREATE_ORGA_SERVICES} +wiki" + fi + + NB_SERVICES_DEDIES=$((NB_SERVICES_DEDIES+1)) + MESSAGE_MAIL_ORGA_1="${MESSAGE_MAIL_ORGA_1}${NL}* un wiki dédié pour votre documentation : ${httpProto}://${URL_WIKI_ORGA}" + + + # TODO : ??? à voir https://www.dokuwiki.org/devel:xmlrpc:clients + + if grep -q "^${IDENT_KAZ}:" "${DOCK_VOL}/orga_${ORGA}-wikiConf/_data/users.auth.php" 2>/dev/null; then + echo "${IDENT_KAZ} existe déjà sur ${URL_WIKI_ORGA}" | tee -a "${LOG}" + else + echo "echo \"${IDENT_KAZ}:$(htpasswd -bnBC 10 "" ${PASSWORD}):${PRENOM} ${NOM}:${EMAIL_SOUHAITE}:admin,user\" >> \"${DOCK_VOL}/orga_${ORGA}-wikiConf/_data/users.auth.php\"" | tee -a "${CMD_INIT}" + fi + fi + + ############## + # MATTERMOST # + ############## + + # on ne gère pas la création du docker dédié mattermost + if [ "${ORGA}" != "" -a "${service[AGORA_ORGA]}" == "O" ]; then + echo "# ******************************************************************************" | tee -a "${CMD_INIT}" + echo "# **************************** ATTENTION ***************************************" | tee -a "${CMD_INIT}" + echo "# ******************************************************************************" | tee -a "${CMD_INIT}" + echo "# Mattermost dédié : on ne fait rien." | tee -a "${CMD_INIT}" + echo "# ******************************************************************************" | tee -a "${CMD_INIT}" + fi + + if grep -q "^${IDENT_KAZ}$" "${TFILE_MM}" 2>/dev/null; then + echo "${IDENT_KAZ} existe déjà sur mattermost" | tee -a "${LOG}" + else + # on créé le compte mattermost + echo "docker exec -ti mattermostServ bin/mmctl user create --email ${EMAIL_SOUHAITE} --username ${IDENT_KAZ} --password ${PASSWORD}" | tee -a "${CMD_LOGIN}" + # et enfin on ajoute toujours le user à l'équipe KAZ et aux 2 channels publiques + echo "docker exec -ti mattermostServ bin/mmctl team users add kaz ${EMAIL_SOUHAITE}" | tee -a "${CMD_LOGIN}" + echo "docker exec -ti mattermostServ bin/mmctl channel users add kaz:une-question--un-soucis ${EMAIL_SOUHAITE}" | tee -a "${CMD_LOGIN}" + echo "docker exec -ti mattermostServ bin/mmctl channel users add kaz:cafe-du-commerce--ouvert-2424h ${EMAIL_SOUHAITE}" | tee -a "${CMD_LOGIN}" + NB_SERVICES_BASE=$((NB_SERVICES_BASE+1)) + fi + + if [ "${EQUIPE_AGORA}" != "" -a "${EQUIPE_AGORA}" != "kaz" ]; then + # l'équipe existe t-elle déjà ? + nb=$(docker exec mattermostServ bin/mmctl team list | grep -w "${EQUIPE_AGORA}" | wc -l) + if [ "${nb}" == "0" ];then # non, on la créé en mettant le user en admin de l'équipe + echo "docker exec -ti mattermostServ bin/mmctl team create --name ${EQUIPE_AGORA} --display_name ${EQUIPE_AGORA} --email ${EMAIL_SOUHAITE}" --private | tee -a "${CMD_INIT}" + fi + # puis ajouter le user à l'équipe + echo "docker exec -ti mattermostServ bin/mmctl team users add ${EQUIPE_AGORA} ${EMAIL_SOUHAITE}" | tee -a "${CMD_INIT}" + fi + + if [ -n "${CREATE_ORGA_SERVICES}" ]; then + SEND_MSG_CREATE=true + echo "${CREATE_ORGA_CMD}" --create ${CREATE_ORGA_SERVICES} "${ORGA}" | tee -a "${CMD_ORGA}" + echo "${CREATE_ORGA_CMD}" --init ${CREATE_ORGA_SERVICES} "${ORGA}" | tee -a "${CMD_FIRST}" + ALL_ORGA="${ALL_ORGA} ${ORGA}" + fi + + ########################## + # Inscription newsletter # + ########################## + + # TODO : utiliser liste sur dev également + + # on inscrit le user sur sympa, à la liste infos@${domain_sympa} + # docker exec -ti sympaServ /usr/lib/sympa/bin/sympa_soap_client.pl --soap_url=https://listes.kaz.sns/sympasoap --trusted_application=SOAP_USER --trusted_application_password=SOAP_PASSWORD --proxy_vars="USER_EMAIL=contact1@kaz.sns" --service=which + if [[ "${mode}" = "dev" ]]; then + echo "# DEV, on teste l'inscription à sympa"| tee -a "${CMD_SYMPA}" + LISTMASTER=$(echo ${sympa_LISTMASTERS} | cut -d',' -f1) + echo "docker exec -ti sympaServ /usr/lib/sympa/bin/sympa_soap_client.pl --soap_url=${httpProto}://${URL_LISTE}/sympasoap --trusted_application=${sympa_SOAP_USER} --trusted_application_password=${sympa_SOAP_PASSWORD} --proxy_vars=\"USER_EMAIL=${LISTMASTER}\" --service=add --service_parameters=\"${NL_LIST},${EMAIL_SOUHAITE}\"" | tee -a "${CMD_SYMPA}" + else + echo "# PROD, on inscrit à sympa"| tee -a "${CMD_SYMPA}" + LISTMASTER=$(echo ${sympa_LISTMASTERS} | cut -d',' -f1) + echo "docker exec -ti sympaServ /usr/lib/sympa/bin/sympa_soap_client.pl --soap_url=${httpProto}://${URL_LISTE}/sympasoap --trusted_application=${sympa_SOAP_USER} --trusted_application_password=${sympa_SOAP_PASSWORD} --proxy_vars=\"USER_EMAIL=${LISTMASTER}\" --service=add --service_parameters=\"${NL_LIST},${EMAIL_SOUHAITE}\"" | tee -a "${CMD_SYMPA}" + echo "docker exec -ti sympaServ /usr/lib/sympa/bin/sympa_soap_client.pl --soap_url=${httpProto}://${URL_LISTE}/sympasoap --trusted_application=${sympa_SOAP_USER} --trusted_application_password=${sympa_SOAP_PASSWORD} --proxy_vars=\"USER_EMAIL=${LISTMASTER}\" --service=add --service_parameters=\"${NL_LIST},${EMAIL_SECOURS}\"" | tee -a "${CMD_SYMPA}" + fi + + if [ "${service[ADMIN_ORGA]}" == "O" ]; then + MESSAGE_MAIL_ORGA_2="${MESSAGE_MAIL_ORGA_2}Comme administrateur de votre organisation, vous pouvez créer des listes de diffusion en vous rendant sur ${httpProto}://${URL_LISTE}" + fi + + ################### + # update paheko # + ################### + + # TODO : problème si 2 comptes partagent le même email souhaité (cela ne devrait pas arriver) + curl -s "https://${paheko_API_USER}:${paheko_API_PASSWORD}@kaz-paheko.kaz.bzh/api/sql" -d "SELECT numero,nom,quota_disque from users WHERE email='${EMAIL_SOUHAITE}'" | jq '.results[] | .numero,.nom,.quota_disque ' | tr \\n ',' | sed 's/,$/,Aucune\n/' >> "${TEMP_PAHEKO}" + + #################### + # Inscription MAIL # + #################### + + if [ "${NB_SERVICES_DEDIES}" != "0" ];then + MESSAGE_MAIL_ORGA_1="${NL} dont ${NB_SERVICES_DEDIES} service(s) dédié(s) pour votre organisation:${NL} ${MESSAGE_MAIL_ORGA_1}" + fi + + if [ -z "${SEND_MSG_CREATE}" ]; then + # rien de créé => pas de message + continue + fi + + #si admin alors msg pour indiquer qu'il faut nous renvoyer ce qu'il souhaite comme service. + if [ "${service[ADMIN_ORGA]}" == "O" ]; then + MESSAGE_MAIL_ORGA_3="${MESSAGE_MAIL_ORGA_3}En tant qu'association/famille/société. Vous avez la possibilité d'ouvrir, quand vous le voulez, des services kaz, il vous suffit de nous le demander. + +Pourquoi n'ouvrons-nous pas tous les services tout de suite ? parce que nous aimons la sobriété et que nous préservons notre espace disque ;) +A quoi sert d'avoir un site web si on ne l'utilise pas, n'est-ce pas ? + +Par retour de mail, dites-nous de quoi vous avez besoin tout de suite entre: +* une comptabilité : un service de gestion adhérents/clients +* un site web de type WordPress +* un cloud : bureau virtuel pour stocker des fichiers/calendriers/contacts et partager avec vos connaissances + +Une fois que vous aurez répondu à ce mail, votre demande sera traitée manuellement. +" + fi + + # on envoie le mail de bienvenue + MAIL_KAZ="Bonjour, + +Bienvenue chez KAZ! + +Vous disposez de $((${NB_SERVICES_BASE} + ${NB_SERVICES_DEDIES})) services kaz avec authentification : + +* une messagerie classique : ${httpProto}://${URL_WEBMAIL} +* une messagerie instantanée pour discuter au sein d'équipes : ${httpProto}://${URL_AGORA} + +Votre email et identifiant pour tous ces services : ${EMAIL_SOUHAITE} +Le mot de passe : ${PASSWORD} + +Pour changer votre mot de passe de messagerie, c'est ici: ${httpProto}://${URL_MDP} +Si vous avez perdu votre mot de passe, c'est ici: ${httpProto}://${URL_MDP}/?action=sendtoken + +Vous pouvez accéder à votre messagerie : +* soit depuis votre webmail : ${httpProto}://${URL_WEBMAIL} +* soit depuis votre bureau virtuel : ${httpProto}://${URL_NC} +* soit depuis un client de messagerie comme thunderbird + +${MESSAGE_MAIL_ORGA_3} + +Vous avez quelques docs intéressantes sur le wiki de kaz: + +* Migrer son site internet wordpress vers kaz +https://wiki.kaz.bzh/wordpress/start#migrer_son_site_wordpress_vers_kaz + +* Migrer sa messagerie vers kaz +https://wiki.kaz.bzh/messagerie/gmail/start + +* Démarrer simplement avec son cloud +https://wiki.kaz.bzh/nextcloud/start + +Votre quota est de ${QUOTA}GB. Si vous souhaitez plus de place pour vos fichiers ou la messagerie, faites-nous signe ! + +Pour accéder à la messagerie instantanée et communiquer avec les membres de votre équipe ou ceux de kaz : ${httpProto}://${URL_AGORA}/login + +${MESSAGE_MAIL_ORGA_2} + +Enfin, vous disposez de tous les autres services KAZ où l'authentification n'est pas nécessaire : ${httpProto}://${URL_SITE} + +En cas de soucis, n'hésitez pas à poser vos questions sur le canal 'Une question ? un soucis' de l'agora dispo ici : ${httpProto}://${URL_AGORA} + +Si vous avez besoin d'accompagnement pour votre site, votre cloud, votre compta, votre migration de messagerie,... nous proposons des formations mensuelles gratuites. Si vous souhaitez être accompagné par un professionnel, nous pouvons vous donner une liste de pros, référencés par KAZ. + +À bientôt ;) + +La collégiale de KAZ. " + + echo "docker exec -i mailServ mailx -a 'Content-Type: text/plain; charset=\"UTF-8\"' -r contact@kaz.bzh -s \"KAZ: confirmation d'inscription\" ${EMAIL_SOUHAITE} ${EMAIL_SECOURS} << EOF +${MAIL_KAZ} +EOF" | tee -a "${CMD_MSG}" + + # on envoie le mail de confirmation d'inscription à contact + MAIL_KAZ="*****POST AUTOMATIQUE****** +Hello, +${NOM} ${PRENOM} vient d'être inscrit avec l'email ${EMAIL_SOUHAITE} +quota : ${QUOTA}GB + +NC_BASE : ${service[NC_BASE]} +groupe NC base : ${GROUPE_NC_BASE} +équipe agora base : ${EQUIPE_AGORA} +email de secours : ${EMAIL_SECOURS} + +ORGA : ${ORGA} +ADMIN_ORGA : ${service[ADMIN_ORGA]} +NC_ORGA : ${service[NC_ORGA]} +PAHEKO_ORGA : ${service[PAHEKO_ORGA]} +WP_ORGA : ${service[WP_ORGA]} +AGORA_ORGA : ${service[AGORA_ORGA]} +WIKI_ORGA : ${service[WIKI_ORGA]} + +bisou!" + echo "docker exec -i mailServ mailx -a 'Content-Type: text/plain; charset=\"UTF-8\"' -r contact@kaz.bzh -s \"KAZ: confirmation d'inscription\" ${EMAIL_CONTACT} << EOF +${MAIL_KAZ} +EOF" | tee -a "${CMD_MSG}" + + echo " # on envoie la confirmation d'inscription sur l'agora " | tee -a "${CMD_MSG}" + echo "docker exec -ti mattermostServ bin/mmctl post create kaz:Creation-Comptes --message \"${MAIL_KAZ}\"" | tee -a "${CMD_MSG}" + + # fin des inscriptions +done <<< "${ALL_LINES}" + +if [[ -n "${ALL_ORGA}" ]]; then + echo "sleep 2" | tee -a "${CMD_PROXY}" + echo "${KAZ_BIN_DIR}/container.sh start ${ALL_ORGA}" | tee -a "${CMD_PROXY}" + for item in "${availableProxyComposes[@]}"; do + echo "cd \"${KAZ_COMP_DIR}/${item}/\"; ./proxy-gen.sh; docker-compose up -d; ./reload.sh " | tee -a "${CMD_PROXY}" + done +fi + +########################### +# Lancement des commandes # +########################### +if [ "${SIMULATION}" == "NO" ]; then + echo "on exécute" + "${CMD_LOGIN}" + # on attend qques secondes que le mail soit bien créé avant de continuer (prob de lecture de la BAL : à investiguer) + # je rallonge à 20s car je vois que le docker sympa ne connait pas toujours l'email kaz créé + echo "on attend 20s pour que la création des emails soit certaine" + sleep 20 + "${CMD_SYMPA}" + "${CMD_ORGA}" + "${CMD_PROXY}" + "${CMD_FIRST}" + "${CMD_INIT}" + "${CMD_PAHEKO}" + "${CMD_MSG}" +else + echo "Aucune commande n'a été lancée : Possibilité de le faire à la main. cf ${KAZ_ROOT}/tmp/${RACINE}_cmds_to_run-*.sh" +fi + +# END diff --git a/bin/cron-cloud.sh b/bin/cron-cloud.sh new file mode 100755 index 0000000..742bb54 --- /dev/null +++ b/bin/cron-cloud.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +for cloud in $(docker ps | grep -i nextcloudServ |awk '{print $12}') +do + docker exec -u www-data $cloud php cron.php + +done diff --git a/bin/dns.sh b/bin/dns.sh new file mode 100755 index 0000000..d09625a --- /dev/null +++ b/bin/dns.sh @@ -0,0 +1,209 @@ +#!/bin/bash + +# list/ajout/supprime/ un sous-domaine + +KAZ_ROOT=$(cd "$(dirname $0)"/..; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars +. "${DOCKERS_ENV}" + +cd "${KAZ_ROOT}" +export PRG="$0" +export IP="127.0.0.1" +export ETC_HOSTS="/etc/hosts" + +# no more export in .env +export $(set | grep "domain=") + +declare -a forbidenName +forbidenName=(${calcHost} calc ${cloudHost} bureau ${dateHost} date ${dokuwikiHost} dokuwiki ${fileHost} file ${ldapHost} ${pahekoHost} ${gitHost} ${gravHost} ${matterHost} ${officeHost} collabora ${padHost} ${sympaHost} listes ${webmailHost} ${wordpressHost} www ${vigiloHost} form) + +export FORCE="NO" +export CMD="" +export SIMU="" + +usage(){ + echo "Usage: ${PRG} list [sub-domain...]" + echo " ${PRG} [-n] [-f] {add/del} sub-domain..." + echo " -h help" + echo " -n simulation" + echo " -f force protected domain" + exit 1 +} + +for ARG in $@ +do + case "${ARG}" in + '-h' | '-help' ) + usage + ;; + '-f' ) + shift + export FORCE="YES" + ;; + '-n' ) + shift + export SIMU="echo" + ;; + 'list'|'add'|'del' ) + shift + CMD="${ARG}" + break + ;; + * ) + usage + ;; + esac +done + +if [ -z "${CMD}" ]; then + usage +fi + +. "${KAZ_KEY_DIR}/env-gandi" + +if [[ -z "${GANDI_KEY}" ]] ; then + echo + echo "no GANDI_KEY set in ${KAZ_KEY_DIR}/env-gandi" + usage +fi + + +waitNet () { + if [[ "${domain}" = "kaz.local" ]]; then + return + fi + + ### wait when error code 503 + if [[ $(curl -H "authorization: Apikey ${GANDI_KEY}" --connect-timeout 2 -s -D - "${GANDI_API}" -o /dev/null 2>/dev/null | head -n1) != *200* ]]; then + echo "DNS not available. Please wait..." + while [[ $(curl -H "authorization: Apikey ${GANDI_KEY}" --connect-timeout 2 -s -D - "${GANDI_API}" -o /dev/null 2>/dev/null | head -n1) != *200* ]] + do + sleep 5 + done + exit + fi +} + +list(){ + if [[ "${domain}" = "kaz.local" ]]; then + grep --perl-regex "^${IP}\s.*${domain}" "${ETC_HOSTS}" 2> /dev/null | sed -e "s|^${IP}\s*\([0-9a-z.-]${domain}\)$|\1|g" + return + fi + waitNet + trap 'rm -f "${TMPFILE}"' EXIT + TMPFILE="$(mktemp)" || exit 1 + if [[ -n "${SIMU}" ]] ; then + ${SIMU} curl -X GET "${GANDI_API}/records" -H "authorization: Apikey ${GANDI_KEY}" + else + curl -X GET "${GANDI_API}/records" -H "authorization: Apikey ${GANDI_KEY}" 2>/dev/null | \ + sed "s/,{/\n/g" | \ + sed 's/.*rrset_name":"\([^"]*\)".*rrset_values":\["\([^"]*\)".*/\1:\2/g'| \ + grep -v '^[_@]'| \ + grep -e ":${domain}\.*$" -e ":prod[0-9]*$" > ${TMPFILE} + fi + if [ $# -lt 1 ]; then + cat ${TMPFILE} + else + for ARG in $@ + do + cat ${TMPFILE} | grep "${ARG}.*:" + done + fi +} + +saveDns () { + for ARG in $@ ; do + if [[ "${ARG}" =~ .local$ ]] ; then + echo "${PRG}: old fasion style (remove .local at the end)" + usage; + fi + if [[ "${ARG}" =~ .bzh$ ]] ; then + echo "${PRG}: old fasion style (remove .bzh at the end)" + usage; + fi + if [[ "${ARG}" =~ .dev$ ]] ; then + echo "${PRG}: old fasion style (remove .dev at the end)" + usage; + fi + done + if [[ "${domain}" = "kaz.local" ]]; then + return + fi + waitNet + ${SIMU} curl -X POST "${GANDI_API}/snapshots" -H "authorization: Apikey ${GANDI_KEY}" 2>/dev/null +} + +badName(){ + [[ -z "$1" ]] && return 0; + for item in "${forbidenName[@]}"; do + [[ "${item}" == "$1" ]] && [[ "${FORCE}" == "NO" ]] && return 0 + done + return 1 +} + +add(){ + if [ $# -lt 1 ]; then + exit + fi + saveDns $@ + declare -a ADDED + for ARG in $@ + do + if badName "${ARG}" ; then + echo "can't manage '${ARG}'. Use -f option" + continue + fi + case "${domain}" in + kaz.local ) + if grep -q --perl-regex "^${IP}.*[ \t]${ARG}.${domain}" "${ETC_HOSTS}" 2> /dev/null ; then + break + fi + if grep -q --perl-regex "^${IP}[ \t]" "${ETC_HOSTS}" 2> /dev/null ; then + ${SIMU} sudo sed -i -e "0,/^${IP}[ \t]/s/^\(${IP}[ \t]\)/\1${ARG}.${domain} /g" "${ETC_HOSTS}" + else + ${SIMU} sudo sed -i -e "$ a ${IP}\t${ARG}.${domain}" "${ETC_HOSTS}" 2> /dev/null + fi + ;; + *) + ${SIMU} curl -X POST "${GANDI_API}/records" -H "authorization: Apikey ${GANDI_KEY}" -H 'content-type: application/json' -d '{"rrset_type":"CNAME", "rrset_name":"'${ARG}'", "rrset_values":["'${site}'"]}' + echo + ;; + esac + ADDED+=("${ARG}") + done + echo "Domains added to ${domain}: ${ADDED[@]}" +} + +del(){ + if [ $# -lt 1 ]; then + exit + fi + saveDns $@ + declare -a REMOVED + for ARG in $@ + do + if badName "${ARG}" ; then + echo "can't manage '${ARG}'. Use -f option" + continue + fi + case "${domain}" in + kaz.local ) + if !grep -q --perl-regex "^${IP}.*[ \t]${ARG}.${domain}" "${ETC_HOSTS}" 2> /dev/null ; then + break + fi + ${SIMU} sudo sed -i -e "/^${IP}[ \t]*${ARG}.${domain}[ \t]*$/d" \ + -e "s|^\(${IP}.*\)[ \t]${ARG}.${domain}|\1|g" "${ETC_HOSTS}" + ;; + * ) + ${SIMU} curl -X DELETE "${GANDI_API}/records/${ARG}" -H "authorization: Apikey ${GANDI_KEY}" + echo + ;; + esac + REMOVED+=("${ARG}") + done + echo "Domains removed from ${domain}: ${REMOVED[@]}" +} + +#echo "CMD: ${CMD} $*" +${CMD} $* diff --git a/bin/envoiMails.sh b/bin/envoiMails.sh new file mode 100755 index 0000000..7346b89 --- /dev/null +++ b/bin/envoiMails.sh @@ -0,0 +1,66 @@ +#!/bin/bash + +#kan: 09/09/2022 +#koi: envoyer un mail à une liste (sans utiliser sympa sur listes.kaz.bzh) +#ki : fab + +#on récupère toutes les variables et mdp +# on prend comme source des repertoire le dossier du dessus ( /kaz dans notre cas ) +KAZ_ROOT=$(cd "$(dirname $0)"/..; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +SIMULATION=NO + +CMD="/tmp/envoiMail_cmds_to_run.sh" +echo "#!/bin/bash" > ${CMD} && chmod +x ${CMD} + +################################################################################################################# +MAIL_KAZ=" +KAZ, association morbihannaise, propose de \"dégoogliser\" l’internet avec des solutions et services numériques libres alternatifs à ceux des GAFAM. Elle invite les habitants et les élus de Vannes et de sa région à une réunion publique d’information et d’échange : +Le jeudi 22 septembre 2022 à 18 heures +à la Maison des associations, 31 rue Guillaume Le Bartz à Vannes. + +Cette invitation est destinée à toute personne sensible aux enjeux du numérique, aux risques pour la démocratie et les libertés, à sa participation au dérèglement climatique et à l’épuisement des ressources de notre planète. + +Nous dirons qui nous sommes, quelles sont nos valeurs et quels sont concrètement les solutions et services que nous proposons, leurs conditions d’accès et l’accompagnement utile pour leur prise en main. + +Les premières pierres de KAZ ont été posées voilà bientôt deux ans, en pleine pandémie de la COVID, par sept citoyens qui ont voulu répondre à l’appel de Framasoft de dégoogliser l’internet. Ne plus se lamenter sur la puissance des GAFAM, mais proposer des solutions et des services numériques alternatifs à ceux de Google, Amazon, Facebook, Apple et Microsoft en s’appuyant sur les fondamentaux Sobre, Libre, Éthique et Local. + +A ce jour, près de 200 particuliers ont ouvert une adresse @kaz.bz, plus de 80 organisations ont souscrit au bouquet de services de KAZ et près de 800 collaborateurs d’organisations utilisatrices des services de KAZ participent peu ou prou au réseau de KAZ. +Beaucoup de services sont gratuits et accessibles sur le site https://kaz.bzh. D’autres demandent l’ouverture d’un compte moyennant une petite participation financière de 10€ par an pour les particuliers et de 30€ par an pour les organisations. Ceci est permis par le bénévolat des membres de la collégiale qui dirigent l’association et administrent les serveurs et les services. + +A ce stade, de nombreuses questions se posent à KAZ. Quelle ambition de couverture de ses services auprès des particuliers et des organisations sur son territoire, le Morbihan ? Quel accompagnement dans la prise en main des outils ? Quels nouveaux services seraient utiles ? + +Nous serions heureux de votre participation à notre réunion publique du 22 septembre d'une durée totale de 2h : + * Présentation valeurs / contexte (15mn) + * Présentation outils (45mn) + * Questions / Réponses (1h) + +En restant à votre disposition, + +La Collégiale de KAZ +" +################################################################################################################# + +FIC_MAILS="/tmp/fic_mails" + +while read EMAIL_CIBLE +do + + echo "docker exec -i mailServ mailx -a 'Content-Type: text/plain; charset=\"UTF-8\"' -r contact@kaz.bzh -s \"Invitation rencontre KAZ jeudi 22 septembre à 18h00 à la Maison des assos à Vannes\" ${EMAIL_CIBLE} << EOF +${MAIL_KAZ} +EOF" | tee -a ${CMD} + + echo "sleep 2" | tee -a ${CMD} + +done < ${FIC_MAILS} + +# des commandes à lancer ? +if [ "${SIMULATION}" == "NO" ];then + echo "on exécute" + ${CMD} +else + echo "Aucune commande n'a été lancée: Possibilité de le faire à la main. cf ${CMD}" +fi + diff --git a/bin/foreign-domain.sh b/bin/foreign-domain.sh new file mode 100755 index 0000000..2343361 --- /dev/null +++ b/bin/foreign-domain.sh @@ -0,0 +1,240 @@ +#!/bin/bash + +# list/ajout/supprime/ les domaines extérieurs à kaz.bzh + +KAZ_ROOT=$(cd "$(dirname $0)"/..; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +export PRG="$0" +cd $(dirname $0) + +. "${DOCKERS_ENV}" + +LETS_DIR="/etc/letsencrypt/$([ "${mode}" == "local" ] && echo "local" || echo "live")" + +declare -a availableComposes availableOrga +availableComposes=(${pahekoHost} ${cloudHost} ${dokuwikiHost} ${wordpressHost} ${matterHost} ${castopodHost}) +availableOrga=($(sed -e "s/\(.*\)[ \t]*#.*$/\1/" -e "s/^[ \t]*\(.*\)-orga$/\1/" -e "/^$/d" "${KAZ_CONF_DIR}/container-orga.list")) +availableProxyComposes=($(getList "${KAZ_CONF_DIR}/container-proxy.list")) + +# no more export in .env +export $(set | grep "domain=") + +export CMD="" +export SIMU="" +export CHANGE="" + +usage(){ + echo "Usage: ${PRG} list [friend-domain...]" + echo " ${PRG} [-n] add orga [${pahekoHost} ${cloudHost} ${dokuwikiHost} ${wordpressHost} ${matterHost} ${castopodHost}] [friend-domain...] " + echo " ${PRG} [-n] del [friend-domain...]" + echo " ${PRG} -l" + echo " -l short list" + echo " -renewAll" + echo " -h help" + echo " -n simulation" + exit 1 +} + +export CERT_CFG="${KAZ_CONF_PROXY_DIR}/foreign-certificate" + +createCert () { + ( + fileName="${LETS_DIR}/$1-key.pem" + #[ -f "${fileName}" ] || return + # if [ -f "${fileName}" ]; then + # fileTime=$(stat --format='%Y' "${fileName}") + # current_time=$(date +%s) + # if (( "${fileTime}" > ( "${current_time}" - ( 60 * 60 * 24 * 89 ) ) )); then + # exit + # fi + # fi + printKazMsg "create certificat for $1" + ${SIMU} docker exec -i proxyServ bash -c "/opt/certbot/bin/certbot certonly -n --nginx -d $1" + ) + +} + +for ARG in $@; do + case "${ARG}" in + '-h' | '-help' ) + usage + ;; + '-n' ) + shift + export SIMU="echo" + ;; + '-renewAll') + for i in $("${KAZ_BIN_DIR}/foreign-domain.sh" -l); do + echo "$i" + createCert "$i" |grep failed + done + exit + ;; + '-l') + for compose in ${availableComposes[@]} ; do + grep "server_name" "${KAZ_CONF_PROXY_DIR}/${compose}_kaz_name" | sed -e "s/[ \t]*\([^#]*\)#.*/\1/g" -e "/^$/d" -e "s/.*server_name[ \t]\([^ ;]*\).*/\1/" + done + exit + ;; + 'list'|'add'|'del' ) + shift + CMD="${ARG}" + break + ;; + * ) + usage + ;; + esac +done + +if [ -z "${CMD}" ]; then + echo "Commande missing" + usage +fi + +######################################## +badDomaine () { + [[ -z "$1" ]] && return 0; + [[ ! "$1" =~ ^[-.a-zA-Z0-9]*$ ]] && return 0; + return 1 +} +badOrga () { + [[ -z "$1" ]] && return 0; + [[ ! " ${availableOrga[*]} " =~ " $1 " ]] && return 0 + return 1 +} +badCompose () { + [[ -z "$1" ]] && return 0; + [[ ! " ${availableComposes[*]} " =~ " $1 " ]] && return 0 + return 1 +} + +######################################## +listServ () { + for compose in ${availableComposes[@]} ; do + sed -e "s/[ \t]*\([^#]*\)#.*/\1/g" -e "/^$/d" -e "s/.*server_name[ \t]\([^ ;]*\).*/\1 : ${compose}/" "${KAZ_CONF_PROXY_DIR}/${compose}_kaz_name" + done +} + +listOrgaServ () { + for compose in ${availableComposes[@]} ; do + sed -e "s/[ \t]*\([^#]*\)#.*/\1/g" -e "/^$/d" -e "s/\([^ ]*\)[ \t]*\([^ \t;]*\).*/\1 => \2 : ${compose}/" "${KAZ_CONF_PROXY_DIR}/${compose}_kaz_map" + done +} + +######################################## +list () { + previousOrga=$(listOrgaServ) + previousServ=$(listServ) + if [ $# -lt 1 ]; then + [ -n "${previousOrga}" ] && echo "${previousOrga}" + [ -n "${previousServ}" ] && echo "${previousServ}" + return + fi + for ARG in $@ + do + orga=$(echo "${previousOrga}" | grep "${ARG}.* =>") + serv=$(echo "${previousServ}" | grep "${ARG}.* =>") + [ -n "${orga}" ] && echo "${orga}" + [ -n "${serv}" ] && echo "${serv}" + done +} + +######################################## +add () { + # $1 : orga + # $2 : service + # $3 : friend-domain + [ $# -lt 3 ] && usage + badOrga $1 && echo "bad orga: ${RED}$1${NC} not in ${GREEN}${availableOrga[@]}${NC}" && usage + badCompose $2 && echo "bad compose: ${RED}$2${NC} not in ${GREEN}${availableComposes[@]}${NC}" && usage + ORGA=$1 + COMPOSE=$2 + shift; shift + CLOUD_SERVNAME="${ORGA}-${nextcloudServName}" + CLOUD_CONFIG="${DOCK_VOL}/orga_${ORGA}-cloudConfig/_data/config.php" + + # XXX check compose exist in orga ? + # /kaz/bin/kazList.sh service enable ${ORGA} + if [ "${COMPOSE}" = "${cloudHost}" ]; then + if ! [[ "$(docker ps -f name=${CLOUD_SERVNAME} | grep -w ${CLOUD_SERVNAME})" ]]; then + printKazError "${CLOUD_SERVNAME} not running... abort" + exit + fi + fi + + for FRIEND in $@; do + badDomaine "${FRIEND}" && echo "bad domaine: ${RED}${FRIEND}${NC}" && usage + done + + for FRIEND in $@; do + createCert "${FRIEND}" + if [ "${COMPOSE}" = "${cloudHost}" ]; then + IDX=$(awk 'BEGIN {flag=0; cpt=0} /trusted_domains/ {flag=1} /)/ {if (flag) {print cpt+1; exit 0}} / => / {if (flag && cpt<$1) cpt=$1}' "${CLOUD_CONFIG}") + ${SIMU} docker exec -ti -u 33 "${CLOUD_SERVNAME}" /var/www/html/occ config:system:set trusted_domains "${IDX}" --value="${FRIEND}" + fi + + previousOrga=$(listOrgaServ | grep "${FRIEND}") + [[ " ${previousOrga}" =~ " ${FRIEND} => ${ORGA} : ${COMPOSE}" ]] && echo " - already done" && continue + [[ " ${previousOrga}" =~ " ${FRIEND} " ]] && echo " - ${YELLOW}${BOLD}$(echo "${previousOrga}" | grep -e "${FRIEND}")${NC} must be deleted before" && return + if [[ -n "${SIMU}" ]] ; then + echo "${FRIEND} ${ORGA}; => ${KAZ_CONF_PROXY_DIR}/${COMPOSE}_kaz_map" + cat < ${KAZ_CONF_PROXY_DIR}/${COMPOSE}_kaz_name +server_name ${FRIEND}; +EOF + else + echo "${FRIEND} ${ORGA};" >> "${KAZ_CONF_PROXY_DIR}/${COMPOSE}_kaz_map" + cat >> "${KAZ_CONF_PROXY_DIR}/${COMPOSE}_kaz_name" <\s*'${FRIEND}'/d" -i "${CLOUD_CONFIG}" + fi + ${SIMU} sed -e "/^[ \t]*${FRIEND}[ \t]/d" -i "${KAZ_CONF_PROXY_DIR}/${COMPOSE}_kaz_map" + fi + if grep -q -e "^[ \t]*server_name ${FRIEND};" "${KAZ_CONF_PROXY_DIR}/${COMPOSE}_kaz_name" ; then + ${SIMU} sed -i "${KAZ_CONF_PROXY_DIR}/${COMPOSE}_kaz_name" \ + -e "/^[ \t]*server_name ${FRIEND};/d" + fi + done + echo "${PRG}: ${FRIEND} deleted" + CHANGE="del" + done +} + +######################################## +${CMD} $@ + +if [ -n "${CHANGE}" ] ; then + echo "Reload proxy conf" + for item in "${availableProxyComposes[@]}"; do + ${SIMU} ${KAZ_COMP_DIR}/${item}/proxy-gen.sh + ${SIMU} "${KAZ_COMP_DIR}/proxy/reload.sh" + done +fi + +######################################## diff --git a/bin/gestContainers.sh b/bin/gestContainers.sh new file mode 100755 index 0000000..1678d0c --- /dev/null +++ b/bin/gestContainers.sh @@ -0,0 +1,648 @@ +#!/bin/bash +# Script de manipulation des containers en masse +# init /versions / restart ... +# + +KAZ_ROOT=$(cd "$(dirname $0)"/..; pwd) +. $KAZ_ROOT/bin/.commonFunctions.sh +setKazVars +. $DOCKERS_ENV +. $KAZ_ROOT/secret/SetAllPass.sh + +PRG=$(basename $0) + +#TODO: ce tab doit être construit à partir de la liste des machines dispos et pas en dur +tab_sites_destinations_possibles=("kazoulet" "prod2" "prod1") + +#GLOBAL VARS +NAS_VOL="/mnt/disk-nas1/docker/volumes/" + +availableOrga=($(getList "${KAZ_CONF_DIR}/container-orga.list")) +AVAILABLE_ORGAS=${availableOrga[*]//-orga/} + +availableContainersCommuns=( $(getList "${KAZ_CONF_DIR}/container-withMail.list") $(getList "${KAZ_CONF_DIR}/container-withoutMail.list")) + +OPERATE_ON_MAIN= # par defaut NON on ne traite que des orgas +OPERATE_ON_NAS_ORGA="OUI" # par defaut oui, on va aussi sur les orgas du NAS +OPERATE_LOCAL_ORGA="OUI" # par defaut oui + +TEMPO_ACTION_STOP=2 # Lors de redémarrage avec tempo, on attend après le stop +TEMPO_ACTION_START=60 # Lors de redémarrage avec tempo, avant de reload le proxy + +CONTAINERS_TYPES= + +defaultContainersTypes="cloud agora wp wiki office paheko castopod" # les containers gérés par ce script. + +declare -A DockerServNames # le nom des containers correspondant +DockerServNames=( [cloud]="${nextcloudServName}" [agora]="${mattermostServName}" [wiki]="${dokuwikiServName}" [wp]="${wordpressServName}" [office]="${officeServName}" [paheko]="${pahekoServName}" [castopod]="${castopodServName}" ) + +declare -A FilterLsVolume # Pour trouver quel volume appartient à quel container +FilterLsVolume=( [cloud]="cloudMain" [agora]="matterConfig" [wiki]="wikiConf" [wp]="wordpress" [castopod]="castopodMedia" ) + +declare -A composeDirs # Le nom du repertoire compose pour le commun +composeDirs=( [cloud]="cloud" [agora]="mattermost" [wiki]="dokuwiki" [office]="collabora" [paheko]="paheko" [castopod]="castopod" ) + +declare -A serviceNames # Le nom du du service dans le dockerfile d'orga +serviceNames=( [cloud]="cloud" [agora]="agora" [wiki]="dokuwiki" [wp]="wordpress" [office]="collabora" [castopod]="castopod") + +declare -A subScripts +subScripts=( [cloud]="manageCloud.sh" [agora]="manageAgora.sh" [wiki]="manageWiki.sh" [wp]="manageWp.sh" [castopod]="manageCastopod.sh" ) + +declare -A OrgasOnNAS +declare -A OrgasLocales +declare -A NbOrgas +declare -A RunningOrgas +declare -A Posts + +QUIET="1" # redirection des echo + +OCCCOMANDS=() +MMCTLCOMANDS=() +EXECCOMANDS=() + +# CLOUD +APPLIS_PAR_DEFAUT="tasks calendar contacts bookmarks mail richdocuments external drawio snappymail ransomware_protection" #rainloop richdocumentscode + + +usage() { +echo "${PRG} [OPTION] [CONTAINERS_TYPES] [COMMANDES] [ORGAS] +Ce script regroupe l'ensemble des opérations que l'on souhaite automatiser sur plusieurs containers +Par defaut, sur les orgas, mais on peut aussi ajouter les communs + +OPTIONS + -h|--help Cette aide :-) + -n|--simu SIMULATION + -q|--quiet On ne parle pas (utile avec le -n pour avoir que les commandes) + -m|--main Traite aussi le container commun (cloud commun / agora commun / wiki commun) + -M Ne traite que le container commun, et pas les orgas + --nas Ne traite QUE les orgas sur le NAS + --local Ne traite pas les orgas sur le NAS + -v|--version Donne la version des containers et signale les MàJ + -l|--list Liste des containers (up / down, local ou nas) de cette machine + +CONTAINERS_TYPES + -cloud Pour agir sur les clouds + -agora Pour agir sur les agoras + -wp Les wp + -wiki Les wiki + -office Les collabora + -paheko Le paheko + -castopod Les castopod + +COMMANDES (on peut en mettre plusieurs dans l'ordre souhaité) + -I|--install L'initialisation du container + -t Redémarre avec tempo (docker-compose down puis sleep ${TEMPO_ACTION_STOP} puis up puis sleep ${TEMPO_ACTION_START}) + -r Redémarre sans tempo (docker restart) + -exec \"command\" Envoie une commande docker exec + + --optim Lance la procédure Nextcloud pour optimiser les performances ** ** + -occ \"command\" Envoie une commande via occ ** ** + -u Mets à jour les applis ** SPECIFIQUES ** + -i Install des applis ** CLOUD ** + -a \"app1 app2 ...\" Choix des appli à installer ou mettre à jour (entre guillemets) ** ** + -U|--upgrade Upgrade des clouds ** ** + + -mmctl \"command\" Envoie une commande via mmctl ** SPECIFIQUES ** + -p|--post \"team\" \"message\" Poste un message dans une team agora ** AGORA ** + +ORGAS + [orga1 orga2 ... ] on peut filtrer parmi : ${AVAILABLE_ORGAS} + + +Exemples : +${PRG} -office -m -r restart de tous les collaboras (libére RAM) +${PRG} -cloud -u -r -q -n Affiche toutes les commandes (-n -q ) pour mettre à jour toutes les applis des clouds + restart (-u -r) +${PRG} -p \"monorga:town-square\" \"Hello\" monorga # envoie Hello sur le centreville de l'orga monorga sur son mattermost dédié + +" +} + + +#################################################### +################ fonctions clefs ################### +#################################################### + +_populate_lists(){ + # récupère les listes d'orga à traiter + # on rempli les tableaux OrgasOnNAS / OrgasLocales / NbOrgas ... par type de container + + if [ -z "${CONTAINERS_TYPES}" ]; then + # wow, on traite tout le monde d'un coup... + CONTAINERS_TYPES="$defaultContainersTypes" + fi + + for TYPE in ${CONTAINERS_TYPES}; do + if [ -n "${FilterLsVolume[$TYPE]}" ] ; then # on regarde dans les volumes + [ -n "$OPERATE_ON_NAS_ORGA" ] && OrgasOnNAS["$TYPE"]=$( _getListOrgas ${NAS_VOL} ${FilterLsVolume[$TYPE]} ) + [ -n "$OPERATE_LOCAL_ORGA" ] && OrgasLocales["$TYPE"]=$( _getListOrgas ${DOCK_VOL} ${FilterLsVolume[$TYPE]} "SANSLN") + else # un docker ps s'il n'y a pas de volumes + [ -n "$OPERATE_LOCAL_ORGA" ] && OrgasLocales["$TYPE"]=$(docker ps --format '{{.Names}}' | grep ${DockerServNames[$TYPE]} | sed -e "s/-*${DockerServNames[$TYPE]}//") + fi + NbOrgas["$TYPE"]=$(($(echo ${OrgasOnNAS["$TYPE"]} | wc -w) + $(echo ${OrgasLocales["$TYPE"]} | wc -w))) + RunningOrgas["$TYPE"]=$(docker ps --format '{{.Names}}' | grep ${DockerServNames[$TYPE]} | sed -e "s/-*${DockerServNames[$TYPE]}//") + done +} + +_getListOrgas(){ + # retrouve les orgas à partir des volume présents + # $1 where to lookup + # $2 filter + # $3 removeSymbolicLinks + [ ! -d $1 ] || [ -z "$2" ] && return 1 # si le repertoire n'existe pas on skip + LIST=$(ls "${1}" | grep -i orga | grep -i "$2" | sed -e "s/-${2}$//g" | sed -e 's/^orga_//') + [ -n "$3" ] && LIST=$(ls -F "${1}" | grep '/' | grep -i orga | grep -i "$2" | sed -e "s/-${2}\/$//g" | sed -e 's/^orga_//') + LIST=$(comm -12 <(printf '%s\n' ${LIST} | sort) <(printf '%s\n' ${AVAILABLE_ORGAS} | sort)) + echo "$LIST" +} + +_executeFunctionForAll(){ + # Parcours des container et lancement des commandes + # Les commandes ont en derniers paramètres le type et l'orga et une string parmi KAZ/ORGANAS/ORGALOCAL pour savoir sur quoi on opère + # $1 function + # $2 nom de la commande + # $3 quel types de containers + # $4 params : quels paramètres à passer à la commande (les clefs sont #ORGA# #DOCKERSERVNAME# #SURNAS# #ISMAIN# #TYPE# #COMPOSEDIR# ) + for TYPE in ${3}; do + if [ -n "$OPERATE_ON_MAIN" ]; then + if [[ -n "${composeDirs[$TYPE]}" && "${availableContainersCommuns[*]}" =~ "${composeDirs[$TYPE]}" ]]; then # pas de cloud / agora / wp / wiki sur cette instance + Dockername=${DockerServNames[$TYPE]} + PARAMS=$(echo $4 | sed -e "s/#ORGA#//g;s/#DOCKERSERVNAME#/$Dockername/g;s/#ISMAIN#/OUI/g;s/#SURNAS#/NON/g;s/#TYPE#/$TYPE/g;s%#COMPOSEDIR#%${KAZ_COMP_DIR}/${composeDirs[$TYPE]}%g" ) + echo "-------- $2 $TYPE COMMUN ----------------------------" >& $QUIET + eval "$1" $PARAMS + fi + fi + if [[ ${NbOrgas[$TYPE]} -gt 0 ]]; then + echo "-------- $2 des $TYPE des ORGAS ----------------------------" >& $QUIET + COMPTEUR=1 + if [ -n "$OPERATE_LOCAL_ORGA" ]; then + for ORGA in ${OrgasLocales[$TYPE]}; do + Dockername=${ORGA}-${DockerServNames[$TYPE]} + PARAMS=$(echo $4 | sed -e "s/#ORGA#/${ORGA}/g;s/#DOCKERSERVNAME#/$Dockername/g;s/#ISMAIN#/NON/g;s/#SURNAS#/NON/g;s/#TYPE#/$TYPE/g;s%#COMPOSEDIR#%${KAZ_COMP_DIR}/${ORGA}-orga%g" ) + echo "${RED} ${ORGA}-orga ${NC}($COMPTEUR/${NbOrgas[$TYPE]})" >& $QUIET + eval "$1" $PARAMS + COMPTEUR=$((COMPTEUR + 1)) + done + fi + if [ -n "$OPERATE_ON_NAS_ORGA" ]; then + for ORGA in ${OrgasOnNAS[$TYPE]}; do + Dockername=${ORGA}-${DockerServNames[$TYPE]} + PARAMS=$(echo $4 | sed -e "s/#ORGA#/${ORGA}/g;s/#DOCKERSERVNAME#/$Dockername/g;s/#ISMAIN#/NON/g;s/#SURNAS#/OUI/g;s/#TYPE#/$TYPE/g;s%#COMPOSEDIR#%${KAZ_COMP_DIR}/${ORGA}-orga%g" ) + echo "${RED} ${ORGA}-orga ${NC}($COMPTEUR/${NbOrgas[$TYPE]})" >& $QUIET + eval "$1" $PARAMS + COMPTEUR=$((COMPTEUR + 1)) + done + fi + fi + done +} + + +############################################## +################ COMMANDES ################### +############################################## +Init(){ + # Initialisation des containers + echo "${NC}--------------------------------------------------------" >& $QUIET + echo "Initialisation" >& $QUIET + echo "--------------------------------------------------------" >& $QUIET + + _executeFunctionForAll "_initContainer" "Initialisation" "${CONTAINERS_TYPES[@]}" "#TYPE# #ISMAIN# #SURNAS# #ORGA# " +} + +restart-compose() { + # Parcours les containers et redémarre avec tempo + echo "${NC}--------------------------------------------------------" >& $QUIET + echo "DOCKER-COMPOSE DOWN puis sleep ${TEMPO_ACTION_STOP}" >& $QUIET + echo "DOCKER-COMPOSE UP puis sleep ${TEMPO_ACTION_START}" >& $QUIET + echo "de ${CONTAINERS_TYPES} pour $NB_ORGAS_STR" >& $QUIET + echo "--------------------------------------------------------" >& $QUIET + + _executeFunctionForAll "_restartContainerAvecTempo" "Restart" "${CONTAINERS_TYPES[@]}" "#TYPE# #ISMAIN# #COMPOSEDIR#" + + ${SIMU} sleep ${TEMPO_ACTION_START} + _reloadProxy + echo "--------------------------------------------------------" >& $QUIET + echo "${GREEN}FIN${NC} " >& $QUIET + echo "--------------------------------------------------------" >& $QUIET +} + +restart() { + # Parcours les containers et redémarre + echo "${NC}--------------------------------------------------------" >& $QUIET + echo "DOCKER RESTART des ${CONTAINERS_TYPES} pour $NB_ORGAS_STR" >& $QUIET + echo "--------------------------------------------------------" >& $QUIET + + _executeFunctionForAll "_restartContainer" "Restart" "${CONTAINERS_TYPES[@]}" "#DOCKERSERVNAME#" + + _reloadProxy + echo "--------------------------------------------------------" >& $QUIET + echo "${GREEN}FIN${NC} " >& $QUIET + echo "--------------------------------------------------------" >& $QUIET +} + +version(){ + # Parcours les containers et affiche leurs versions + echo "${NC}--------------------------------------------------------" >& $QUIET + echo "VERSIONS" >& $QUIET + echo "--------------------------------------------------------" >& $QUIET + _executeFunctionForAll "_versionContainer" "Version" "${CONTAINERS_TYPES[@]}" "#TYPE# #ISMAIN# #ORGA#" +} + + +listContainers(){ + echo "${NC}--------------------------------------------------------" + echo "LISTES" + echo "------------------------------------------------------------" + for TYPE in ${CONTAINERS_TYPES}; do + echo "****************** $TYPE ****************" + _listContainer "$TYPE" + done +} + + + +######################## Fonctions génériques ####################### + +_initContainer(){ + # $1 type + # $2 COMMUN + # $3 ON NAS + # $4 orgas + if [ -n "${subScripts[$1]}" ] ; then + evalStr="${KAZ_BIN_DIR}/${subScripts[$1]} --install" + if [ "$3" = "OUI" ]; then evalStr="${evalStr} -nas" ; fi + if [ ! "$QUIET" = "1" ]; then evalStr="${evalStr} -q" ; fi + if [ -n "$SIMU" ]; then evalStr="${evalStr} -n" ; fi + if [ ! "$2" = "OUI" ]; then evalStr="${evalStr} $4" ; fi + eval $evalStr + fi +} + +_restartContainer(){ + # $1 Dockername + echo -n "${NC}Redemarrage ... " >& $QUIET + ${SIMU} + ${SIMU} docker restart $1 + echo "${GREEN}OK${NC}" >& $QUIET +} + +_restartContainerAvecTempo(){ + # $1 type + # $2 main container + # $2 composeDir + dir=$3 + if [ -z $dir ]; then return 1; fi # le compose n'existe pas ... par exemple wordpress commun + cd "$dir" + echo -n "${NC}Arrêt ... " >& $QUIET + ${SIMU} + if [ "$2" = "OUI" ]; then ${SIMU} docker-compose stop ; + else ${SIMU} docker-compose stop "${serviceNames[$1]}" + fi + ${SIMU} sleep ${TEMPO_ACTION_STOP} + echo "${GREEN}OK${NC}" >& $QUIET + echo -n "${NC}Démarrage ... " >& $QUIET + if [ "$2" = "OUI" ]; then ${SIMU} docker-compose up -d ; + else ${SIMU} docker-compose up -d "${serviceNames[$1]}" + fi + ${SIMU} sleep ${TEMPO_ACTION_START} + echo "${GREEN}OK${NC}" >& $QUIET +} + + +_reloadProxy() { + availableProxyComposes=($(getList "${KAZ_CONF_DIR}/container-proxy.list")) + + for item in "${availableProxyComposes[@]}"; do + ${SIMU} ${KAZ_COMP_DIR}/${item}/reload.sh + done +} + +_versionContainer() { + # Affiche la version d'un container donné + # $1 type + # $2 COMMUN + # $3 orgas + if [ -n "${subScripts[$1]}" ] ; then + evalStr="${KAZ_BIN_DIR}/${subScripts[$1]} --version" + if [ ! "$2" = "OUI" ]; then evalStr="${evalStr} $3" ; fi + eval $evalStr + fi +} + + +_listContainer(){ + # pour un type donné (cloud / agora / wiki / wp), fait une synthèse de qui est up et down / nas ou local + # $1 type + RUNNING_FROM_NAS=$(comm -12 <(printf '%s\n' ${OrgasOnNAS[$1]} | sort) <(printf '%s\n' ${RunningOrgas[$1]} | sort) | sed -e ':a;N;$!ba;s/\n/ /g') + RUNNING_LOCAL=$(comm -12 <(printf '%s\n' ${OrgasLocales[$1]} | sort) <(printf '%s\n' ${RunningOrgas[$1]} | sort) | sed -e ':a;N;$!ba;s/\n/ /g') + # tu l'a vu la belle commande pour faire une exclusion de liste + DOWN_ON_NAS=$(comm -23 <(printf '%s\n' ${OrgasOnNAS[$1]} | sort) <(printf '%s\n' ${RunningOrgas[$1]} | sort) | sed -e ':a;N;$!ba;s/\n/ /g') + DOWN_LOCAL=$(comm -23 <(printf '%s\n' ${OrgasLocales[$1]} | sort) <(printf '%s\n' ${RunningOrgas[$1]} | sort)| sed -e ':a;N;$!ba;s/\n/ /g') + NB_SUR_NAS=$(echo ${OrgasOnNAS[$1]} | wc -w) + NB_LOCAUX=$(echo ${OrgasLocales[$1]} | wc -w) + NB_RUNNING_SUR_NAS=$(echo $RUNNING_FROM_NAS | wc -w) + NB_RUNNING_LOCALLY=$(echo $RUNNING_LOCAL | wc -w) + MAIN_RUNNING="${RED}DOWN${NC}" + if docker ps | grep -q " ${DockerServNames[$1]}" + then + MAIN_RUNNING="${GREEN}UP${NC}" + fi + + [ -n "${composeDirs[${1}]}" ] && echo "${NC}Le ${1} commun est $MAIN_RUNNING" + if [[ ${NbOrgas[$1]} -gt 0 ]]; then + ENLOCALSTR= + if [[ ${NB_RUNNING_SUR_NAS[$1]} -gt 0 ]]; then ENLOCALSTR=" en local" ; fi + echo "Orgas : $NB_RUNNING_LOCALLY / $NB_LOCAUX running ${1}$ENLOCALSTR" + echo "${NC}UP : ${GREEN}${RUNNING_LOCAL}" + echo "${NC}DOWN : ${RED}$DOWN_LOCAL${NC}" + if [[ ${NB_RUNNING_SUR_NAS[$1]} -gt 0 ]]; then + echo "${NC}Orgas : $NB_RUNNING_SUR_NAS / $NB_SUR_NAS running depuis le NAS :" + echo "${NC}UP : ${GREEN}${RUNNING_FROM_NAS}" + echo "${NC}DOWN : ${RED}$DOWN_ON_NAS${NC}" + fi + fi +} + + +######################################################### +############# FONCTIONS SPECIFIQUES ##################### +######################################################### + +################################## +############### CLOUD ############ +################################## + +UpgradeClouds() { + echo "${NC}--------------------------------------------------------" >& $QUIET + echo "UPGRADE des cloud" >& $QUIET + echo "--------------------------------------------------------" >& $QUIET + RunOCCCommand "upgrade" +} + +OptimiseClouds() { + echo "${NC}--------------------------------------------------------" >& $QUIET + echo "Optimisation des cloud" >& $QUIET + echo "--------------------------------------------------------" >& $QUIET + RunOCCCommand "db:add-missing-indices" "db:convert-filecache-bigint --no-interaction" +} + +InstallApps(){ + echo "${NC}--------------------------------------------------------" >& $QUIET + echo "INSTALL DES APPLIS sur les clouds : ${LISTE_APPS}" >& $QUIET + echo "-------------------------------------------------------------" >& $QUIET + if [ -z "${LISTE_APPS}" ]; then + echo "Aucune appli n'est précisée, j'installe les applis par défaut : ${APPLIS_PAR_DEFAUT}" >& $QUIET + LISTE_APPS="${APPLIS_PAR_DEFAUT}" + fi + PARAMS="-a \"$LISTE_APPS\"" + if [ ! "$QUIET" = "1" ]; then PARAMS="${PARAMS} -q" ; fi + if [ -n "$SIMU" ]; then PARAMS="${PARAMS} -n" ; fi + _executeFunctionForAll "${KAZ_BIN_DIR}/${subScripts["cloud"]} -i $PARAMS" "Install des applis" "cloud" "#ORGA#" +} + +UpdateApplis() { + echo "${NC}--------------------------------------------------------" >& $QUIET + echo "UPDATE DES APPLIS des cloud : ${LISTE_APPS}" >& $QUIET + echo "--------------------------------------------------------" >& $QUIET + PARAMS="-a ${LISTE_APPS}" + if [ -z "${LISTE_APPS}" ]; then + echo "Aucune appli n'est précisée, je les met toutes à jour! " >& $QUIET + PARAMS= + fi + if [ ! "$QUIET" = "1" ]; then PARAMS="${PARAMS} -q" ; fi + if [ -n "$SIMU" ]; then PARAMS="${PARAMS} -n" ; fi + _executeFunctionForAll "${KAZ_BIN_DIR}/${subScripts["cloud"]} -u $PARAMS" "Maj des applis" "cloud" "#ORGA#" +} + + +################################## +############### AGORA ############ +################################## + + +PostMessages(){ + echo "${NC}--------------------------------------------------------" >& $QUIET + echo "Envoi de messages sur mattermost" >& $QUIET + echo "--------------------------------------------------------" >& $QUIET + + for TEAM in "${!Posts[@]}" + do + MSG=${Posts[$TEAM]/\"/\\\"} + PARAMS="-p \"$TEAM\" \"$MSG\"" + if [ ! "$QUIET" = "1" ]; then PARAMS="${PARAMS} -q" ; fi + if [ -n "$SIMU" ]; then PARAMS="${PARAMS} -n" ; fi + _executeFunctionForAll "${KAZ_BIN_DIR}/${subScripts["agora"]} $PARAMS" "Post vers $TEAM sur l'agora" "agora" "#ORGA#" + done +} + + +########## LANCEMENT COMMANDES OCC / MMCTL ############ + +RunCommands() { + echo "${NC}--------------------------------------------------------" >& $QUIET + echo "Envoi de commandes en direct" >& $QUIET + echo "--------------------------------------------------------" >& $QUIET + # $1 OCC / MMCTL / EXEC + # $ suivants : les commandes + for command in "${@:2}" + do + if [ $1 = "OCC" ]; then RunOCCCommand "$command" ; fi + if [ $1 = "MMCTL" ]; then RunMMCTLCommand "$command" ; fi + if [ $1 = "EXEC" ]; then RunEXECCommand "$command" ; fi + done +} + +_runSingleOccCommand(){ + # $1 Command + # $2 Dockername + ${SIMU} docker exec -u 33 $2 /var/www/html/occ $1 +} + +_runSingleMmctlCommand(){ + # $1 Command + # $2 Dockername + ${SIMU} docker exec $2 bin/mmctl $1 +} + +_runSingleExecCommand(){ + # $1 Command + # $2 Dockername + ${SIMU} docker exec $2 $1 +} + +RunOCCCommand() { + # $1 Command + _executeFunctionForAll "_runSingleOccCommand \"${1}\"" "OCC $1" "cloud" "#DOCKERSERVNAME#" +} + +RunMMCTLCommand() { + # $1 Command + _executeFunctionForAll "_runSingleMmctlCommand \"${1}\"" "MMCTL $1" "agora" "#DOCKERSERVNAME#" +} + +RunEXECCommand() { + # $1 Command + _executeFunctionForAll "_runSingleExecCommand \"${1}\"" "docker exec $1" "${CONTAINERS_TYPES[@]}" "#DOCKERSERVNAME#" +} + + + +########## Main ################# +for ARG in "$@"; do + if [ -n "${GETOCCCOMAND}" ]; then # après un -occ + OCCCOMANDS+=("${ARG}") + GETOCCCOMAND= + elif [ -n "${GETEXECCOMAND}" ]; then # après un -exec + EXECCOMANDS+=("${ARG}") + GETEXECCOMAND= + elif [ -n "${GETAPPS}" ]; then # après un -a + LISTE_APPS="${LISTE_APPS} ${ARG}" + GETAPPS="" + elif [ -n "${GETMMCTLCOMAND}" ]; then # après un -mmctl + MMCTLCOMANDS+=("${ARG}") + GETMMCTLCOMAND= + elif [ -n "${GETTEAM}" ]; then # après un --post + GETMESSAGE="now" + GETTEAM="" + TEAM="${ARG}" + elif [ -n "${GETMESSAGE}" ]; then # après un --post "team:channel" + if [[ $TEAM == "-*" && ${#TEAM} -le 5 ]]; then echo "J'envoie mon message à \"${TEAM}\" ?? Arf, ça me plait pas j'ai l'impression que tu t'es planté sur la commande."; usage ; exit 1 ; fi + if [[ $ARG == "-*" && ${#ARG} -le 5 ]]; then echo "J'envoie le message \"${ARG}\" ?? Arf, ça me plait pas j'ai l'impression que tu t'es planté sur la commande."; usage ; exit 1 ; fi + if [[ ! $TEAM =~ .*:.+ ]]; then echo "Il faut mettre un destinataire sous la forme team:channel. Recommence !"; usage ; exit 1 ; fi + Posts+=( ["${TEAM}"]="$ARG" ) + GETMESSAGE="" + TEAM="" + else + case "${ARG}" in + '-h' | '--help' ) + usage && exit ;; + '-n' | '--simu') + SIMU="echo" ;; + '-q' ) + QUIET="/dev/null" ;; + '-m' | '--main' ) + OPERATE_ON_MAIN="OUI-OUI" ;; + '-M' ) + AVAILABLE_ORGAS= && OPERATE_ON_MAIN="OUI-OUI" ;; #pas d'orgas + '--nas' | '-nas' ) + OPERATE_LOCAL_ORGA= ;; # pas les locales + '--local' | '-local' ) + OPERATE_ON_NAS_ORGA= ;; # pas celles sur NAS + '-cloud'|'--cloud') + CONTAINERS_TYPES="${CONTAINERS_TYPES} cloud" ;; + '-agora'|'--agora'|'-mm'|'--mm'|'-matter'*|'--matter'*) + CONTAINERS_TYPES="${CONTAINERS_TYPES} agora" ;; + '-wiki'|'--wiki') + CONTAINERS_TYPES="${CONTAINERS_TYPES} wiki" ;; + '-wp'|'--wp') + CONTAINERS_TYPES="${CONTAINERS_TYPES} wp" ;; + '-office'|'--office'|'-collab'*|'--collab'*) + CONTAINERS_TYPES="${CONTAINERS_TYPES} office" ;; + '-paheko'|'--paheko') + CONTAINERS_TYPES="${CONTAINERS_TYPES} paheko" ;; + '-pod'|'--pod'|'-castopod'|'--castopod') + CONTAINERS_TYPES="${CONTAINERS_TYPES} castopod" ;; + '-t' ) + COMMANDS="${COMMANDS} RESTART-COMPOSE" ;; + '-r' ) + COMMANDS="${COMMANDS} RESTART-DOCKER" ;; + '-l' | '--list' ) + COMMANDS="$(echo "${COMMANDS} LIST" | sed "s/\s/\n/g" | sort | uniq)" ;; + '-v' | '--version') + COMMANDS="$(echo "${COMMANDS} VERSION" | sed "s/\s/\n/g" | sort | uniq)" ;; + '-I' | '--install' ) + COMMANDS="$(echo "${COMMANDS} INIT" | sed "s/\s/\n/g" | sort | uniq)" ;; # le sed sort uniq, c'est pour pas l'avoir en double + '-U' | '--upgrade') + COMMANDS="$(echo "${COMMANDS} UPGRADE" | sed "s/\s/\n/g" | sort | uniq)" ;; + '--optim' ) + COMMANDS="$(echo "${COMMANDS} OPTIMISE-CLOUD" | sed "s/\s/\n/g" | sort | uniq)" ;; + '-u' ) + COMMANDS="$(echo "${COMMANDS} UPDATE-CLOUD-APP" | sed "s/\s/\n/g" | sort | uniq)" ;; + '-i' ) + COMMANDS="$(echo "${COMMANDS} INSTALL-CLOUD-APP" | sed "s/\s/\n/g" | sort | uniq)" ;; + '-a' ) + GETAPPS="now" ;; + '-occ' ) + COMMANDS="$(echo "${COMMANDS} RUN-CLOUD-OCC" | sed "s/\s/\n/g" | sort | uniq)" + GETOCCCOMAND="now" ;; + '-mmctl' ) + COMMANDS="$(echo "${COMMANDS} RUN-AGORA-MMCTL" | sed "s/\s/\n/g" | sort | uniq)" + GETMMCTLCOMAND="now" ;; + '-exec' ) + COMMANDS="$(echo "${COMMANDS} RUN-DOCKER-EXEC" | sed "s/\s/\n/g" | sort | uniq)" + GETEXECCOMAND="now" ;; + '-p' | '--post' ) + COMMANDS="$(echo "${COMMANDS} POST-AGORA" | sed "s/\s/\n/g" | sort | uniq)" + GETTEAM="now" ;; + '-*' ) # ignore + ;; + *) + GIVEN_ORGA="${GIVEN_ORGA} ${ARG%-orga}" + ;; + esac + fi +done + + +if [[ "${COMMANDS[*]}" =~ "RESTART-COMPOSE" && "${COMMANDS[*]}" =~ "RESTART-TYPE" ]]; then + echo "Je restarte via docker-compose ou via docker mais pas les deux !" + usage + exit 1 +fi +if [ -z "${COMMANDS}" ]; then + usage && exit +fi +if [ -n "${GIVEN_ORGA}" ]; then + # intersection des 2 listes : quelle commande de ouf !! + AVAILABLE_ORGAS=$(comm -12 <(printf '%s\n' ${AVAILABLE_ORGAS} | sort) <(printf '%s\n' ${GIVEN_ORGA} | sort)) +fi + +NB_ORGAS=$(echo "${AVAILABLE_ORGAS}" | wc -w ) + +if [[ $NB_ORGAS = 0 && -z "${OPERATE_ON_MAIN}" ]]; then + echo "Aucune orga trouvée." + exit 1 +fi + +NB_ORGAS_STR="$NB_ORGAS orgas" +[ -n "${OPERATE_ON_MAIN}" ] && NB_ORGAS_STR="$NB_ORGAS_STR + les communs" + +_populate_lists # on récupère les clouds / agora / wiki / wp correspondants aux orga + +if [[ $NB_ORGAS -gt 2 && "${COMMANDS[*]}" =~ 'INIT' ]]; then + ETLECLOUDCOMMUN= + [ -n "${OPERATE_ON_MAIN}" ] && ETLECLOUDCOMMUN=" ainsi que les containers commun" + echo "On s'apprête à initialiser les ${CONTAINERS_TYPES} suivants : ${AVAILABLE_ORGAS}${ETLECLOUDCOMMUN}" + checkContinue +fi + +for COMMAND in ${COMMANDS}; do + case "${COMMAND}" in + 'LIST' ) + listContainers && exit ;; + 'VERSION' ) + version && exit ;; + 'OPTIMISE-CLOUD' ) + OptimiseClouds ;; + 'RESTART-COMPOSE' ) + restart-compose ;; + 'RESTART-DOCKER' ) + restart ;; + 'UPDATE-CLOUD-APP' ) + UpdateApplis ;; + 'UPGRADE' ) + UpgradeClouds ;; + 'INIT' ) + Init ;; + 'INSTALL-CLOUD-APP' ) + InstallApps ;; + 'RUN-CLOUD-OCC' ) + RunCommands "OCC" "${OCCCOMANDS[@]}" ;; + 'RUN-AGORA-MMCTL' ) + RunCommands "MMCTL" "${MMCTLCOMANDS[@]}" ;; + 'RUN-DOCKER-EXEC' ) + RunCommands "EXEC" "${EXECCOMANDS[@]}" ;; + 'POST-AGORA' ) + PostMessages ${Posts} ;; + esac +done diff --git a/bin/gestContainers_v2.sh b/bin/gestContainers_v2.sh new file mode 100755 index 0000000..840ab8d --- /dev/null +++ b/bin/gestContainers_v2.sh @@ -0,0 +1,659 @@ +#!/bin/bash +# Script de manipulation des containers en masse +# init /versions / restart ... +# + +KAZ_ROOT=$(cd "$(dirname $0)"/..; pwd) +. $KAZ_ROOT/bin/.commonFunctions.sh +setKazVars +. $DOCKERS_ENV +. $KAZ_ROOT/secret/SetAllPass.sh + +PRG=$(basename $0) + +tab_sites_destinations_possibles=($(get_Serveurs_Kaz)) +# ${SIMU} ssh -p 2201 root@${SITE_DST}.${domain} "${CMD}" +# SITE_DST="${tab_sites_destinations_possibles[1]}" +# ${tab_sites_destinations_possibles[@]} + +#GLOBAL VARS +NAS_VOL="/mnt/disk-nas1/docker/volumes/" + +availableOrga=($(getList "${KAZ_CONF_DIR}/container-orga.list")) +AVAILABLE_ORGAS=${availableOrga[*]//-orga/} + +availableContainersCommuns=( $(getList "${KAZ_CONF_DIR}/container-withMail.list") $(getList "${KAZ_CONF_DIR}/container-withoutMail.list")) + +OPERATE_ON_MAIN= # par defaut NON on ne traite que des orgas +OPERATE_ON_NAS_ORGA="OUI" # par defaut oui, on va aussi sur les orgas du NAS +OPERATE_LOCAL_ORGA="OUI" # par defaut oui + +TEMPO_ACTION_STOP=2 # Lors de redémarrage avec tempo, on attend après le stop +TEMPO_ACTION_START=120 # Lors de redémarrage avec tempo, avant de reload le proxy + +CONTAINERS_TYPES= + +defaultContainersTypes="cloud agora wp wiki office paheko" # les containers gérés par ce script. + +declare -A DockerServNames # le nom des containers correspondant +DockerServNames=( [cloud]="${nextcloudServName}" [agora]="${mattermostServName}" [wiki]="${dokuwikiServName}" [wp]="${wordpressServName}" [office]="${officeServName}" [paheko]="${pahekoServName}" ) + +declare -A FilterLsVolume # Pour trouver quel volume appartient à quel container +FilterLsVolume=( [cloud]="cloudMain" [agora]="matterConfig" [wiki]="wikiConf" [wp]="wordpress" ) + +declare -A composeDirs # Le nom du repertoire compose pour le commun +composeDirs=( [cloud]="cloud" [agora]="mattermost" [wiki]="dokuwiki" [office]="collabora" [paheko]="paheko" ) + +declare -A serviceNames # Le nom du du service dans le dockerfile d'orga +serviceNames=( [cloud]="cloud" [agora]="agora" [wiki]="dokuwiki" [wp]="wordpress" [office]="collabora") + +declare -A subScripts +subScripts=( [cloud]="manageCloud.sh" [agora]="manageAgora.sh" [wiki]="manageWiki.sh" [wp]="manageWp.sh" ) + +declare -A OrgasOnNAS +declare -A OrgasLocales +declare -A NbOrgas +declare -A RunningOrgas +declare -A Posts + +QUIET="1" # redirection des echo + +OCCCOMANDS=() +MMCTLCOMANDS=() +EXECCOMANDS=() + +# CLOUD +APPLIS_PAR_DEFAUT="tasks calendar contacts bookmarks richdocuments external drawio snappymail" + +usage() { +echo "${PRG} [OPTION] [CONTAINERS_TYPES] [COMMANDES] [SERVEURS] [ORGAS] +Ce script regroupe l'ensemble des opérations que l'on souhaite automatiser sur plusieurs containers, sur un ou plusieurs sites. +Par defaut, sur les orgas, mais on peut aussi ajouter les communs + +OPTIONS + -h|--help Cette aide :-) + -n|--simu SIMULATION + -q|--quiet On ne parle pas (utile avec le -n pour avoir que les commandes) + -m|--main Traite aussi le container commun (cloud commun / agora commun / wiki commun) + -M Ne traite que le container commun, et pas les orgas + --nas Ne traite QUE les orgas sur le NAS + --local Ne traite pas les orgas sur le NAS + -v|--version Donne la version des containers et signale les MàJ + -l|--list Liste des containers (up / down, local ou nas) de cette machine + +CONTAINERS_TYPES + -cloud Pour agir sur les clouds + -agora Pour agir sur les agoras + -wp Les wp + -wiki Les wiki + -office Les collabora + +COMMANDES (on peut en mettre plusieurs dans l'ordre souhaité) + -I|--install L'initialisation du container + -t Redémarre avec tempo (docker-compose down puis sleep ${TEMPO_ACTION_STOP} puis up puis sleep ${TEMPO_ACTION_START}) + -r Redémarre sans tempo (docker restart) + -exec \"command\" Envoie une commande docker exec + + --optim Lance la procédure Nextcloud pour optimiser les performances ** ** + -occ \"command\" Envoie une commande via occ ** ** + -u Mets à jour les applis ** SPECIFIQUES ** + -i Install des applis ** CLOUD ** + -a \"app1 app2 ...\" Choix des appli à installer ou mettre à jour (entre guillemets) ** ** + -U|--upgrade Upgrade des clouds ** ** + + -mmctl \"command\" Envoie une commande via mmctl ** SPECIFIQUES ** + -p|--post \"team\" \"message\" Poste un message dans une team agora ** AGORA ** + +SERVEURS + --all-srv Lance sur tous les serveurs ${tab_sites_destinations_possibles[@]}, sinon c'est uniquement sur ${site} + +ORGAS sur ${site} + [orga1 orga2 ... ] on peut filtrer parmi : ${AVAILABLE_ORGAS} + + +Exemples : +${PRG} -office -m -r # restart de tous les collaboras (libére RAM) +${PRG} -cloud -u -r -q -n # affiche toutes les commandes (-n -q ) pour mettre à jour toutes les applis des clouds + restart (-u -r) +${PRG} -p \"monorga:town-square\" \"Hello\" monorga # envoie Hello sur le centreville de l'orga monorga sur son mattermost dédié +${PRG} -cloud -occ \"config:system:set default_phone_region --value='FR'\" --all-srv # modifie la variable default_phone_region dans le config.php de tous les clouds de tous les serveurs +" +} + + +#################################################### +################ fonctions clefs ################### +#################################################### + +_populate_lists(){ + # récupère les listes d'orga à traiter + # on rempli les tableaux OrgasOnNAS / OrgasLocales / NbOrgas ... par type de container + + if [ -z "${CONTAINERS_TYPES}" ]; then + # wow, on traite tout le monde d'un coup... + CONTAINERS_TYPES="$defaultContainersTypes" + fi + + for TYPE in ${CONTAINERS_TYPES}; do + if [ -n "${FilterLsVolume[$TYPE]}" ] ; then # on regarde dans les volumes + [ -n "$OPERATE_ON_NAS_ORGA" ] && OrgasOnNAS["$TYPE"]=$( _getListOrgas ${NAS_VOL} ${FilterLsVolume[$TYPE]} ) + [ -n "$OPERATE_LOCAL_ORGA" ] && OrgasLocales["$TYPE"]=$( _getListOrgas ${DOCK_VOL} ${FilterLsVolume[$TYPE]} "SANSLN") + else # un docker ps s'il n'y a pas de volumes + [ -n "$OPERATE_LOCAL_ORGA" ] && OrgasLocales["$TYPE"]=$(docker ps --format '{{.Names}}' | grep ${DockerServNames[$TYPE]} | sed -e "s/-*${DockerServNames[$TYPE]}//") + fi + NbOrgas["$TYPE"]=$(($(echo ${OrgasOnNAS["$TYPE"]} | wc -w) + $(echo ${OrgasLocales["$TYPE"]} | wc -w))) + RunningOrgas["$TYPE"]=$(docker ps --format '{{.Names}}' | grep ${DockerServNames[$TYPE]} | sed -e "s/-*${DockerServNames[$TYPE]}//") + done +} + +_getListOrgas(){ + # retrouve les orgas à partir des volume présents + # $1 where to lookup + # $2 filter + # $3 removeSymbolicLinks + [ ! -d $1 ] || [ -z "$2" ] && return 1 # si le repertoire n'existe pas on skip + LIST=$(ls "${1}" | grep -i orga | grep -i "$2" | sed -e "s/-${2}$//g" | sed -e 's/^orga_//') + [ -n "$3" ] && LIST=$(ls -F "${1}" | grep '/' | grep -i orga | grep -i "$2" | sed -e "s/-${2}\/$//g" | sed -e 's/^orga_//') + LIST=$(comm -12 <(printf '%s\n' ${LIST} | sort) <(printf '%s\n' ${AVAILABLE_ORGAS} | sort)) + echo "$LIST" +} + +_executeFunctionForAll(){ + # Parcours des container et lancement des commandes + # Les commandes ont en derniers paramètres le type et l'orga et une string parmi KAZ/ORGANAS/ORGALOCAL pour savoir sur quoi on opère + # $1 function + # $2 nom de la commande + # $3 quel types de containers + # $4 params : quels paramètres à passer à la commande (les clefs sont #ORGA# #DOCKERSERVNAME# #SURNAS# #ISMAIN# #TYPE# #COMPOSEDIR# ) + for TYPE in ${3}; do + if [ -n "$OPERATE_ON_MAIN" ]; then + if [[ -n "${composeDirs[$TYPE]}" && "${availableContainersCommuns[*]}" =~ "${composeDirs[$TYPE]}" ]]; then # pas de cloud / agora / wp / wiki sur cette instance + Dockername=${DockerServNames[$TYPE]} + PARAMS=$(echo $4 | sed -e "s/#ORGA#//g;s/#DOCKERSERVNAME#/$Dockername/g;s/#ISMAIN#/OUI/g;s/#SURNAS#/NON/g;s/#TYPE#/$TYPE/g;s%#COMPOSEDIR#%${KAZ_COMP_DIR}/${composeDirs[$TYPE]}%g" ) + echo "-------- $2 $TYPE COMMUN ----------------------------" >& $QUIET + eval "$1" $PARAMS + fi + fi + if [[ ${NbOrgas[$TYPE]} -gt 0 ]]; then + echo "-------- $2 des $TYPE des ORGAS ----------------------------" >& $QUIET + COMPTEUR=1 + if [ -n "$OPERATE_LOCAL_ORGA" ]; then + for ORGA in ${OrgasLocales[$TYPE]}; do + Dockername=${ORGA}-${DockerServNames[$TYPE]} + PARAMS=$(echo $4 | sed -e "s/#ORGA#/${ORGA}/g;s/#DOCKERSERVNAME#/$Dockername/g;s/#ISMAIN#/NON/g;s/#SURNAS#/NON/g;s/#TYPE#/$TYPE/g;s%#COMPOSEDIR#%${KAZ_COMP_DIR}/${ORGA}-orga%g" ) + echo "${RED} ${ORGA}-orga ${NC}($COMPTEUR/${NbOrgas[$TYPE]})" >& $QUIET + eval "$1" $PARAMS + COMPTEUR=$((COMPTEUR + 1)) + done + fi + if [ -n "$OPERATE_ON_NAS_ORGA" ]; then + for ORGA in ${OrgasOnNAS[$TYPE]}; do + Dockername=${ORGA}-${DockerServNames[$TYPE]} + PARAMS=$(echo $4 | sed -e "s/#ORGA#/${ORGA}/g;s/#DOCKERSERVNAME#/$Dockername/g;s/#ISMAIN#/NON/g;s/#SURNAS#/OUI/g;s/#TYPE#/$TYPE/g;s%#COMPOSEDIR#%${KAZ_COMP_DIR}/${ORGA}-orga%g" ) + echo "${RED} ${ORGA}-orga ${NC}($COMPTEUR/${NbOrgas[$TYPE]})" >& $QUIET + eval "$1" $PARAMS + COMPTEUR=$((COMPTEUR + 1)) + done + fi + fi + done +} + + +############################################## +################ COMMANDES ################### +############################################## +Init(){ + # Initialisation des containers + echo "${NC}--------------------------------------------------------" >& $QUIET + echo "Initialisation" >& $QUIET + echo "--------------------------------------------------------" >& $QUIET + + _executeFunctionForAll "_initContainer" "Initialisation" "${CONTAINERS_TYPES[@]}" "#TYPE# #ISMAIN# #SURNAS# #ORGA# " +} + +restart-compose() { + # Parcours les containers et redémarre avec tempo + echo "${NC}--------------------------------------------------------" >& $QUIET + echo "DOCKER-COMPOSE DOWN puis sleep ${TEMPO_ACTION_STOP}" >& $QUIET + echo "DOCKER-COMPOSE UP puis sleep ${TEMPO_ACTION_START}" >& $QUIET + echo "de ${CONTAINERS_TYPES} pour $NB_ORGAS_STR" >& $QUIET + echo "--------------------------------------------------------" >& $QUIET + + _executeFunctionForAll "_restartContainerAvecTempo" "Restart" "${CONTAINERS_TYPES[@]}" "#TYPE# #ISMAIN# #COMPOSEDIR#" + + ${SIMU} sleep ${TEMPO_ACTION_START} + _reloadProxy + echo "--------------------------------------------------------" >& $QUIET + echo "${GREEN}FIN${NC} " >& $QUIET + echo "--------------------------------------------------------" >& $QUIET +} + +restart() { + # Parcours les containers et redémarre + echo "${NC}--------------------------------------------------------" >& $QUIET + echo "DOCKER RESTART des ${CONTAINERS_TYPES} pour $NB_ORGAS_STR" >& $QUIET + echo "--------------------------------------------------------" >& $QUIET + + _executeFunctionForAll "_restartContainer" "Restart" "${CONTAINERS_TYPES[@]}" "#DOCKERSERVNAME#" + + _reloadProxy + echo "--------------------------------------------------------" >& $QUIET + echo "${GREEN}FIN${NC} " >& $QUIET + echo "--------------------------------------------------------" >& $QUIET +} + +version(){ + # Parcours les containers et affiche leurs versions + echo "${NC}--------------------------------------------------------" >& $QUIET + echo "VERSIONS" >& $QUIET + echo "--------------------------------------------------------" >& $QUIET + _executeFunctionForAll "_versionContainer" "Version" "${CONTAINERS_TYPES[@]}" "#TYPE# #ISMAIN# #ORGA#" +} + + +listContainers(){ + echo "${NC}--------------------------------------------------------" + echo "LISTES" + echo "------------------------------------------------------------" + for TYPE in ${CONTAINERS_TYPES}; do + echo "****************** $TYPE ****************" + _listContainer "$TYPE" + done +} + + + +######################## Fonctions génériques ####################### + +_initContainer(){ + # $1 type + # $2 COMMUN + # $3 ON NAS + # $4 orgas + if [ -n "${subScripts[$1]}" ] ; then + evalStr="${KAZ_BIN_DIR}/${subScripts[$1]} --install" + if [ "$3" = "OUI" ]; then evalStr="${evalStr} -nas" ; fi + if [ ! "$QUIET" = "1" ]; then evalStr="${evalStr} -q" ; fi + if [ -n "$SIMU" ]; then evalStr="${evalStr} -n" ; fi + if [ ! "$2" = "OUI" ]; then evalStr="${evalStr} $4" ; fi + eval $evalStr + fi +} + +_restartContainer(){ + # $1 Dockername + echo -n "${NC}Redemarrage ... " >& $QUIET + ${SIMU} + ${SIMU} docker restart $1 + echo "${GREEN}OK${NC}" >& $QUIET +} + +_restartContainerAvecTempo(){ + # $1 type + # $2 main container + # $2 composeDir + dir=$3 + if [ -z $dir ]; then return 1; fi # le compose n'existe pas ... par exemple wordpress commun + cd "$dir" + echo -n "${NC}Arrêt ... " >& $QUIET + ${SIMU} + if [ "$2" = "OUI" ]; then ${SIMU} docker-compose stop ; + else ${SIMU} docker-compose stop "${serviceNames[$1]}" + fi + ${SIMU} sleep ${TEMPO_ACTION_STOP} + echo "${GREEN}OK${NC}" >& $QUIET + echo -n "${NC}Démarrage ... " >& $QUIET + if [ "$2" = "OUI" ]; then ${SIMU} docker-compose up -d ; + else ${SIMU} docker-compose up -d "${serviceNames[$1]}" + fi + ${SIMU} sleep ${TEMPO_ACTION_START} + echo "${GREEN}OK${NC}" >& $QUIET +} + + +_reloadProxy() { + availableProxyComposes=($(getList "${KAZ_CONF_DIR}/container-proxy.list")) + + for item in "${availableProxyComposes[@]}"; do + ${SIMU} ${KAZ_COMP_DIR}/${item}/reload.sh + done +} + +_versionContainer() { + # Affiche la version d'un container donné + # $1 type + # $2 COMMUN + # $3 orgas + if [ -n "${subScripts[$1]}" ] ; then + evalStr="${KAZ_BIN_DIR}/${subScripts[$1]} --version" + if [ ! "$2" = "OUI" ]; then evalStr="${evalStr} $3" ; fi + eval $evalStr + fi +} + + +_listContainer(){ + # pour un type donné (cloud / agora / wiki / wp), fait une synthèse de qui est up et down / nas ou local + # $1 type + RUNNING_FROM_NAS=$(comm -12 <(printf '%s\n' ${OrgasOnNAS[$1]} | sort) <(printf '%s\n' ${RunningOrgas[$1]} | sort) | sed -e ':a;N;$!ba;s/\n/ /g') + RUNNING_LOCAL=$(comm -12 <(printf '%s\n' ${OrgasLocales[$1]} | sort) <(printf '%s\n' ${RunningOrgas[$1]} | sort) | sed -e ':a;N;$!ba;s/\n/ /g') + # tu l'a vu la belle commande pour faire une exclusion de liste + DOWN_ON_NAS=$(comm -23 <(printf '%s\n' ${OrgasOnNAS[$1]} | sort) <(printf '%s\n' ${RunningOrgas[$1]} | sort) | sed -e ':a;N;$!ba;s/\n/ /g') + DOWN_LOCAL=$(comm -23 <(printf '%s\n' ${OrgasLocales[$1]} | sort) <(printf '%s\n' ${RunningOrgas[$1]} | sort)| sed -e ':a;N;$!ba;s/\n/ /g') + NB_SUR_NAS=$(echo ${OrgasOnNAS[$1]} | wc -w) + NB_LOCAUX=$(echo ${OrgasLocales[$1]} | wc -w) + NB_RUNNING_SUR_NAS=$(echo $RUNNING_FROM_NAS | wc -w) + NB_RUNNING_LOCALLY=$(echo $RUNNING_LOCAL | wc -w) + MAIN_RUNNING="${RED}DOWN${NC}" + if docker ps | grep -q " ${DockerServNames[$1]}" + then + MAIN_RUNNING="${GREEN}UP${NC}" + fi + + [ -n "${composeDirs[${1}]}" ] && echo "${NC}Le ${1} commun est $MAIN_RUNNING" + if [[ ${NbOrgas[$1]} -gt 0 ]]; then + ENLOCALSTR= + if [[ ${NB_RUNNING_SUR_NAS[$1]} -gt 0 ]]; then ENLOCALSTR=" en local" ; fi + echo "Orgas : $NB_RUNNING_LOCALLY / $NB_LOCAUX running ${1}$ENLOCALSTR" + echo "${NC}UP : ${GREEN}${RUNNING_LOCAL}" + echo "${NC}DOWN : ${RED}$DOWN_LOCAL${NC}" + if [[ ${NB_RUNNING_SUR_NAS[$1]} -gt 0 ]]; then + echo "${NC}Orgas : $NB_RUNNING_SUR_NAS / $NB_SUR_NAS running depuis le NAS :" + echo "${NC}UP : ${GREEN}${RUNNING_FROM_NAS}" + echo "${NC}DOWN : ${RED}$DOWN_ON_NAS${NC}" + fi + fi +} + + +######################################################### +############# FONCTIONS SPECIFIQUES ##################### +######################################################### + +################################## +############### CLOUD ############ +################################## + +UpgradeClouds() { + echo "${NC}--------------------------------------------------------" >& $QUIET + echo "UPGRADE des cloud" >& $QUIET + echo "--------------------------------------------------------" >& $QUIET + RunOCCCommand "upgrade" +} + +OptimiseClouds() { + echo "${NC}--------------------------------------------------------" >& $QUIET + echo "Optimisation des cloud" >& $QUIET + echo "--------------------------------------------------------" >& $QUIET + RunOCCCommands "db:add-missing-indices" "db:convert-filecache-bigint --no-interaction" +} + +InstallApps(){ + echo "${NC}--------------------------------------------------------" >& $QUIET + echo "INSTALL DES APPLIS sur les clouds : ${LISTE_APPS}" >& $QUIET + echo "-------------------------------------------------------------" >& $QUIET + if [ -z "${LISTE_APPS}" ]; then + echo "Aucune appli n'est précisée, j'installe les applis par défaut : ${APPLIS_PAR_DEFAUT}" >& $QUIET + LISTE_APPS="${APPLIS_PAR_DEFAUT}" + fi + PARAMS="-a \"$LISTE_APPS\"" + if [ ! "$QUIET" = "1" ]; then PARAMS="${PARAMS} -q" ; fi + if [ -n "$SIMU" ]; then PARAMS="${PARAMS} -n" ; fi + _executeFunctionForAll "${KAZ_BIN_DIR}/${subScripts["cloud"]} -i $PARAMS" "Install des applis" "cloud" "#ORGA#" +} + +UpdateApplis() { + echo "${NC}--------------------------------------------------------" >& $QUIET + echo "UPDATE DES APPLIS des cloud : ${LISTE_APPS}" >& $QUIET + echo "--------------------------------------------------------" >& $QUIET + PARAMS="-a ${LISTE_APPS}" + if [ -z "${LISTE_APPS}" ]; then + echo "Aucune appli n'est précisée, je les met toutes à jour! " >& $QUIET + PARAMS= + fi + if [ ! "$QUIET" = "1" ]; then PARAMS="${PARAMS} -q" ; fi + if [ -n "$SIMU" ]; then PARAMS="${PARAMS} -n" ; fi + _executeFunctionForAll "${KAZ_BIN_DIR}/${subScripts["cloud"]} -u $PARAMS" "Maj des applis" "cloud" "#ORGA#" +} + + +################################## +############### AGORA ############ +################################## + + +PostMessages(){ + echo "${NC}--------------------------------------------------------" >& $QUIET + echo "Envoi de messages sur mattermost" >& $QUIET + echo "--------------------------------------------------------" >& $QUIET + + for TEAM in "${!Posts[@]}" + do + MSG=${Posts[$TEAM]/\"/\\\"} + PARAMS="-p \"$TEAM\" \"$MSG\"" + if [ ! "$QUIET" = "1" ]; then PARAMS="${PARAMS} -q" ; fi + if [ -n "$SIMU" ]; then PARAMS="${PARAMS} -n" ; fi + _executeFunctionForAll "${KAZ_BIN_DIR}/${subScripts["agora"]} $PARAMS" "Post vers $TEAM sur l'agora" "agora" "#ORGA#" + done +} + + +########## LANCEMENT COMMANDES OCC / MMCTL ############ + +RunCommands() { + echo "${NC}--------------------------------------------------------" >& $QUIET + echo "Envoi de commandes en direct" >& $QUIET + echo "--------------------------------------------------------" >& $QUIET + # $1 OCC / MMCTL / EXEC + # $ suivants : les commandes + for command in "${@:2}" + do + if [ $1 = "OCC" ]; then RunOCCCommand "$command" ; fi + if [ $1 = "MMCTL" ]; then RunMMCTLCommand "$command" ; fi + if [ $1 = "EXEC" ]; then RunEXECCommand "$command" ; fi + done +} + +_runSingleOccCommand(){ + # $1 Command + # $2 Dockername + ${SIMU} docker exec -u 33 $2 /var/www/html/occ $1 +} + +_runSingleMmctlCommand(){ + # $1 Command + # $2 Dockername + ${SIMU} docker exec $2 bin/mmctl $1 +} + +_runSingleExecCommand(){ + # $1 Command + # $2 Dockername + ${SIMU} ssh -p 2201 root@${SITE_DST}.${domain} docker exec $2 $1 +} + +RunOCCCommand() { + # $1 Command + _executeFunctionForAll "_runSingleOccCommand \"${1}\"" "OCC $1" "cloud" "#DOCKERSERVNAME#" +} + +RunMMCTLCommand() { + # $1 Command + _executeFunctionForAll "_runSingleMmctlCommand \"${1}\"" "MMCTL $1" "agora" "#DOCKERSERVNAME#" +} + +RunEXECCommand() { + # $1 Command + _executeFunctionForAll "_runSingleExecCommand \"${1}\"" "docker exec $1" "${CONTAINERS_TYPES[@]}" "#DOCKERSERVNAME#" +} + + + +########## Contrôle ################# +for ARG in "$@"; do + # Seul PROD1 peut attaquer tous les autres serveurs kaz sinon un serveur kaz peut juste s'attaquer lui-même (aie!) + if [ "${ARG}" == "--all-srv" -a "${site}" != "prod1" ]; then + echo "${RED}--all-srv choisi alors qu'on n'est pas sur prod1 : impossible, on quitte${NC}" +# mais pour l'instant on autorise pour les tests +# exit + fi +done + +########## Main ################# +for ARG in "$@"; do + +#echo "${ARG}" + + if [ -n "${GETOCCCOMAND}" ]; then # après un -occ + OCCCOMANDS+=("${ARG}") + GETOCCCOMAND= + elif [ -n "${GETEXECCOMAND}" ]; then # après un -exec + EXECCOMANDS+=("${ARG}") + GETEXECCOMAND= + elif [ -n "${GETAPPS}" ]; then # après un -a + LISTE_APPS="${LISTE_APPS} ${ARG}" + GETAPPS="" + elif [ -n "${GETMMCTLCOMAND}" ]; then # après un -mmctl + MMCTLCOMANDS+=("${ARG}") + GETMMCTLCOMAND= + elif [ -n "${GETTEAM}" ]; then # après un --post + GETMESSAGE="now" + GETTEAM="" + TEAM="${ARG}" + elif [ -n "${GETMESSAGE}" ]; then # après un --post "team:channel" + if [[ $TEAM == "-*" && ${#TEAM} -le 5 ]]; then echo "J'envoie mon message à \"${TEAM}\" ?? Arf, ça me plait pas j'ai l'impression que tu t'es planté sur la commande."; usage ; exit 1 ; fi + if [[ $ARG == "-*" && ${#ARG} -le 5 ]]; then echo "J'envoie le message \"${ARG}\" ?? Arf, ça me plait pas j'ai l'impression que tu t'es planté sur la commande."; usage ; exit 1 ; fi + if [[ ! $TEAM =~ .*:.+ ]]; then echo "Il faut mettre un destinataire sous la forme team:channel. Recommence !"; usage ; exit 1 ; fi + Posts+=( ["${TEAM}"]="$ARG" ) + GETMESSAGE="" + TEAM="" + else + case "${ARG}" in + '-h' | '--help' ) + usage && exit ;; + '-n' | '--simu') + SIMU="echo" ;; + '-q' ) + QUIET="/dev/null" ;; + '-m' | '--main' ) + OPERATE_ON_MAIN="OUI-OUI" ;; + '-M' ) + AVAILABLE_ORGAS= && OPERATE_ON_MAIN="OUI-OUI" ;; #pas d'orgas + '--nas' | '-nas' ) + OPERATE_LOCAL_ORGA= ;; # pas les locales + '--local' | '-local' ) + OPERATE_ON_NAS_ORGA= ;; # pas celles sur NAS + '-cloud'|'--cloud') + CONTAINERS_TYPES="${CONTAINERS_TYPES} cloud" ;; + '-agora'|'--agora') + CONTAINERS_TYPES="${CONTAINERS_TYPES} agora" ;; + '-wiki'|'--wiki') + CONTAINERS_TYPES="${CONTAINERS_TYPES} wiki" ;; + '-wp'|'--wp') + CONTAINERS_TYPES="${CONTAINERS_TYPES} wp" ;; + '-office'|'--office') + CONTAINERS_TYPES="${CONTAINERS_TYPES} office" ;; + '-t' ) + COMMANDS="${COMMANDS} RESTART-COMPOSE" ;; + '-r' ) + COMMANDS="${COMMANDS} RESTART-DOCKER" ;; + '-l' | '--list' ) + COMMANDS="$(echo "${COMMANDS} LIST" | sed "s/\s/\n/g" | sort | uniq)" ;; + '-v' | '--version') + COMMANDS="$(echo "${COMMANDS} VERSION" | sed "s/\s/\n/g" | sort | uniq)" ;; + '-I' | '--install' ) + COMMANDS="$(echo "${COMMANDS} INIT" | sed "s/\s/\n/g" | sort | uniq)" ;; # le sed sort uniq, c'est pour pas l'avoir en double + '-U' | '--upgrade') + COMMANDS="$(echo "${COMMANDS} UPGRADE" | sed "s/\s/\n/g" | sort | uniq)" ;; + '--optim' ) + COMMANDS="$(echo "${COMMANDS} OPTIMISE-CLOUD" | sed "s/\s/\n/g" | sort | uniq)" ;; + '-u' ) + COMMANDS="$(echo "${COMMANDS} UPDATE-CLOUD-APP" | sed "s/\s/\n/g" | sort | uniq)" ;; + '-i' ) + COMMANDS="$(echo "${COMMANDS} INSTALL-CLOUD-APP" | sed "s/\s/\n/g" | sort | uniq)" ;; + '-a' ) + GETAPPS="now" ;; + '-occ' ) + COMMANDS="$(echo "${COMMANDS} RUN-CLOUD-OCC" | sed "s/\s/\n/g" | sort | uniq)" + GETOCCCOMAND="now" ;; + '-mmctl' ) + COMMANDS="$(echo "${COMMANDS} RUN-AGORA-MMCTL" | sed "s/\s/\n/g" | sort | uniq)" + GETMMCTLCOMAND="now" ;; + '-exec' ) + COMMANDS="$(echo "${COMMANDS} RUN-DOCKER-EXEC" | sed "s/\s/\n/g" | sort | uniq)" + GETEXECCOMAND="now" ;; + '-p' | '--post' ) + COMMANDS="$(echo "${COMMANDS} POST-AGORA" | sed "s/\s/\n/g" | sort | uniq)" + GETTEAM="now" ;; + '-*' ) # ignore + ;; + *) + GIVEN_ORGA="${GIVEN_ORGA} ${ARG%-orga}" + ;; + esac + fi +done + + +if [[ "${COMMANDS[*]}" =~ "RESTART-COMPOSE" && "${COMMANDS[*]}" =~ "RESTART-TYPE" ]]; then + echo "Je restarte via docker-compose ou via docker mais pas les deux !" + usage + exit 1 +fi +if [ -z "${COMMANDS}" ]; then + usage && exit +fi +if [ -n "${GIVEN_ORGA}" ]; then + # intersection des 2 listes : quelle commande de ouf !! + AVAILABLE_ORGAS=$(comm -12 <(printf '%s\n' ${AVAILABLE_ORGAS} | sort) <(printf '%s\n' ${GIVEN_ORGA} | sort)) +fi + +NB_ORGAS=$(echo "${AVAILABLE_ORGAS}" | wc -w ) + +if [[ $NB_ORGAS = 0 && -z "${OPERATE_ON_MAIN}" ]]; then + echo "Aucune orga trouvée." + exit 1 +fi + +NB_ORGAS_STR="$NB_ORGAS orgas" +[ -n "${OPERATE_ON_MAIN}" ] && NB_ORGAS_STR="$NB_ORGAS_STR + les communs" + +_populate_lists # on récupère les clouds / agora / wiki / wp correspondants aux orga + +if [[ $NB_ORGAS -gt 2 && "${COMMANDS[*]}" =~ 'INIT' ]]; then + ETLECLOUDCOMMUN= + [ -n "${OPERATE_ON_MAIN}" ] && ETLECLOUDCOMMUN=" ainsi que les containers commun" + echo "On s'apprête à initialiser les ${CONTAINERS_TYPES} suivants : ${AVAILABLE_ORGAS}${ETLECLOUDCOMMUN}" + checkContinue +fi + +for COMMAND in ${COMMANDS}; do + case "${COMMAND}" in + 'LIST' ) + listContainers && exit ;; + 'VERSION' ) + version && exit ;; + 'OPTIMISE-CLOUD' ) + OptimiseClouds ;; + 'RESTART-COMPOSE' ) + restart-compose ;; + 'RESTART-DOCKER' ) + restart ;; + 'UPDATE-CLOUD-APP' ) + UpdateApplis ;; + 'UPGRADE' ) + UpgradeClouds ;; + 'INIT' ) + Init ;; + 'INSTALL-CLOUD-APP' ) + InstallApps ;; + 'RUN-CLOUD-OCC' ) + RunCommands "OCC" "${OCCCOMANDS[@]}" ;; + 'RUN-AGORA-MMCTL' ) + RunCommands "MMCTL" "${MMCTLCOMANDS[@]}" ;; + 'RUN-DOCKER-EXEC' ) + RunCommands "EXEC" "${EXECCOMANDS[@]}" ;; + 'POST-AGORA' ) + PostMessages ${Posts} ;; + esac +done diff --git a/bin/gestUsers.sh b/bin/gestUsers.sh new file mode 100755 index 0000000..1fb9d27 --- /dev/null +++ b/bin/gestUsers.sh @@ -0,0 +1,1151 @@ +#!/bin/bash +# gestion des utilisateurs de kaz ( mail, cloud général, mattermost ) +#KAZ_ROOT=$(cd "$(dirname $0)"/..; pwd) +KAZ_ROOT=/kaz +. $KAZ_ROOT/bin/.commonFunctions.sh +setKazVars + +. $DOCKERS_ENV +. $KAZ_ROOT/secret/SetAllPass.sh + +VERSION="13-12-2023" +PRG=$(basename $0) +RACINE=$(echo $PRG | awk '{print $1}') +IFS=' ' +BLINK='\033[0;5m' + +LOG=$RACINE".log" +URL_NC=$(echo $cloudHost).$(echo $domain) +URL_AGORA=$(echo $matterHost).$(echo $domain) +URL_LISTE=$(echo $sympaHost).$(echo $domain) +URL_PAHEKO="$httpProto://${paheko_API_USER}:${paheko_API_PASSWORD}@kaz-paheko.$(echo $domain)" +SETUP_MAIL="docker exec -ti mailServ setup" +NL_LIST=infos@listes.kaz.bzh +URL_AGORA_API=${URL_AGORA}/api/v4 +EQUIPE=kaz +LISTMASTER=$(echo ${sympa_LISTMASTERS} | cut -d',' -f1) + + +#### Test du serveur sur lequel s' execute le script #### +echo ${site} | grep -i prod2 && { echo "Le script ne fonctionne que sur Prod1 et Dev ";exit;} +############################## + +TFILE_EMAILS=$(mktemp /tmp/$RACINE.XXXXXXXXX.TFILE_EMAILS) +TFILE_EMAILS_LIST=$(mktemp /tmp/$RACINE.XXXXXXXXX.TFILE_EMAILS_LIST) +TFILE_MAILS_TROUVE=$(mktemp /tmp/$RACINE.XXXXXXXXX.TFILE_MAILS_TROUVE) +TFILE_MAILS_MATTERMOST=$(mktemp /tmp/$RACINE.XXXXXXXXTFILE_MAILS_MATTERMOST) +TFILE_CREATE_MAIL=$(mktemp /tmp/$RACINE.XXXXXXXXTFILE_CREATE_MAIL.sh) +LDAP_IP=$(docker inspect -f '{{.NetworkSettings.Networks.ldapNet.IPAddress}}' ldapServ) + +regexMail="^(([A-Za-z0-9]+((\.|\-|\_|\+)?[A-Za-z0-9]?)*[A-Za-z0-9]+)|[A-Za-z0-9]+)@(([A-Za-z0-9]+)+((\.|\-|\_)?([A-Za-z0-9]+)+)*)+\.([A-Za-z]{2,})+$" + + +rm -rf /tmp/$RACINE* +rm -rf /tmp/*.json + +############################################ Fonctions ####################################################### + +ExpMail() { + MAIL_DEST=$1 + MAIL_SUJET=$2 + MAIL_TEXTE=$3 + # a mettre ailleurs + mailexp=${service_mail} + mailpassword=${service_password} + mailserveur=${smtpHost}.${domain} + printf "Subject:${MAIL_SUJET}\n${MAIL_TEXTE}" | msmtp ${MAIL_DEST} +} + +PostMattermost() { + PostM=$1 + CHANNEL=$2 + TEAMID=$(curl -s -H "Authorization: Bearer ${mattermost_token}" "${URL_AGORA_API}/teams/name/${EQUIPE}" | jq .id | sed -e 's/"//g') + CHANNELID=$(curl -s -H "Authorization: Bearer ${mattermost_token}" ${URL_AGORA_API}/teams/${TEAMID}/channels/name/${CHANNEL} | jq .id | sed -e 's/"//g') + curl -s i-X POST -i -H "Authorization: Bearer ${mattermost_token}" -d "{\"channel_id\":\"${CHANNELID}\",\"message\":\"${PostM}\"}" "${URL_AGORA_API}/posts" >/dev/null 2>&1 +} + + +searchEmail() { + # on peut appeler cette fonction avec un paramêtre + # qui doit être une adresse email + CHOIX_MAIL="" + SEARCH_OBJECT_CLASS="inetOrgPerson" + [ "$1" = "alias" ] && SEARCH_OBJECT_CLASS="PostfixBookMailForward" + rm -rf $TFILE_MAILS_TROUVE + rm -rf $TFILE_MAILS + fait=0 + while [ ${fait} = 0 ] + do + clear + echo "----------------------------------------------------------------------" + printKazMsg "${ACTION_EN_COURS}" + echo "----------------------------------------------------------------------" + read -p "Adresse ou caractere contenu dans cette adresse (r ou R pour retour ) ? : " RMAIL + [[ ${RMAIL} =~ ^[rRqQ]$ ]] && Main + if [ "${RMAIL}" == "" ] + then + fait=0 + else + fait=1 + fi + done + ldapsearch -H ldap://${LDAP_IP} \ + -x -D "cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}" \ + -w "${ldap_LDAP_ADMIN_PASSWORD}" \ + -b "${ldap_root}" "(&(objectclass=${SEARCH_OBJECT_CLASS})(cn=*${RMAIL}*))" cn | grep ^cn | sed -e 's/^cn: //' >$TFILE_EMAILS + COMPTEUR_LIGNE=0 + while read LIGNE + do + COMPTEUR_LIGNE=$(expr $COMPTEUR_LIGNE + 1) + printf "%d - %s\n" $COMPTEUR_LIGNE $LIGNE >>$TFILE_MAILS_TROUVE + done <$TFILE_EMAILS + if [ $COMPTEUR_LIGNE -gt 0 ] + then + fait=0 + while [ ${fait} = 0 ] + do + echo "----------------------------------------------------------------------" + cat $TFILE_MAILS_TROUVE + echo "----------------------------------------------------------------------" + read -p "Choisir le numéro correspondant ( R ou r ou 0 pour retour ): " NB_LIGNE_MAIL + # si on tape r ou r ou Q ou q ou 0 on relance le menu de recherche de mail + [[ $NB_LIGNE_MAIL =~ [rRqQ0] ]] && searchEmail $1 + CHOIX_MAIL=$(cat ${TFILE_MAILS_TROUVE} | grep "^${NB_LIGNE_MAIL}\b" | awk '{print $3}' | tr -d '[:space:]') + + # si on répond par entrée nb_ligne_mail sera vide + # si on donne une réponse qui est une lettre ou un mauvais chiffre choix_mail sera vide + # alors on reboucle sur la liste des mails + + if [ "$CHOIX_MAIL" == "" ] || [ "$NB_LIGNE_MAIL" == "" ] + then + CHOIX_MAIL="" + fait=0 + else + fait=1 + fi + done + else + searchEmail $1 + fi +} + +searchMattermost() { + #Ici $1 est une adresse email + docker exec -ti ${mattermostServName} bin/mmctl --suppress-warnings auth login $httpProto://$URL_AGORA --name local-server --username $mattermost_user --password $mattermost_pass >/dev/null 2>&1 + docker exec -ti ${mattermostServName} bin/mmctl --suppress-warnings config set ServiceSettings.EnableAPIUserDeletion "true" >/dev/null 2>&1 + #on créé la list des mails dans mattermost + docker exec -ti ${mattermostServName} bin/mmctl --suppress-warnings user list --all >${TFILE_MAILS_MATTERMOST} 2>/dev/null + REP_SEARCH_MATTERMOST=$(cat ${TFILE_MAILS_MATTERMOST} | grep $1 | awk '{print $2}' | tr -d '[:space:]') + if [ ! -z ${REP_SEARCH_MATTERMOST} ] + then + if [ "$2" = "DETAILS" ] + then + touch /tmp/$1-mattermost.json + docker exec -ti ${mattermostServName} bin/mmctl --suppress-warnings --format json user search $1 >/tmp/$1-mattermost.json 2>/dev/null + echo -e "${RED} Compte : ${GREEN} " ;jq .username /tmp/$1-mattermost.json + echo -e "${RED} Mail : ${GREEN} " ; jq .email /tmp/$1-mattermost.json + echo -e "${RED} Role : ${GREEN} " ; jq .roles /tmp/$1-mattermost.json + else + echo "${REP_SEARCH_MATTERMOST}" + fi + else + echo "" + fi +} + + +infoEmail() { + ACTION_EN_COURS="Information sur un compte" + while : + do + clear + echo "------------------------------------------------" + printKazMsg "${ACTION_EN_COURS}" + echo "------------------------------------------------" + read -p "Alias ou Mail ? (R pour retour ou M/A [M] :" RINFOMAIL + case ${RINFOMAIL} in + "" | M | m ) + infofait=O + searchEmail + clear + echo "------------------------------------------------" + printKazMsg " ADRESSE DE MESSAGERIE : $CHOIX_MAIL" + echo "------------------------------------------------" + printKazMsg " DETAILS DU COMPTE DANS MATTERMOST" + searchMattermost $CHOIX_MAIL DETAILS + echo " ------------------------------------------------" + printKazMsg " DETAILS DU COMPTE DANS NEXTCLOUD PRINCIPAL" + echo -e "" + #TEMP_USER_NC=$(mktemp /tmp/$RACINE.XXXXXXXXX.TEMP_USER_NC) + #curl -s -o $TEMP_USER_NC -X GET -H 'OCS-APIRequest:true' $httpProto://admin:$nextcloud_NEXTCLOUD_ADMIN_PASSWORD@$URL_NC/ocs/v1.php/cloud/users?search=$CHOIX_MAIL + #cat $TEMP_USER_NC | grep -i "element" | sed -e s/[\<\>\/]//g | sed -e s/element//g + echo -ne "${NC}" + echo -ne " - Nextcloud enable : " + echo -ne "${GREEN}" + ldapsearch -H ldap://${LDAP_IP} -D "cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}" -x -w "${ldap_LDAP_ADMIN_PASSWORD}" -b "cn=${CHOIX_MAIL},ou=users,${ldap_root}" | grep -i nextcloudEnabled | cut -c 18-30 + echo -ne "${NC}" + echo -e "${NC} ------------------------------------------------" + printKazMsg " DETAILS DU COMPTE DANS LDAP ET PAHEKO" + echo "" + curl -s ${URL_PAHEKO}/api/sql -d "SELECT nom,adresse,code_postal,ville,email,email_secours,admin_orga,nom_orga,quota_disque FROM users where email='${CHOIX_MAIL}' LIMIT 1;" >/tmp/$CHOIX_MAIL-paheko.json + jq .results[].nom /tmp/$CHOIX_MAIL-paheko.json + jq .results[].adresse /tmp/$CHOIX_MAIL-paheko.json + jq .results[].code_postal /tmp/$CHOIX_MAIL-paheko.json + jq .results[].ville /tmp/$CHOIX_MAIL-paheko.json + echo -n " - Quota (Paheko) : " + echo -ne "${GREEN}" + jq .results[].quota_disque /tmp/$CHOIX_MAIL-paheko.json + echo -ne "${NC}" + echo -n " - Quota Mail (Ldap) : " + echo -ne "${GREEN}" + ldapsearch -H ldap://${LDAP_IP} -D "cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}" -x -w "${ldap_LDAP_ADMIN_PASSWORD}" -b "cn=${CHOIX_MAIL},ou=users,${ldap_root}" | grep -i mailquota | cut -c 11-60 + echo -ne "${NC}" + echo -n " - Quota Nextcloud (Ldap) : " + echo -ne "${GREEN}" + ldapsearch -H ldap://${LDAP_IP} -D "cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}" -x -w "${ldap_LDAP_ADMIN_PASSWORD}" -b "cn=${CHOIX_MAIL},ou=users,${ldap_root}" | grep -i nextcloudquota | cut -c 17-60 + echo -ne "${NC}" + echo -n " - Mail de secours (Paheko ): " + echo -ne "${GREEN}" + jq .results[].email_secours /tmp/$CHOIX_MAIL-paheko.json | sed -e 's/"//g' + echo -ne "${NC}" + echo -n " - Mail de secours (Ldap): " + echo -ne "${GREEN}" + ldapsearch -H ldap://${LDAP_IP} -D "cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}" -x -w "${ldap_LDAP_ADMIN_PASSWORD}" -b "cn=${CHOIX_MAIL},ou=users,${ldap_root}" | grep -i maildeSecours | sed -e 's/mailDeSecours://' + echo -ne "${NC}" + echo -n " - Alias (Ldap) : " + echo -ne "${GREEN}" + LDAP_ALIAS=$(ldapsearch -H ldap://${LDAP_IP} -D "cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}" -x -w "${ldap_LDAP_ADMIN_PASSWORD}" -b "cn=${CHOIX_MAIL},ou=users,${ldap_root}" | grep -i alias | cut -c 11-60) + echo -ne "${NC}" + echo -ne "${GREEN}" + for ldap_alias in ${LDAP_ALIAS} + do + echo "${ldap_alias}" | tr -d [:space:] + echo -n " " + done + echo -e "${NC}" + echo " - L' adresse apparait dans :" + echo -n -e "${GREEN}" + curl -s ${URL_PAHEKO}/api/sql -d "SELECT * FROM users where emails_Rattaches like '%$CHOIX_MAIL%' ;" | jq .results[].nom + echo -e "${NC}" + read -p " ==> Entrée pour continuer <==" + ;; + A | a ) + searchEmail alias + echo "------------------------------------------------" + echo " Alias : ${CHOIX_MAIL} " + echo "" + for INFOALIAS in $(ldapsearch -H ldap://${LDAP_IP} -x -D "cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}" \ + -w "${ldap_LDAP_ADMIN_PASSWORD}" -b "${ldap_root}" "(&(objectclass=PostfixBookMailForward)(cn=*${CHOIX_MAIL}*))" mail \ + | grep ^mail: | sed -e 's/^mail://') + do + echo -ne "=====> ${GREEN} " + echo "${INFOALIAS}" | tr -d [:space:] + echo "${NC}" + done + echo "" + read -p "==> Entrée pour continuer <==" + ;; + r | R ) + Main + ;; + * ) + echo "" + ;; + esac + done +} + +searchDestroy() { + + ACTION_EN_COURS="Suppression d'un compte" + clear + #TODO pourquoi REP_SEARCH_DESTROY=$(searchEmail) ne marche pas et fait déconner la fonction search_mail, j' en suis la à m' esbaudir + searchEmail + REP_SEARCH_DESTROY=$CHOIX_MAIL + echo "CHOIX=$REP_SEARCH_DESTROY" + echo "--------------------------------- SUPPRESION ----------------------------------------" + while : + do + echo "----------------------------------------------------------------------" + printKazMsg "${GREEN}${ACTION_EN_COURS}${NC}" + echo "----------------------------------------------------------------------" + echo -e "${BLINK} TOUT RETOUR EN ARRIERE EST IMPOSSIBLE ${NC}" + read -p "ON CONTINUE ? [ o / n ]: " SEARCH_DESTROY_INPUT + if [ "$SEARCH_DESTROY_INPUT" = "n" ] || [ "$SEARCH_DESTROY_INPUT" = "N" ] + then + searchDestroy + fi + if [ "$SEARCH_DESTROY_INPUT" = "o" ] || [ "$SEARCH_DESTROY_INPUT" = "O" ] + then + REP_SEARCH=$(searchMattermost $REP_SEARCH_DESTROY) + printKazMsg "réponse de mattermost : ${REP_SEARCH_DESTROY}" + if [ ! -z ${REP_SEARCH} ] + then + echo -e "${RED} suppression de ${REP_SEARCH_DESTROY} dans mattermost ${NC}" + docker exec -ti ${mattermostServName} bin/mmctl user delete ${REP_SEARCH_DESTROY} --confirm >/dev/null 2>&1 + if [ "$?" -eq "0" ] + then + printKazMsg "Suppresion ok" + else + printKazError -e "Erreur de suppression" + fi + echo " ----------------- " + else + echo "Rien a supprimer dans mattermost" + fi + echo -e "${NC}" + echo -e "Recherche de ${GREEN} ${REP_SEARCH_DESTROY} ${NC} dans nextcloud" + USER_NEXTCLOUD_SUPPR=$(curl -s -X GET -H 'OCS-APIRequest:true' $httpProto://admin:$nextcloud_NEXTCLOUD_ADMIN_PASSWORD@$URL_NC/ocs/v1.php/cloud/users?search=${REP_SEARCH_DESTROY} | grep element | sed -s 's/[ \<\>\/]//g' | sed 's/element//g') + if [ ! -z ${USER_NEXTCLOUD_SUPPR} ] + then + printKazMsg "le user trouvé est : ${USER_NEXTCLOUD_SUPPR}" + echo -e "${RED} Suppresion de ${USER_NEXTCLOUD_SUPPR}" + curl -H 'OCS-APIREQUEST: true' -X DELETE $httpProto://admin:$nextcloud_NEXTCLOUD_ADMIN_PASSWORD@$URL_NC/ocs/v1.php/cloud/users/${USER_NEXTCLOUD_SUPPR} >/dev/null 2>&1 + if [ "$?" -eq "0" ] + then + printKazMsg "Suppresion ok" + else + printKazError "Erreur de suppression" + fi + else:w + + printKazMsg "Rien à supprimer dans Nextcloud" + fi + echo -e "${NC}" + echo "" + echo -e "${RED} suppression de ${REP_SEARCH_DESTROY} dans la liste info de sympa" + echo -e "${NC}" + echo "" + docker exec -ti sympaServ /usr/lib/sympa/bin/sympa_soap_client.pl --soap_url=${httpProto}://${URL_LISTE}/sympasoap --trusted_application=${sympa_SOAP_USER} --trusted_application_password=${sympa_SOAP_PASSWORD} --proxy_vars=USER_EMAIL=${LISTMASTER} --service=del --service_parameters="${NL_LIST},${REP_SEARCH_DESTROY}" + echo -e "${NC}" + echo "" + echo -e "${RED} suppression de ${REP_SEARCH_DESTROY} dans le serveur de mail" + echo -e "${NC}" + echo "" + ${SETUP_MAIL} email del -y ${REP_SEARCH_DESTROY} >/dev/null 2>&1 + echo -e "${NC}" + echo "" + echo -e "${RED} suppression de ${REP_SEARCH_DESTROY} dans le ldap" + echo -e "${NC}" + echo "" + ldapdelete -H ldap://${LDAP_IP} -D "cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}" -x -w "${ldap_LDAP_ADMIN_PASSWORD}" "cn=${REP_SEARCH_DESTROY},ou=users,${ldap_root}" + if [ "$?" -eq "0" ] + then + printKazMsg "Suppresion ok" + else + printKazError "Erreur de suppression" + fi + printKazMsg "Envoi d'un message dans mattermost pour la suppression du compte" + docker exec -ti mattermostServ bin/mmctl post create kaz:Creation-Comptes --message "Le compte ${REP_SEARCH_DESTROY} est supprimé" >/dev/null 2>&1 + MAIL_SUPPR="Suppression du compte ${REP_SEARCH_DESTROY}" + docker exec -i mailServ mailx -a 'Content-Type: text/plain; charset="UTF-8"' -r contact@${domain} -s "Suppression de mail" contact@${domain} << EOF + ${MAIL_SUPPR} +EOF + echo -e "${NC}" + read -p " ---------------------- Appuyer sur une touche pour continuer -------------------------" + searchDestroy + fi + done +} + +gestPassword() { + ACTION_EN_COURS="Gestion du mot de passe d' un compte" + searchEmail + #cree un mdp acceptable par postfix/nc/mattermost + PASSWORD=_`apg -n 1 -m 10 -M NCL -d`_ + COMPTE_A_MODIFIER=${CHOIX_MAIL} + + # On considère le mot de passe de secours de ldap + # FICMAILSECOURS=$(mktemp /tmp/FICMAILSECOURSXXXXX.json) + # curl -s ${URL_PAHEKO}/api/sql -d "SELECT email_secours FROM users where email='${COMPTE_A_MODIFIER}' LIMIT 1 ;" >$FICMAILSECOURS + # MAIL_SECOURS=$(jq .results[].email_secours $FICMAILSECOURS | sed -e 's/\"//g') + + MAIL_SECOURS=$(ldapsearch -H ldap://${LDAP_IP} \ + -x -D "cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}" \ + -w "${ldap_LDAP_ADMIN_PASSWORD}" \ + -b "${ldap_root}" "(&(objectclass=inetOrgPerson)(cn=*${CHOIX_MAIL}*))" | grep ^mailDeSecours | sed -e 's/^mailDeSecours: //') + if [ "$MAIL_SECOURS" = "" ] + then + ADRESSE_SEC="NON" + else + ADRESSE_SEC="OUI" + fi + ############################################################################## + # on cherche le moyen de savoir si ce mail est le mail dans une orga + # si c' est le cas ça veut dire qu' il faut changer le mot de passe dans le cloud de l' orga + rm -rf /tmp/FICMAILORGA* + EMAIL_ORGA_TROUVE=0 + EMAIL_ORGA_TROUVE=$(curl -s ${URL_PAHEKO}/api/sql -d "SELECT id FROM users where emails_Rattaches like '%$CHOIX_MAIL%' ;" | jq .results[].id | wc -l) + if [ $EMAIL_ORGA_TROUVE -ge 1 ] + then + printKazError "L' adresse apparait dans plusieurs orga comme mails rattaché IL EST PREFERABLE DE REPONDRE NON !!!" + fi + ########################################################################## + read -p "ON CONTINUE ? [ o / n ]: " SEARCH_RESET_INPUT + if [ "$SEARCH_RESET_INPUT" = "n" ] || [ "$SEARCH_RESET_INPUT" = "N" ] + then + clear + gestPassword + fi + if [ "$SEARCH_RESET_INPUT" = "o" ] || [ "$SEARCH_RESET_INPUT" = "O" ] + then + USER_NEXTCLOUD_MODIF=$(curl -s -X GET -H 'OCS-APIRequest:true' $httpProto://admin:$nextcloud_NEXTCLOUD_ADMIN_PASSWORD@$URL_NC/ocs/v1.php/cloud/users?search=${COMPTE_A_MODIFIER} | grep element | sed -e 's/[ \<\>\/]//g' -e 's/element//g') + echo -e "$GREEN Compte à modifier = $RED ${COMPTE_A_MODIFIER} ${NC}" + echo -e "$GREEN Mail de secours = $RED ${MAIL_SECOURS} ${NC}" + echo -e "$GREEN Compte $RED $(searchMattermost $COMPTE_A_MODIFIER) ${NC}" + echo -e "$GREEN Compte Nextcloud $RED ${USER_NEXTCLOUD_MODIF} ${NC}" + echo -e "$GREEN Le mot de passe sera = $RED ${PASSWORD} ${NC}" + docker exec -ti mattermostServ bin/mmctl user change-password $(searchMattermost $COMPTE_A_MODIFIER) -p $PASSWORD >/dev/null 2>&1 + curl -H 'OCS-APIREQUEST: true' -X PUT $httpProto://admin:$nextcloud_NEXTCLOUD_ADMIN_PASSWORD@$URL_NC/ocs/v1.php/cloud/users/${USER_NEXTCLOUD_MODIF} -d key=password -d value=${PASSWORD} >/dev/null 2>&1 + ${SETUP_MAIL} email update ${COMPTE_A_MODIFIER} ${PASSWORD} + pass=$(mkpasswd -m sha512crypt ${PASSWORD}) + echo -e "\n\ndn: cn=${COMPTE_A_MODIFIER},ou=users,${ldap_root}\n\ +changeType: modify\n\ +replace: userPassword\n\ +userPassword: {CRYPT}${pass}\n\n" | ldapmodify -c -H ldap://${LDAP_IP} -D "cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}" -x -w "${ldap_LDAP_ADMIN_PASSWORD}" + echo -e "Envoi d'un message dans mattermost pour la modification du mot de passe" + docker exec -ti mattermostServ bin/mmctl post create kaz:Creation-Comptes --message "Le mot de passe du compte ${COMPTE_A_MODIFIER} a été modifié" >/dev/null 2>&1 + if [ $ADRESSE_SEC == "OUI" ] + then + echo -e "Envoi d'un message à l' adresse de secours : $GREEN${MAIL_SECOURS}${NC}" + MAIL_CHANG=" +Hello, + +le nouveau mot de passe de ${COMPTE_A_MODIFIER} est : ${PASSWORD} + +le webmail est disponible à https://webmail.kaz.bzh +Le site d' aide pour la configuration des services : https://wiki.kaz.bzh +Le site Web de Kaz : https://kaz.bzh +Le mail de la collégiale : contact@kaz.bzh + +A bientôt" + docker exec -i mailServ mailx -a 'Content-Type: text/plain; charset="UTF-8"' -r admin@${domain} -s "Modification du compte" $MAIL_SECOURS << EOF + ${MAIL_CHANG} +EOF + fi + if [ $ADRESSE_SEC == "NON" ] + then + echo -e "${RED} Pas d adresse de secours ${NC}" + fi + fi + +} + +createMail() { + ACTION_EN_COURS="Création d' une adresse de messagerie" + QUOTA=1 + TRUE_KAZ=TRUE + fait=0 + # On demande le mail souhaite on regarde si c' est valide et si ça existe déjà + while [ $fait -eq 0 ] + do + clear + echo "----------------------------------------------------------------------" + printKazMsg "${ACTION_EN_COURS}" + echo "----------------------------------------------------------------------" + read -p "Mail souhaité (r ou R pour quitter ) : " EMAIL_SOUHAITE + [[ ${EMAIL_SOUHAITE} =~ ^[rRqQ]$ ]] && Main + if [[ ${EMAIL_SOUHAITE} =~ ${regexMail} ]] + then + ldapsearch -H ldap://${LDAP_IP} \ + -x -D "cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}" \ + -w "${ldap_LDAP_ADMIN_PASSWORD}" \ + -b "${ldap_root}" "(&(objectclass=inetOrgPerson)(cn=${EMAIL_SOUHAITE}))" cn | grep ^cn | sed -e 's/^cn: //' >$TFILE_EMAILS + if grep -q "^${EMAIL_SOUHAITE}$" "${TFILE_EMAILS}" + then + printKazError " - Le mail ${EMAIL_SOUHAITE} existe déjà" + fait=0 + elif ! grep $(echo ${EMAIL_SOUHAITE} | sed -e 's/^.*@//') ${KAZ_CONF_DIR}/autorized-domains.txt >/dev/null + then + printKazMsg "Le domaine n' est pas dans la liste autorisée" + sleep 2 + fait=0 + else + fait=1 + fi + else + printKazError " - Mail invalide !" + sleep 2 + fait=0 + fi + done + # On demande le mail de secours et on teste si c' est un mail valide + fait=0 + while [ $fait -eq 0 ] + do + read -p "Mail de secours : " EMAIL_SECOURS + if [[ ${EMAIL_SECOURS} =~ ${regexMail} ]] + then + fait=1 + else + printKazError "Mail invalide" + fait=0 + fi + done + # On demande le prenom + read -p "Prénom : " PRENOM + # On demande le nom + read -p "Nom : " NOM + # On constitue l' ident kaz pour le ldap + IDENT_KAZ=$(unaccent utf8 "${PRENOM,,}.${NOM,,}") + # on créé le mot de passe + PASSWORD=_`apg -n 1 -m 10 -M NCL -d`_ + # on constitue le user,domain et pass crypté pour le ldap + LDAPUSER=$(echo ${EMAIL_SOUHAITE} | awk -F '@' '{print $1}') + LDAPDOMAIN=$(echo ${EMAIL_SOUHAITE} | awk -F '@' '{print $2}') + LDAPPASS=$(mkpasswd -m sha512crypt ${PASSWORD}) + [ "$(echo ${EMAIL_SOUHAITE} | sed -e 's/^.*@//')" != "${domain}" ] && TRUE_KAZ=FALSE + echo "${GREEN}Mail souhaité : ${NC}${EMAIL_SOUHAITE}" + echo "${GREEN}Mail secours : ${NC}${EMAIL_SECOURS}" + echo "${GREEN}Prénom : ${NC}${PRENOM}" + echo "${GREEN}Nom : ${NC}${NOM}" + echo "${GREEN}Mot de passe : ${NC}${PASSWORD}" + RCREATION="" + repRegEx='^[oOnN]$' + while ! [[ ${RCREATION} =~ ${repRegEx} ]] + do + read -p "Création ? o/n : " RCREATION + done + if [ ${RCREATION} = "n" ] || [ ${RCREATION} = "N" ] + then + Main + fi + # on créé le mail et on met les quotas avec le setup du docker mail pour que + # le contenu des fichiers à plat soient idem que l' annuaire ldap + ${SETUP_MAIL} email add ${EMAIL_SOUHAITE} ${PASSWORD} + ${SETUP_MAIL} quota set ${EMAIL_SOUHAITE} ${QUOTA}G + echo "echo -e '\n\ndn: cn=${EMAIL_SOUHAITE},ou=users,${ldap_root}\n\ +changeType: add\n\ +objectclass: inetOrgPerson\n\ +objectClass: PostfixBookMailAccount\n\ +objectClass: nextcloudAccount\n\ +objectClass: kaznaute\n\ +sn: ${PRENOM} ${NOM}\n\ +mail: ${EMAIL_SOUHAITE}\n\ +mailEnabled: TRUE\n\ +mailGidNumber: 5000\n\ +mailHomeDirectory: /var/mail/${LDAPDOMAIN}/${LDAPUSER}/\n\ +mailQuota: ${QUOTA}G\n\ +mailStorageDirectory: maildir:/var/mail/${LDAPDOMAIN}/${LDAPUSER}/\n\ +mailUidNumber: 5000\n\ +mailDeSecours: ${EMAIL_SECOURS}\n\ +identifiantKaz: ${IDENT_KAZ}\n\ +quota: ${QUOTA}\n\ +nextcloudEnabled: ${TRUE_KAZ}\n\ +nextcloudQuota: ${QUOTA} GB\n\ +mobilizonEnabled: ${TRUE_KAZ}\n\ +agoraEnabled: ${TRUE_KAZ}\n\ +userPassword: {CRYPT}${LDAPPASS}\n\n' | ldapmodify -c -H ldap://${LDAP_IP} -D \"cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}\" -x -w ${ldap_LDAP_ADMIN_PASSWORD}" >${TFILE_CREATE_MAIL} +# on execute le fichier avec les données ldap pour créer l' entrée dans l' annuaire +bash ${TFILE_CREATE_MAIL} >/dev/null +# on colle le compte et le mot de passe dans le fichier +echo "Création de : ${EMAIL_SOUHAITE} avec le mot de passe : ${PASSWORD}" >>${TFILE_CREATE_MAIL} +OLDIFS=${IFS} +IFS='' +MSG_CREATION_MAIL="Bonjour + +Le compte de messagerie ${EMAIL_SOUHAITE} a été créé avec le mot de passe ${PASSWORD} + +Le mot de passe peut être changé à l' adresse https://mdp.${domain} + +Bonne journée" +ExpMail ${EMAIL_SECOURS} "Creation de ${EMAIL_SOUHAITE}" ${MSG_CREATION_MAIL} +docker exec -ti mattermostServ bin/mmctl post create kaz:Creation-Comptes --message "création du compte ${EMAIL_SOUHAITE} avec l' adresse de secours ${EMAIL_SECOURS}" >/dev/null 2>&1 +IFS=${OLDIFS} +echo " --------------------------------------------------------------------" +echo "! INFO : !" +echo "! Le Mot de passe !" +echo "! Est dans le fichier !" +echo "! ${TFILE_CREATE_MAIL} !" +echo " --------------------------------------------------------------------" +echo +read -p " - ENTREE pour continuer - " +# Retour au menu principal +Main +} + +createAlias() { + ACTION_EN_COURS="création d' un alias de messagerie" + fait=0 + # On demande alias souhaite on regarde si c' est valide et si ça existe déjà + while [ $fait -eq 0 ] + do + clear + echo "-------------------------------------------------" + printKazMsg "${ACTION_EN_COURS}" + echo "-------------------------------------------------" + read -p "Alias souhaité (r ou q pour quitter ) : " AMAIL + [[ ${AMAIL} =~ ^[rRqQ]$ ]] && Main + if [[ ${AMAIL} =~ ${regexMail} ]] + then + RESU_ALIAS=$(ldapsearch -H ldap://${LDAP_IP} \ + -x -D "cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}" \ + -w "${ldap_LDAP_ADMIN_PASSWORD}" \ + -b "${ldap_root}" "(&(objectclass=PostfixBookMailForward)(cn=*${AMAIL}*))" | grep ^cn | sed -e 's/^cn: //') + RESU_ALIAS_IS_MAIL=$(ldapsearch -H ldap://${LDAP_IP} \ + -x -D "cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}" \ + -w "${ldap_LDAP_ADMIN_PASSWORD}" \ + -b "${ldap_root}" "(&(objectclass=inetOrgPerson)(cn=*${AMAIL}*))" cn | grep ^cn | sed -e 's/^cn: //') + + if echo ${RESU_ALIAS} | grep -q "^${AMAIL}$" || echo ${RESU_ALIAS_IS_MAIL} | grep -q "^${AMAIL}$" + then + echo "${GREEN} - alias ou mail ${AMAIL} déjà dans présent l' annuaire${NC}" + sleep 2 + fait=0 + elif ! grep $(echo ${AMAIL} | sed -e 's/^.*@//') ${KAZ_CONF_DIR}/autorized-domains.txt >/dev/null + then + echo -e "${GREEN}Le domaine n' est pas dans la liste autorisée${NC}" + sleep 2 + fait=0 + else + fait=1 + fi + else + echo -e "${GREEN} - Mail invalide !${NC}" + sleep 2 + fait=0 + fi + done + echo "Alias : ${AMAIL} peut être créé" + declare -a TABAMAIL + fait=0 + while [ $fait -eq 0 ] + do + clear + echo "-----------------------------------------------------------------" + echo "Ajout de mail pour cet alias" + echo "-----------------------------------------------------------------" + [ "${TABAMAIL[*]}" == "" ] || echo -e "Liste des mails dans cet alias: [ ${GREEN}${TABAMAIL[*]}${NC} ]" + read -p "ENTREE ou F ( pour fin :-) pour une liste vide ou saisir une adresse et enfin saisir F pour arrêter : " RALIAS + if [[ ${RALIAS} =~ ^[Ff]$ ]] || [ "${RALIAS}" == "" ] + then + fait=1 + else + if echo ${TABAMAIL[*]} | grep -i ${RALIAS} >/dev/null 2>&1 + then + echo -e "${GREEN}l' alias est déjà dans la liste ${NC}" + sleep 2 + else + if [[ ${RALIAS} =~ ${regexMail} ]] + then + TABAMAIL+=(${RALIAS}) + else + echo -e "${GREEN} - Mail invalide !${NC}" + sleep 2 + fi + fi + fi + done + fait=0 + while [ "$fait" = 0 ] + do + clear + echo "--------------------------------------------------" + echo -e "${GREEN}résumé de la situation${NC}" + echo "--------------------------------------------------" + echo -e "création d' un alias : ${GREEN}${AMAIL}${NC}" + echo -e "avec le(s) Mail suivant : ${GREEN}${TABAMAIL[*]}${NC}" + echo "--------------------------------------------------" + RCREATONALIAS="" + read -p "OK pour la création ?(o/n) : " RCREATIONALIAS + case "$RCREATIONALIAS" in + o | O ) + for ALIASMAIL in ${TABAMAIL[*]} + do + LDAPALAISMAIL="${LDAPALAISMAIL}$(echo mail :${ALIASMAIL})\n" + done + echo -e "\n\ndn: cn=${AMAIL},ou=mailForwardings,${ldap_root}\n\ +changeType: add\n\ +objectClass: organizationalRole\n\ +objectClass: PostfixBookMailForward\n\ +mailAlias: ${AMAIL}\n\ +${LDAPALAISMAIL}\n\n" | ldapmodify -c -H ldap://${LDAP_IP} -D "cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}" -x -w ${ldap_LDAP_ADMIN_PASSWORD} + fait=1 + printKazMsg "Création de ${AMAIL}" + sleep 3 + createAlias + ;; + n | N ) + printKazMsg "abandon de création" + sleep 2 + createAlias + ;; + * ) + fait=0 + ;; + esac + done +} + +delAlias() { + ACTION_EN_COURS="Suppresion d' un alias de messagerie" + fait=0 + while [ $fait -eq 0 ] + do + RALIAS="" + CHOIX_MAIL="" + RESU_ALIAS="" + searchEmail alias + RALIAS=${CHOIX_MAIL} + [[ ${RALIAS} =~ ^[rRqQ]$ ]] && Main + if [[ ${RALIAS} =~ ${regexMail} ]] + then + RESU_ALIAS=$(ldapsearch -H ldap://${LDAP_IP} \ + -x -D "cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}" \ + -w "${ldap_LDAP_ADMIN_PASSWORD}" \ + -b "${ldap_root}" "(&(objectclass=PostfixBookMailForward)(cn=${RALIAS}))" cn | grep ^cn | sed -e 's/^cn: //') + if [ ! -z ${RESU_ALIAS} ] + then + faitdel=0 + while [ ${faitdel} = 0 ] + do + read -p "suppression de ${RESU_ALIAS} ? (o/n): " REPDELALIAS + case "${REPDELALIAS}" in + o | O ) + ldapdelete -H ldap://${LDAP_IP} -D "cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}" -x -w "${ldap_LDAP_ADMIN_PASSWORD}" "cn=${RESU_ALIAS},ou=mailForwardings,${ldap_root}" + printKazMsg "suppression ${RESU_ALIAS} effectuée" + sleep 2 + faitdel=1 + ;; + n | N ) + faitdel=1 + ;; + "" | * ) + faitdel=0 + ;; + + esac + done + else + fait=0 + fi + + else + printKazError " - format alias invalide !" + sleep 2 + fait=0 + fi + done + delAlias +} + +modifyAlias() +{ + ACTION_EN_COURS="Modfication d' un alias de messagerie" + MRESU_ALIAS="" + LISTE_MAIL_ALIAS="" + NEW_LISTE_MAIL_ALIAS="" + ACHANGE=0 + searchEmail alias + LISTE_MAIL_ALIAS=$(ldapsearch -H ldap://${LDAP_IP} \ + -x -D "cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}" \ + -w "${ldap_LDAP_ADMIN_PASSWORD}" \ + -b "${ldap_root}" "(&(objectclass=PostfixBookMailForward)(cn=*${CHOIX_MAIL}*))" \ + | grep -i ^mail: | sed -e 's/^mail: /_/' | tr -d [:space:] | sed -s 's/_/ /g') + echo "-------------------------------------------------------------------" + echo "-------------------------------------------------------------------" + echo -e "${GREEN} [ ${LISTE_MAIL_ALIAS} ] ${NC} " + echo "-------------------------------------------------------------------" + BOUCLE_M_MAIL=0 + NEW_LISTE_MAIL_ALIAS="" + for M_MAIL in ${LISTE_MAIL_ALIAS} + do + read -p " - On garde l' alias ${M_MAIL} (o/n) ? [o] : " READM_MAIL + case ${READM_MAIL} in + "" | o | O ) + NEW_LISTE_MAIL_ALIAS="${NEW_LISTE_MAIL_ALIAS} ${M_MAIL}" + ;; + n | N ) + ACHANGE=$(expr ${ACHANGE} + 1) + NEW_LISTE_MAIL_ALIAS=${NEW_LISTE_MAIL_ALIAS} + ;; + * ) + NEW_LISTE_MAIL_ALIAS="${NEW_LISTE_MAIL_ALIAS} ${M_MAIL}" + ;; + esac + done + TALIAS_SUPP="" + BOUCLE_ADD_MAIL=0 + BOUCLE_ALIAS=0 + read -p "Ajouter un alias ? (o/n) [n] :" RALIAS_SUPP + while [ $BOUCLE_ADD_MAIL = 0 ] + do + case ${RALIAS_SUPP} in + o | O ) + while [ ${BOUCLE_ADD_MAIL} = 0 ] + do + read -p " - Nouvel Alias: ( F pour finir ) : " ALIAS_SUPP + if [[ ${ALIAS_SUPP} =~ ${regexMail} ]] + then + if echo ${NEW_LISTE_MAIL_ALIAS} | grep -w ${ALIAS_SUPP} >/dev/null + then + printKazMsg "Alias déjà existant" + BOUCLE_ADD_MAIL=0 + else + NEW_LISTE_MAIL_ALIAS="${NEW_LISTE_MAIL_ALIAS} ${ALIAS_SUPP}" + ACHANGE=$(expr ${ACHANGE} + 1) + fi + elif [[ ${ALIAS_SUPP} =~ ^[[fF]+$ ]] + then + BOUCLE_ADD_MAIL=1 + else + printKazMsg "erreur de mail" + fi + done + ;; + "" | n | N ) + BOUCLE_ADD_MAIL=1 + ;; + * ) + BOUCLE_ADD_MAIL=1 + ;; + esac + done + if [ "${ACHANGE}" -gt "0" ] + then + printKazMsg "Changement à effectuer : ${NEW_LISTE_MAIL_ALIAS}" + sleep 3 + FIC_MODIF_LDIF=$(mktemp /tmp/$RACINE.XXXXXXXXX.TFIC_MODIF_LDIF.ldif) + echo "dn: cn=${CHOIX_MAIL},ou=mailForwardings,${ldap_root}" >${FIC_MODIF_LDIF} + echo "changeType: modify" >>${FIC_MODIF_LDIF} + echo "replace: mail" >>${FIC_MODIF_LDIF} + for key in ${NEW_LISTE_MAIL_ALIAS} + do + echo "mail: ${key}" >>${FIC_MODIF_LDIF} + done + echo "-" >>${FIC_MODIF_LDIF} + ldapmodify -c -H ldap://${LDAP_IP} -D "cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}" \ + -x -w ${ldap_LDAP_ADMIN_PASSWORD} \ + -f ${FIC_MODIF_LDIF} >/dev/null + else + printKazMsg "Pas de changement" + sleep 3 + fi +} + +updateUser() { + rm -rf /tmp/*attributs.txt + ACTION_EN_COURS="Modification d'un compte" + ATTRIB_MAILS="mailDeSecours mailAlias" + ATTRIB_QUOTA="mailQuota nextcloudQuota" + rm -rf /tmp/*fic_attributs.txt + while : + do + FIC_ATTRIBUTS=$(mktemp /tmp/XXXXfic_attributs.txt) + CHOIX_MAIL="" + declare -a ATTRIB + searchEmail + MAILDESECOURSACTUEL="" + MAILDESECOURS="" + Ucompteur=1 + for attribut in mailDeSecours mailAlias mailQuota nextcloudQuota + do + ATTRIB+=([${attribut}]=$(ldapsearch -H ldap://${LDAP_IP} \ + -x -D "cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}" \ + -w "${ldap_LDAP_ADMIN_PASSWORD}" \ + -b "${ldap_root}" "(&(objectclass=inetOrgPerson)(cn=*${CHOIX_MAIL}*))" \ + | grep ^"${attribut}": | sed -e 's/^'${attribut}': //' | tr -s '[:space:]' ' ' )) + # si l' attribut est mailDesecours on l' attrape et on on le stocke pour pouvoir l' enlever de sympa + [ ${attribut} = mailDeSecours ] && MAILDESECOURSACTUEL=${ATTRIB[${attribut}]} + echo -e "${Ucompteur} - ${attribut} = ${ATTRIB[${attribut}]}" >>${FIC_ATTRIBUTS} + Ucompteur=$(expr ${Ucompteur} + 1) + done + faitAttrib=0 + declare -A CHANGED + while [ ${faitAttrib} = 0 ] + do + clear + echo "---------------------------------------------------" + printKazMsg "${ACTION_EN_COURS} ${CHOIX_MAIL}" + echo "---------------------------------------------------" + [ "${#CHANGED[@]}" -gt "0" ] && echo "------ Les Modification en cours -------------" + for key in "${!CHANGED[@]}" + do + echo "${GREEN}${key}${NC}: ${CHANGED[$key]}" + done + [ "${#CHANGED[@]}" -gt "0" ] && echo "---------------------------------------------------" + cat ${FIC_ATTRIBUTS} + read -p "Quel attribut est à changer ? (R ou r pour retour F pour Finir ): " REP_ATTRIBUT + case "${REP_ATTRIBUT}" in + r | R ) + if [ "${#CHANGED[@]}" -gt "0" ] + then + echo "------------------------------------------------------------------------" + read -p "=====> ATTENTION : il y a des modifs en cours abandonner ?(o ou n) ? <===== : " RABANDON + case "${RABANDON}" in + o | O ) + faitAttrib=1 + ;; + n | N ) + faitAttrib=0 + ;; + * ) + echo "" + ;; + esac + else + faitAttrib=1 + fi + ;; + [0-9] ) + if [ "${REP_ATTRIBUT}" -gt "$(expr ${Ucompteur} - 1 )" ] + then + printKazMsg "Chiffre invalide" + sleep 3 + else + # pour être sur de virer tous les espaces et les tab etc on utilise [:space:] + # on affiche tout les arguments ( les $1 $2 $2 avec la boucle for) + # on ajoute de ___ pour pouvoir le remplace par un espace entre chaque variable + # afin d' afficher un beau contenu1 contenu2 contenu 3 + + ATTRIBUT_EN_COURS=$(cat ${FIC_ATTRIBUTS} | grep "^${REP_ATTRIBUT}\b" | awk '{print $3}') + CONTENU_ATTRIBUT=$(cat ${FIC_ATTRIBUTS} | grep "^${REP_ATTRIBUT}\b" \ + | awk '{for (i=5; i<=NF; i++) print $i"___"}'\ + | tr -d [:space:] | sed -e 's/___/ /g') + + case ${ATTRIBUT_EN_COURS} in + mailQuota | nextcloudQuota ) + echo "------------------------------------------------" + read -p " - Nouveau ${ATTRIBUT_EN_COURS} (R ou r pour retour) : " RCHANGE + if [[ ${RCHANGE} =~ ^[0-9]+$ ]] + then + [ "${ATTRIBUT_EN_COURS}" = "mailQuota" ] && CHANGED+=([mailQuota]="${RCHANGE}G") + [ "${ATTRIBUT_EN_COURS}" = "nextcloudQuota" ] && CHANGED+=([nextcloudQuota]="${RCHANGE} GB") + else + printKazError "Valeur incorecte" + sleep 3 + fi + faitAttrib=0 + ;; + mailDeSecours ) + echo "------------------------------------------------" + read -p " - Nouveau Mail de Secours : " RCHANGE + if [[ ${RCHANGE} =~ ${regexMail} ]] + then + CHANGED+=([mailDeSecours]=${RCHANGE}) + else + printKazMsg "Mail invalide" + sleep 3 + fi + faitAttrib=0 + ;; + mailAlias ) + NEW_CONTENU_ATTRIBUT="" + READVALMAIL="" + ALIAS_SUPP="" + RALIAS_SUPP="" + TALIAS_SUPP="" + BOUCLE_ADD_MAIL=0 + MAILALIAS_CHANGE=0 + for VALMAIL in ${CONTENU_ATTRIBUT} + do + read -p " - On garde ${VALMAIL} (o/n) ? [o] : " READVALMAIL + case ${READVALMAIL} in + * | "" | o | O ) + NEW_CONTENU_ATTRIBUT="${NEW_CONTENU_ATTRIBUT} ${VALMAIL}" + ;; + n | N ) + NEW_CONTENU_ATTRIBUT="${NEW_CONTENU_ATTRIBUT}" + MAILALIAS_CHANGE=$(expr ${MAILALIAS_INCHANGE} + 1) + ;; + * ) + printKazError "Erreur" + sleep 2 + esac + done + read -p "Ajouter un alias ? (o/n) [n] :" RALIAS_SUPP + case ${RALIAS_SUPP} in + o | O ) + while [ ${BOUCLE_ADD_MAIL} = 0 ] + do + read -p " - ${GREEN}Nouvel Alias: ( F pour finir ) :${NC} " ALIAS_SUPP + BOUCLE_ADD_MAIL=0 + if [[ ${ALIAS_SUPP} =~ ${regexMail} ]] + then + if echo "${CONTENU_ATTRIBUT}" | grep "^${ALIAS_SUPP}$" + then + printKazMsg "Alias déjà existant" + sleep 3 + else + TALIAS_SUPP="${TALIAS_SUPP} ${ALIAS_SUPP}" + MAILALIAS_CHANGE=$(expr ${MAILALIAS_INCHANGE} + 1) + fi + fi + if [[ ${ALIAS_SUPP} =~ ^[[fF]+$ ]] + then + BOUCLE_ADD_MAIL=1 + fi + done + ;; + "" | n | N ) + #CHANGED+=([mailAlias]="${NEW_CONTENU_ATTRIBUT}") + ;; + * ) + printKazMsg "Erreur" + sleep 2 + ;; + esac + [ "${MAILALIAS_CHANGE}" -gt "0" ] && CHANGED+=([mailAlias]="${NEW_CONTENU_ATTRIBUT} ${TALIAS_SUPP}") + faitattrib=0 + ;; + esac + fi + ;; + f | F ) + if [ "${#CHANGED[@]}" -eq "0" ] + then + printKazMsg "Il n'y a pas eu de modif" + sleep 3 + updateUser + else + echo "Départ des modifs" + LISTE_MODIF_USER="" + FIC_MODIF_LDIF=$(mktemp /tmp/$RACINE.XXXXXXXXX.TFIC_MODIF_LDIF.ldif) + echo "dn: cn=${CHOIX_MAIL},ou=users,${ldap_root}" >${FIC_MODIF_LDIF} + echo "changeType: modify" >>${FIC_MODIF_LDIF} + for key in "${!CHANGED[@]}" + do + if [ ${key} = "mailDeSecours" ] + then + MAILDESECOURS=${CHANGED[mailDeSecours]} + fi + if [ ${key} = "mailAlias" ] + then + echo "replace: ${key}" >>${FIC_MODIF_LDIF} + for KEY_MAILALIAS in ${CHANGED[$key]} + do + echo "${key}: ${KEY_MAILALIAS}" >>${FIC_MODIF_LDIF} + done + echo "-" >>${FIC_MODIF_LDIF} + else + echo "replace: ${key}" >>${FIC_MODIF_LDIF} + echo "${key}: ${CHANGED[$key]}" >>${FIC_MODIF_LDIF} + echo "-" >>${FIC_MODIF_LDIF} + fi + done + cat ${FIC_MODIF_LDIF} + sleep 3 + ldapmodify -c -H ldap://${LDAP_IP} -D "cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}" \ + -x -w ${ldap_LDAP_ADMIN_PASSWORD} \ + -f ${FIC_MODIF_LDIF} + if [ ! -z ${MAILDESECOURS} ] + then + # suppression du mail de secours de la liste infos + docker exec -ti sympaServ /usr/lib/sympa/bin/sympa_soap_client.pl --soap_url=${httpProto}://${URL_LISTE}/sympasoap --trusted_application=${sympa_SOAP_USER} --trusted_application_password=${sympa_SOAP_PASSWORD} --proxy_vars=USER_EMAIL=${LISTMASTER} --service=del --service_parameters="${NL_LIST},${MAILDESECOURSACTUEL}" + # ajout de l' adresse de la nouvelle adresse de secours + docker exec -ti sympaServ /usr/lib/sympa/bin/sympa_soap_client.pl --soap_url=${httpProto}://${URL_LISTE}/sympasoap --trusted_application=${sympa_SOAP_USER} --trusted_application_password=${sympa_SOAP_PASSWORD} --proxy_vars=USER_EMAIL=${LISTMASTER} --service=add --service_parameters="${NL_LIST},${MAILDESECOURS}" + fi + updateUser + fi + ;; + "" | * ) + faitAttrib=0 + ;; + esac + done + done + +} +############################################################################################################## +############################################################################################################## +usage () { + printKazMsg "$PRG [-h] [-v]" + printKazMsg "script de gestion des kaznautes" + printKazMsg " -h aide sur ce script" + printKazMsg " -v version du script." +} +############################################################################################################## +# test des dépendances +type -P jq | grep -i jq >/dev/null || { echo "erreur jq n' est pas installé ( apt install jq ? ) " ; exit;} +################ Main loop ############################################ +Main() { + +echo -e "${NC}" +while true +do +clear +echo "-------------------------------------------------" +echo "$PRG, version : $VERSION ($KAZ_ROOT)" +echo "-------------------------------------------------" +echo -e "${BOLD}COMPTES : " +echo -e "${NC}*********" +echo -e "${NC}" +echo -e " ${NC}1 - Information sur un compte (alias ou Mail )" +echo -e " ${YELLOW}2 - Modification d' un compte" +echo -e " ${NC}3 - Changement de mot de passe d' un compte" +echo -e " ${GREEN}4 - Suppression d' un compte " +echo -e " ${NC}5 - Création d' une adresse de messagerie" +echo -e "${NC}" +echo -e "${BOLD}ALIAS : " +echo -e "${NC}*******" +echo -e "${NC}" +echo -e " ${NC}6 - Création d' un alias Général pour mails Externes" +echo -e " ${YELLOW}7 - Modification d' un alias Général pour mails Externes" +echo -e " ${GREEN}8 - Suppression d' un alias Général pour mails Externes" +echo -e "${NC}" +echo "-------------------------------------------------" +echo -e "${NC}" +read -p "Choix ? (Q pour quitter) ==> : " CHOICE +case "$CHOICE" in + '1' ) + infoEmail + ;; + '2' ) + updateUser + ;; + '3' ) + gestPassword + ;; + '4' ) + searchDestroy + ;; + '5' ) + createMail + ;; + '6' ) + createAlias + ;; + '7' ) + modifyAlias + ;; + '8' ) + delAlias + ;; + 'h'| "H" ) + clear + echo "--------------------------------------------" + usage + echo "--------------------------------------------" + ;; + 'q'| "Q" ) + exit + ;; + ""|'*' ) + clear + ;; +esac +done +} + + +################################################################################################################################## + +# Lancement de la boucle principale +#traitement des options ( usage ) + +case "$1" in + '-h' ) + usage + exit + ;; + '-v' ) + printKazMsg "$PRG version $VERSION" + exit + ;; +esac + +# on teste la présence du domaine du site dans le fichier autorized-domains.txt +[ ! -e ${KAZ_CONF_DIR}/autorized-domains.txt ] && { echo "création de ${KAZ_CONF_DIR}/autorized-domains.txt" ; touch ${KAZ_CONF_DIR}/autorized-domains.txt;} +! grep $domain ${KAZ_CONF_DIR}/autorized-domains.txt && echo $domain >> ${KAZ_CONF_DIR}/autorized-domains.txt +Main + diff --git a/bin/indicateurs.sh b/bin/indicateurs.sh new file mode 100755 index 0000000..d871b6e --- /dev/null +++ b/bin/indicateurs.sh @@ -0,0 +1,77 @@ +#!/bin/bash +# 23/04/2021 +# script de mise a jour du fichier de collecte pour future intégration dans la base de donneyyy +# did + +KAZ_ROOT=$(cd $(dirname $0)/..; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +FIC_COLLECTE=${KAZ_STATE_DIR}/collecte.csv +FIC_ACTIVITE_MAILBOX=${KAZ_STATE_DIR}/activites_mailbox.csv + +mkdir -p ${KAZ_STATE_DIR} +mkdir -p ${KAZ_STATE_DIR}/metro + +#Jirafeau +echo "$(date +%Y-%m-%d-%H-%M-%S);" \ + "depot-count;" \ + "$(find ${DOCK_VOL}/jirafeau_fileData/_data/files/ -name \*count| wc -l)" >> "${FIC_COLLECTE}" +echo "$(date +%Y-%m-%d-%H-%M-%S);" \ + "depot-size;" \ + "$(du -ks ${DOCK_VOL}/jirafeau_fileData/_data/files/ | awk -F " " '{print $1}')" >> "${FIC_COLLECTE}" + +#PLACE DISQUE sur serveur +echo "$(date +%Y-%m-%d-%H-%M-%S);" \ + "disk-system-size-used;" \ + "$(df | grep sda | awk -F " " '{print $3}')" >> "${FIC_COLLECTE}" +echo "$(date +%Y-%m-%d-%H-%M-%S);" \ + "disk-system-size-used-human;" \ + "$(df -h | grep sda | awk -F " " '{print $3}')" >> "${FIC_COLLECTE}" + +#nombre de mails kaz: +echo "$(date +%Y-%m-%d-%H-%M-%S);" \ + "mailboxes;" \ + "$(cat ${KAZ_COMP_DIR}/postfix/config/postfix-accounts.cf | wc -l)" >> "${FIC_COLLECTE}" + +#nombre d'alias kaz: +echo "$(date +%Y-%m-%d-%H-%M-%S);" \ + "mail_alias;" \ + "$(cat ${KAZ_COMP_DIR}/postfix/config/postfix-virtual.cf | wc -l)" >> "${FIC_COLLECTE}" +#Nombre d' orgas +echo "$(date +%Y-%m-%d-%H-%M-%S);" \ + "Orgas;" \ + "$(ls -l /kaz/dockers/ | grep orga | wc -l)" >> "${FIC_COLLECTE}" + +#stats des 2 postfix (mail+sympa) +EXP=$(/usr/bin/hostname -s) + +STATS1=$(cat ${DOCK_VOL}/sympa_sympaLog/_data/mail.log.1 | /usr/sbin/pflogsumm) +#docker exec -i mailServ mailx -r $EXP -s "stats Sympa" root < "${FIC_ACTIVITE_MAILBOX}" +done + +#pour pister les fuites mémoires +docker stats --no-stream --format "table {{.Name}}\t{{.Container}}\t{{.MemUsage}}" | sort -k 3 -h > "${KAZ_STATE_DIR}/metro/$(date +"%Y%m%d")_docker_memory_kaz.log" +ps aux --sort -rss > "${KAZ_STATE_DIR}/metro/$(date +"%Y%m%d")_ps_kaz.log" +free -hlt > "${KAZ_STATE_DIR}/metro/$(date +"%Y%m%d")_mem_kaz.log" +systemd-cgls --no-pager > "${KAZ_STATE_DIR}/metro/$(date +"%Y%m%d")_cgls_kaz.log" +for i in $(docker container ls --format "{{.ID}}"); do docker inspect -f '{{.State.Pid}} {{.Name}}' $i; done > "${KAZ_STATE_DIR}/metro/$(date +"%Y%m%d")_dockerpid_kaz.log" + +#on piste cette saloperie d'ethercalc +#echo $(date +"%Y%m%d") >> "${KAZ_STATE_DIR}/metro/docker_stats_ethercalc.log" +#docker stats --no-stream ethercalcServ ethercalcDB >> "${KAZ_STATE_DIR}/metro/docker_stats_ethercalc.log" + +#fab le 04/10/2022 +#taille des dockers +docker system df -v > "${KAZ_STATE_DIR}/metro/$(date +"%Y%m%d")_docker_size_kaz.log" diff --git a/bin/init.sh b/bin/init.sh new file mode 100755 index 0000000..e0e395e --- /dev/null +++ b/bin/init.sh @@ -0,0 +1,220 @@ +#!/bin/bash + +KAZ_ROOT=$(cd "$(dirname $0)/.."; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +cd "${KAZ_ROOT}" + +MY_MAIN_IP=$(ip a | grep "inet " | head -2 | tail -1 | sed "s%.*inet *\([0-9.]*\)/.*%\1%") +MY_SECOND_IP=$(ip a | grep "inet " | head -3 | tail -1 | sed "s%.*inet *\([0-9.]*\)/.*%\1%") + +DOMAIN="kaz.local" +DOMAIN_SYMPA="kaz.local" +HTTP_PROTO="https" +MAIN_IP="${MY_MAIN_IP}" +SYMPA_IP="MY_SECOND_IP" +RESTART_POLICY="no" +JIRAFEAU_DIR="/var/jirafeauData/$(apg -n 1 -m 16 -M NCL)/" + +DOCKERS_TMPL_ENV="${KAZ_CONF_DIR}/dockers.tmpl.env" + +RESET_ENV="true" +if [ -f "${DOCKERS_ENV}" ]; then + DOMAIN=$(getValInFile "${DOCKERS_ENV}" "domain") + DOMAIN_SYMPA=$(getValInFile "${DOCKERS_ENV}" "domain_sympa") + HTTP_PROTO=$(getValInFile "${DOCKERS_ENV}" "httpProto") + MAIN_IP=$(getValInFile "${DOCKERS_ENV}" "MAIN_IP") + SYMPA_IP=$(getValInFile "${DOCKERS_ENV}" "SYMPA_IP") + RESTART_POLICY=$(getValInFile "${DOCKERS_ENV}" "restartPolicy") + JIRAFEAU_DIR=$(getValInFile "${DOCKERS_ENV}" "jirafeauDir") + while : ; do + read -p "Change '${DOCKERS_ENV}'? " resetEnv + case "${resetEnv}" in + [yYoO]* ) + break + ;; + ""|[Nn]* ) + RESET_ENV="" + break + ;; + * ) + echo "Please answer yes no." + ;; + esac + done +fi + +[ -n "${RESET_ENV}" ] && { + echo "Reset '${DOCKERS_ENV}'" + read -p " * domain (kaz.bzh / dev.kaz.bzh / kaz.local)? [${YELLOW}${DOMAIN}${NC}] " domain + case "${domain}" in + "" ) + DOMAIN="${DOMAIN}" + ;; + * ) + # XXX ne conserver que .-0-9a-z + DOMAIN=$(sed 's/[^a-z0-9.-]//g' <<< "${domain}") + ;; + esac + + read -p " * lists domain (kaz.bzh / kaz2.ovh / kaz.local)? [${YELLOW}${DOMAIN_SYMPA}${NC}] " domain + case "${domain}" in + "" ) + DOMAIN_SYMPA="${DOMAIN_SYMPA}" + ;; + * ) + DOMAIN_SYMPA="${domain}" + ;; + esac + + while : ; do + read -p " * protocol (https / http)? [${YELLOW}${HTTP_PROTO}${NC}] " proto + case "${proto}" in + "" ) + HTTP_PROTO="${HTTP_PROTO}" + break + ;; + "https"|"http" ) + HTTP_PROTO="${proto}" + break + ;; + * ) echo "Please answer joe, emacs, vim or no." + ;; + esac + done + + while : ; do + read -p " * main IP (ip)? [${YELLOW}${MAIN_IP}${NC}] " ip + case "${ip}" in + "" ) + MAIN_IP="${MAIN_IP}" + break + ;; + * ) + if testValidIp "${ip}" ; then + MAIN_IP="${ip}" + break + else + echo "Please answer x.x.x.x format." + fi + ;; + esac + done + + while : ; do + read -p " * lists IP (ip)? [${YELLOW}${SYMPA_IP}${NC}] " ip + case "${ip}" in + "" ) + SYMPA_IP="${SYMPA_IP}" + break + ;; + * ) + if testValidIp "${ip}" ; then + SYMPA_IP="${ip}" + break + else + echo "Please answer x.x.x.x format." + fi + ;; + esac + done + + while : ; do + read -p " * restart policy (always / unless-stopped / no)? [${YELLOW}${RESTART_POLICY}${NC}] " policy + case "${policy}" in + "" ) + RESTART_POLICY="${RESTART_POLICY}" + break + ;; + "always"|"unless-stopped"|"no") + RESTART_POLICY="${policy}" + break + ;; + * ) echo "Please answer always, unless-stopped or no." + ;; + esac + done + + while : ; do + read -p " * Jirafeau dir? [${YELLOW}${JIRAFEAU_DIR}${NC}] " jirafeauDir + case "${jirafeauDir}" in + "" ) + JIRAFEAU_DIR="${JIRAFEAU_DIR}" + break + ;; + * ) + if [[ "${jirafeauDir}" =~ ^/var/jirafeauData/[0-9A-Za-z]{1,16}/$ ]]; then + JIRAFEAU_DIR="${jirafeauDir}" + break + else + echo "Please give dir name (/var/jirafeauData/[0-9A-Za-z]{1,3}/)." + fi + ;; + esac + done + + [ -f "${DOCKERS_ENV}" ] || cp "${DOCKERS_TMPL_ENV}" "${DOCKERS_ENV}" + + sed -i "${DOCKERS_ENV}" \ + -e "s%^\s*domain\s*=.*$%domain=${DOMAIN}%" \ + -e "s%^\s*domain_sympa\s*=.*$%domain_sympa=${DOMAIN_SYMPA}%" \ + -e "s%^\s*httpProto\s*=.*$%httpProto=${HTTP_PROTO}%" \ + -e "s%^\s*MAIN_IP\s*=.*$%MAIN_IP=${MAIN_IP}%" \ + -e "s%^\s*SYMPA_IP\s*=.*$%SYMPA_IP=${SYMPA_IP}%" \ + -e "s%^\s*restartPolicy\s*=.*$%restartPolicy=${RESTART_POLICY}%" \ + -e "s%^\s*ldapRoot\s*=.*$%ldapRoot=dc=${DOMAIN_SYMPA/\./,dc=}%" \ + -e "s%^\s*jirafeauDir\s*=.*$%jirafeauDir=${JIRAFEAU_DIR}%" +} + +if [ ! -f "${KAZ_CONF_DIR}/container-mail.list" ]; then + cat > "${KAZ_CONF_DIR}/container-mail.list" < "${KAZ_CONF_DIR}/container-orga.list" < "${KAZ_CONF_DIR}/container-proxy.list" < "${KAZ_CONF_DIR}/container-withMail.list" < "${KAZ_CONF_DIR}/container-withoutMail.list" < >(tee ${DebugLog}stdout.log) 2> >(tee ${DebugLog}stderr.log >&2) diff --git a/bin/installDepollueur.sh b/bin/installDepollueur.sh new file mode 100755 index 0000000..e99489f --- /dev/null +++ b/bin/installDepollueur.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +SRC_DEP=https://git.kaz.bzh/KAZ/depollueur.git + +KAZ_ROOT=$(cd "$(dirname $0)/.."; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +if [[ -f "${KAZ_GIT_DIR}/depollueur/test-running" ]]; then + exit +fi + +printKazMsg "\n *** Installation du dépollueur" + +sudo apt-get install -y --fix-missing build-essential make g++ libboost-program-options-dev libboost-system-dev libboost-filesystem-dev libcurl4-gnutls-dev libssl-dev + +mkdir -p "${KAZ_GIT_DIR}" +cd "${KAZ_GIT_DIR}" +if [ ! -d "depollueur" ]; then + git clone "${SRC_DEP}" +fi +cd depollueur +git reset --hard && git pull +make + +. "${DOCKERS_ENV}" +echo "${domain}" > "src/bash/domainname" diff --git a/bin/interoPaheko.sh b/bin/interoPaheko.sh new file mode 100755 index 0000000..9fdadde --- /dev/null +++ b/bin/interoPaheko.sh @@ -0,0 +1,166 @@ +#!/bin/bash + +KAZ_ROOT=$(cd "$(dirname $0)"/..; pwd) +KAZ_ROOT=/kaz +. $KAZ_ROOT/bin/.commonFunctions.sh +setKazVars + +. $DOCKERS_ENV +. $KAZ_ROOT/secret/SetAllPass.sh + +URL_PAHEKO="$httpProto://${paheko_API_USER}:${paheko_API_PASSWORD}@kaz-paheko.$(echo $domain)" + +PRG=$(basename $0) +RACINE=$(echo $PRG | awk '{print $1}') + +TFILE_INT_PAHEKO_ACTION=$(mktemp /tmp/XXXXXXXX_INT_PAHEKO_ACTION.json) +TFILE_INT_PAHEKO_IDFILE=$(mktemp /tmp/XXXXXXXX_TFILE_INT_PAHEKO_IDFILE.json) +FILE_CREATEUSER="$KAZ_ROOT/tmp/createUser.txt" + +sep=' ' + +#trap "rm -f ${TFILE_INT_PAHEKO_IDFILE} ${TFILE_INT_PAHEKO_ACTION} " 0 1 2 3 15 + +############################################ Fonctions ####################################################### +TEXTE=" +# -- fichier de création des comptes KAZ +# -- +# -- 1 ligne par compte +# -- champs séparés par ;. les espaces en début et en fin sont enlevés +# -- laisser vide si pas de donnée +# -- pas d'espace dans les variables +# -- +# -- ORGA: nom de l'organisation (max 15 car), vide sinon +# -- ADMIN_ORGA: O/N indique si le user est admin de l'orga (va le créer comme admin du NC de l'orga et admin de l'équipe agora) +# -- NC_ORGA: O/N indique si l'orga a demandé un NC +# -- PAHEKO_ORGA: O/N indique si l'orga a demandé un paheko +# -- WP_ORGA: O/N indique si l'orga a demandé un wp +# -- AGORA_ORGA: O/N indique si l'orga a demandé un mattermost +# -- WIKI_ORGA: O/N indique si l'orga a demandé un wiki +# -- NC_BASE: O/N indique si le user doit être inscrit dans le NC de base +# -- GROUPE_NC_BASE: soit null soit le groupe dans le NC de base +# -- EQUIPE_AGORA: soit null soit equipe agora (max 15 car) +# -- QUOTA=(1/10/20/...) en GB +# -- +# NOM ; PRENOM ; EMAIL_SOUHAITE ; EMAIL_SECOURS ; ORGA ; ADMIN_ORGA ; NC_ORGA ; PAHEKO_ORGA ; WP_ORGA ; AGORA_ORGA ; WIKI_ORGA ; NC_BASE ; GROUPE_NC_BASE ; EQUIPE_AGORA ; QUOTA +# +# exemple pour un compte découverte: +# dupont ; jean-louis; jean-louis.dupont@kaz.bzh ; gregomondo@kaz.bzh; ; N; N; N; N; N; N; O; ; ;1 +# +# exemple pour un compte asso de l'orga gogol avec le service dédié NC uniquement + une équipe dans l'agora +# dupont ; jean-louis; jean-louis.dupont@kaz.bzh ; gregomondo@kaz.bzh; gogol ; O; O; N; N; N; N;N;;gogol_team; 10 +" + +Int_paheko_Action() { + # $1 est une action; + ACTION=$1 + OPTION=$2 + # on envoie la requête sur le serveur paheko avec la clause à créer + # problème de gestion de remontée de données dans la table services_users quand le compte a plus de 2 activités + #curl -s ${URL_PAHEKO}/api/sql -d "SELECT * from users cross join services_users on users.id = services_users.id_user where users.action_auto='${ACTION}';" >>${TFILE_INT_PAHEKO_ACTION} + curl -s ${URL_PAHEKO}/api/sql -d "SELECT * from users where action_auto='${ACTION}';" >>${TFILE_INT_PAHEKO_ACTION} + [ ! -z ${TFILE_INT_PAHEKO_ACTION} ] || { echo "probleme de fichier ${TFILE_INT_PAHEKO_ACTION}" ; exit 1;} + REP_ID=$(jq -c '.results[].id ' ${TFILE_INT_PAHEKO_ACTION} 2>/dev/null) + if [ ! -z "${REP_ID}" ] + then + [ "$OPTION" = "silence" ] || echo -e "${RED}Nombre de compte ${ACTION} ${NC}= ${GREEN} $(echo ${REP_ID} | wc -w) ${NC}" + if [ -f "$FILE_CREATEUSER" ] + then + mv $FILE_CREATEUSER $FILE_CREATEUSER.$(date +%d-%m-%Y-%H:%M:%S) + fi + echo "# -------- Fichier généré le $(date +%d-%m-%Y-%H:%M:%S) ----------">${FILE_CREATEUSER} + echo "${TEXTE}" >>${FILE_CREATEUSER} + for VAL_ID in ${REP_ID} + do + jq -c --argjson val "${VAL_ID}" '.results[] | select (.id == $val)' ${TFILE_INT_PAHEKO_ACTION} > ${TFILE_INT_PAHEKO_IDFILE} + for VAL_GAR in id_category action_auto nom email email_secours quota_disque admin_orga nom_orga responsable_organisation responsable_email agora cloud wordpress garradin docuwiki id_service + do + eval $VAL_GAR=$(jq .$VAL_GAR ${TFILE_INT_PAHEKO_IDFILE}) + done + #comme tout va bien on continue + #on compte le nom de champs dans la zone nom pour gérer les noms et prénoms composés + # si il y a 3 champs, on associe les 2 premieres valeurs avec un - et on laisse le 3ème identique + # si il y a 4 champs on associe les 1 et le 2 avec un tiret et le 3 et 4 avec un tiret + # on met les champs nom_ok et prenom_ok à blanc + nom_ok="" + prenom_ok="" + # on regarde si le nom de l' orga est renseigné ou si le nom de l' orga est null et l' activité de membre est 7 (membre rattaché) + # si c' est le cas alors le nom est le nom de l' orga et le prénom est forcé à la valeur Organisation + if [[ "$nom_orga" = null ]] || [[ "$nom_orga" != null && "$id_service" = "7" ]] + then + [ "$OPTION" = "silence" ] || echo -e "${NC}Abonné ${GREEN}${nom}${NC}" + #si lactivité est membre rattaché on affiche a quelle orga il est rattaché + if [ "$id_service" = "7" ] && [ "$OPTION" != "silence" ] && [ "$nom_orga" != null ] + then + echo -e "${NC}Orga Rattachée : ${GREEN}${nom_orga}${NC}" + fi + COMPTE_NOM=$(echo $nom | awk -F' ' '{for (i=1; i != NF; i++); print i;}') + case "${COMPTE_NOM}" in + 0|1) + echo "Il faut corriger le champ nom (il manque un nom ou prénom) de paheko" + echo "je quitte et supprime le fichier ${FILE_CREATEUSER}" + rm -f $FILE_CREATEUSER + exit 2 + ;; + 2) + nom_ok=$(echo $nom | awk -F' ' '{print $1}') + prenom_ok=$(echo $nom | awk -F' ' '{print $2}') + ;; + *) + nom_ok= + prenom_ok= + for i in ${nom}; do grep -q '^[A-Z]*$' <<<"${i}" && nom_ok="${nom_ok}${sep}${i}" || prenom_ok="${prenom_ok}${sep}${i}"; done + nom_ok="${nom_ok#${sep}}" + prenom_ok="${prenom_ok#${sep}}" + if [ -z "${nom_ok}" ] || [ -z "${prenom_ok}" ]; then + echo "Il faut corriger le champ nom (peut être un nom de famille avec une particule ?) de paheko" + echo "je quitte et supprime le fichier ${FILE_CREATEUSER}" + rm -f $FILE_CREATEUSER + exit + fi + esac + # comme l' orga est à null nom orga est a vide, pas d' admin orga, on met dans l' agora générale + # pas d' équipe agora et de groupe nextcloud spécifique + + nom_orga=" " + admin_orga="N" + nc_base="O" + equipe_agora=" " + groupe_nc_base=" " + else + # L' orga est renseigné dans paheko donc les nom et prenoms sont forcé a nom_orga et Organisation + # un équipe agora portera le nom de l' orga, le compte ne sera pas créé dans le nextcloud général + # et le compte est admin de son orga + nom_orga=$(echo $nom_orga | tr [:upper:] [:lower:]) + [ "$OPTION" = "silence" ] || echo -e "${NC}Orga : ${GREEN}${nom_orga}${NC}" + nom_ok=$nom_orga + # test des caractères autorisés dans le nom d' orga: lettres, chiffres et/ou le tiret + if ! [[ "${nom_ok}" =~ ^[[:alnum:]-]+$ ]]; then + echo "Erreur : l' orga doit être avec des lettres et/ou des chiffres. Le séparateur doit être le tiret" + rm -f $FILE_CREATEUSER� + exit 2 + fi + prenom_ok=organisation + equipe_agora=$nom_orga + groupe_nc_base=" " + nc_base="N" + admin_orga="O" + fi + # Pour le reste on renomme les null en N ( non ) et les valeurs 1 en O ( Oui) + cloud=$(echo $cloud | sed -e 's/0/N/g' | sed -e 's/1/O/g') + paheko=$(echo $garradin | sed -e 's/0/N/g' | sed -e 's/1/O/g') + wordpress=$(echo $wordpress | sed -e 's/0/N/g' | sed -e 's/1/O/g') + agora=$(echo $agora | sed -e 's/0/N/g' | sed -e 's/1/O/g') + docuwiki=$(echo $docuwiki | sed -e 's/0/N/g' | sed -e 's/1/O/g') + # et enfin on écrit dans le fichier + echo "$nom_ok;$prenom_ok;$email;$email_secours;$nom_orga;$admin_orga;$cloud;$paheko;$wordpress;$agora;$docuwiki;$nc_base;$groupe_nc_base;$equipe_agora;$quota_disque">>${FILE_CREATEUSER} + done + else + echo "Rien à créer" + exit 2 + fi +} +#Int_paheko_Action "A créer" "silence" +Int_paheko_Action "A créer" +exit 0 + diff --git a/bin/iptables.sh b/bin/iptables.sh new file mode 100755 index 0000000..9ad9dfa --- /dev/null +++ b/bin/iptables.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +#cleaning, may throw errors at first launch +#iptables -t nat -D POSTROUTING -o ens18 -j ipbis +#iptables -t nat -F ipbis +#iptables -t nat -X ipbis + +iptables -t nat -N ipbis +iptables -t nat -F ipbis +iptables -t nat -I ipbis -o ens18 -p tcp --source `docker inspect -f '{{.NetworkSettings.Networks.sympaNet.IPAddress}}' sympaServ` -j SNAT --to `ifconfig ens18:0 | grep "inet" | awk '{print $2}'` +iptables -t nat -I ipbis -o ens18 -p tcp --source `docker inspect -f '{{.NetworkSettings.Networks.jirafeauNet.IPAddress}}' sympaServ` -j SNAT --to `ifconfig ens18:0 | grep "inet" | awk '{print $2}'` +iptables -t nat -A ipbis -j RETURN +iptables -t nat -D POSTROUTING -o ens18 -j ipbis +iptables -t nat -I POSTROUTING -o ens18 -j ipbis diff --git a/bin/kazDockerNet.sh b/bin/kazDockerNet.sh new file mode 100755 index 0000000..c41c565 --- /dev/null +++ b/bin/kazDockerNet.sh @@ -0,0 +1,110 @@ +#!/bin/bash + +# faire un completion avec les composant dispo + +PRG=$(basename $0) + +KAZ_ROOT=$(cd "$(dirname $0)/.."; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +usage () { + echo "Usage: ${PRG} [-n] [-h] list|add [netName]..." + echo " -n : simulation" + echo " -h|--help : help" + echo + echo " create all net : ${PRG} add $(${KAZ_BIN_DIR}/kazList.sh compose validate)" + exit 1 +} + +allNetName="" +export CMD="" +for ARG in $@; do + case "${ARG}" in + '-h' | '-help' ) + usage + ;; + '-n' ) + shift + export SIMU="echo" + ;; + -*) + usage + ;; + list|add) + CMD="${ARG}" + shift; + ;; + *) + allNetName="${allNetName} ${ARG}" + shift + ;; + esac +done + +if [ -z "${CMD}" ] ; then + usage +fi + +# running composes +export allBridgeName="$(docker network list | grep bridge | awk '{print $2}')" +# running network +export allBridgeNet=$(for net in ${allBridgeName} ; do docker inspect ${net} | grep Subnet | sed 's#.*"\([0-9]*\.[0-9]*\.[0-9]*\.[0-9]*/[0-9]*\)".*# \1#'; done) + +minB=0 +minC=0 +minD=0 + +getNet() { + netName="$1Net" + + if [[ "${allBridgeName}" =~ "${netName}" ]]; then + echo "${netName} already created" + return + fi + # echo "start 10.${minB}.${minC}.$((${minD}*16))" + + find="" + for b in $(eval echo {${minB}..255}); do + for c in $(eval echo {${minC}..255}); do + for d in $(eval echo {${minD}..15}); do + if [ ! -z "${find}" ]; then + minB=${b} + minC=${c} + minD=${d} + return + fi + # to try + subnet="10.${b}.${c}.$((d*16))" + if [[ "${allBridgeNet}" =~ " ${subnet}/" ]]; + then + # used + # XXX check netmask + continue + fi + # the winner is... + echo "${netName} => ${subnet}/28" + ${SIMU} docker network create --subnet "${subnet}/28" "${netName}" + find="ok" + done + minD=0 + done + minC=0 + done +} + +list () { + echo "name: " ${allBridgeName} + echo "net: " ${allBridgeNet} +} + +add () { + if [ -z "${allNetName}" ] ; then + usage + fi + for netName in ${allNetName}; do + getNet "${netName}" + done +} + +"${CMD}" diff --git a/bin/kazList.sh b/bin/kazList.sh new file mode 100755 index 0000000..2816f2b --- /dev/null +++ b/bin/kazList.sh @@ -0,0 +1,181 @@ +#!/bin/bash + +PRG=$(basename $0) +SIMU="" +KAZ_ROOT=$(cd "$(dirname $0)"/..; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars +. "${DOCKERS_ENV}" + +cd "$(dirname $0)" + +ALL_STATUS=$(docker ps -a --format "{{.ID}} {{.Names}} {{.Status}}") +SERVICES_CHOICE="$(getAvailableServices | tr "\n" "|")" +SERVICES_CHOICE="${SERVICES_CHOICE%|}" + +usage () { + echo "${RED}${BOLD}" \ + "Usage: $0 [-h] {compose|orga|service} {available|validate|enable|disable|status} [names]...${NL}" \ + " -h help${NL}" \ + " compose {available|validate|enable|disable} : list docker-compose name${NL}" \ + " compose status : status of docker-compose (default available)${NL}" \ + " service {available|validate} : list services name${NL}" \ + " service {enable|disable} : list services in orga${NL}" \ + " service status : status of services in orga${NL}" \ + " service {${SERVICES_CHOICE}}${NL}" \ + " : list of orga enable a service ${NL}" \ + " [compose] in${NL}" \ + " ${CYAN}$((getAvailableComposes;getAvailableOrgas) | tr "\n" " ")${NC}${NL}" + exit 1 +} + +# ======================================== +compose_available () { + echo $* +} + +getComposeEnableByProxy () { + onList=$( + for type in ${KAZ_CONF_DIR}/container-*.list ; do + getList "${type}" + done) + local compose + for compose in ${onList} ; do + composeFlag="proxy_${compose//-/_}" + [[ "${!composeFlag}" == "on" ]] && echo ${compose} + done +} + +compose_validate () { + echo $( + for type in ${KAZ_CONF_DIR}/container-*.list ; do + getList "${type}" + done | filterInList $*) +} + +compose_enable () { + echo $(getComposeEnableByProxy | filterInList $*) +} + +compose_disable () { + echo $(getAvailableComposes | filterNotInList $(getComposeEnableByProxy) | filterInList $*) +} + +compose_status () { + for compose in $*; do + cd "${KAZ_COMP_DIR}/${compose}" + echo "${compose}:" + for service in $(docker-compose ps --services 2>/dev/null); do + id=$(docker-compose ps -q "${service}" | cut -c 1-12) + if [ -z "${id}" ]; then + echo " - ${RED}${BOLD}[Down]${NC} ${service}" + else + status=$(grep "^${id}\b" <<< "${ALL_STATUS}" | sed "s/.*${id}\s\s*\S*\s\s*\(\S*.*\)/\1/") + COLOR=$([[ "${status}" =~ Up ]] && echo "${GREEN}" || echo "${RED}") + echo " - ${COLOR}${BOLD}[${status}]${NC} ${service}" + fi + done + done +} + +# ======================================== +service_available () { + echo $(getAvailableServices) +} + +service_validate () { + echo $(getAvailableServices) +} + +getServiceInOrga () { + for orga in $*; do + [[ "${orga}" = *"-orga" ]] || continue + local ORGA_DIR="${KAZ_COMP_DIR}/${orga}" + ORGA_COMPOSE="${ORGA_DIR}/docker-compose.yml" + [[ -f "${ORGA_COMPOSE}" ]] || continue + for service in $(getAvailableServices); do + case "${service}" in + paheko) + [ -f "${ORGA_DIR}/usePaheko" ] && echo "${service}" + ;; + wiki) + grep -q "\s*dokuwiki:" "${ORGA_COMPOSE}" 2>/dev/null && echo "${service}" + ;; + wp) + grep -q "\s*wordpress:" "${ORGA_COMPOSE}" 2>/dev/null && echo "${service}" + ;; + *) + grep -q "\s*${service}:" "${ORGA_COMPOSE}" 2>/dev/null && echo "${service}" + esac + done + done +} + +getOrgaWithService() { + service="$1" + shift + case "${service}" in + wiki) keyword="dokuwiki" ;; + wp) keyword="wordpress" ;; + *) keyword="${service}" ;; + esac + for orga in $*; do + [[ "${orga}" = *"-orga" ]] || continue + local ORGA_DIR="${KAZ_COMP_DIR}/${orga}" + ORGA_COMPOSE="${ORGA_DIR}/docker-compose.yml" + [[ -f "${ORGA_COMPOSE}" ]] || continue + if [ "${service}" = "paheko" ]; then + [ -f "${ORGA_DIR}/usePaheko" ] && echo "${orga}" + else + grep -q "\s*${keyword}:" "${ORGA_COMPOSE}" 2>/dev/null && echo "${orga}" + fi + done +} + +service_enable () { + echo $(getServiceInOrga $* | sort -u) +} + +service_disable () { + echo $(getAvailableServices | filterNotInList $(getServiceInOrga $*)) +} + +service_status () { + # ps per enable + echo "*** TODO ***" +} + +# ======================================== + +KAZ_CMD="" +case "$1" in + '-h' | '-help' ) + usage + ;; + compose|service) + KAZ_CMD="$1" + shift + ;; + *) + usage + ;; +esac + +KAZ_OPT="" +case "$1" in + available|validate|enable|disable|status) + KAZ_OPT="$1" + shift + ;; + *) + if [ "${KAZ_CMD}" = "service" ] && [[ $1 =~ ^(${SERVICES_CHOICE})$ ]]; then + KAZ_OPT="$1" + shift + getOrgaWithService "${KAZ_OPT}" $(filterAvailableComposes $*) + exit + fi + usage + ;; +esac + +${KAZ_CMD}_${KAZ_OPT} $(filterAvailableComposes $*) diff --git a/bin/ldap/ldap_sauve.sh b/bin/ldap/ldap_sauve.sh new file mode 100755 index 0000000..ca138be --- /dev/null +++ b/bin/ldap/ldap_sauve.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +KAZ_ROOT=/kaz +. $KAZ_ROOT/bin/.commonFunctions.sh +setKazVars + +FILE_LDIF=/home/sauve/ldap.ldif + +. $DOCKERS_ENV +. $KAZ_ROOT/secret/SetAllPass.sh + +docker exec -u 0 -i ${ldapServName} slapcat -F /opt/bitnami/openldap/etc/slapd.d -b ${ldap_root} | gzip >${FILE_LDIF}.gz diff --git a/bin/ldap/ldapvi.sh b/bin/ldap/ldapvi.sh new file mode 100755 index 0000000..d557b0b --- /dev/null +++ b/bin/ldap/ldapvi.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +KAZ_ROOT=/kaz +. $KAZ_ROOT/bin/.commonFunctions.sh +setKazVars + +. $DOCKERS_ENV +. $KAZ_ROOT/secret/SetAllPass.sh + +LDAP_IP=$(docker inspect -f '{{.NetworkSettings.Networks.ldapNet.IPAddress}}' ldapServ) + +read -p "quel éditeur ? [vi] " EDITOR +EDITOR=${EDITOR:-vi} + +# if [ ${EDITOR} = 'emacs' ]; then +# echo "ALERTE ALERTE !!! quelqu'un a voulu utiliser emacs :) :) :)" +# exit +# fi + +EDITOR=${EDITOR:-vi} +export EDITOR=${EDITOR} + +ldapvi -h $LDAP_IP -D "cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}" -w ${ldap_LDAP_ADMIN_PASSWORD} --discover diff --git a/bin/ldap/migrate_to_ldap.sh b/bin/ldap/migrate_to_ldap.sh new file mode 100755 index 0000000..ff339e7 --- /dev/null +++ b/bin/ldap/migrate_to_ldap.sh @@ -0,0 +1,222 @@ +#!/bin/bash + +echo "ATTENTION ! Il ne faut plus utiliser ce script, il est probable qu'il commence à mettre la grouille avec le LDAP qui vit sa vie..." +exit 1 + +KAZ_ROOT=/kaz +. $KAZ_ROOT/bin/.commonFunctions.sh +setKazVars + +. $DOCKERS_ENV +. $KAZ_ROOT/secret/SetAllPass.sh + +ACCOUNTS=/kaz/dockers/postfix/config/postfix-accounts.cf + +LDAP_IP=$(docker inspect -f '{{.NetworkSettings.Networks.ldapNet.IPAddress}}' ldapServ) +URL_GARRADIN="$httpProto://${paheko_API_USER}:${paheko_API_PASSWORD}@kaz-paheko.$(echo $domain)" +# docker exec -i nextcloudDB mysql --user=${nextcloud_MYSQL_USER} --password=${nextcloud_MYSQL_PASSWORD} ${nextcloud_MYSQL_DATABASE} <<< "select * from oc_accounts;" > /tmp/oc_accounts + +ERRORS="/tmp/ldap-errors.log" +> ${ERRORS} + +mkdir -p /tmp/ldap/ + +# curl -s ${URL_GARRADIN}/api/sql -d "SELECT * from membres where emails_rattaches LIKE '%mailrattache%';" + +for line in `cat ${ACCOUNTS}` +do + mail=$(echo $line | awk -F '|' '{print $1}') + user=$(echo $mail | awk -F '@' '{print $1}') + domain=$(echo $mail | awk -F '@' '{print $2}') + pass=$(echo $line | awk -F '|' '{print $2}' | sed -e "s/SHA512-//") + IDENT_KAZ= + if [ ${mode} = "prod" ]; then + ficheGarradin=$(curl -s ${URL_GARRADIN}/api/sql -d "SELECT * from membres where email='${mail}';") + mailDeSecours=$(echo ${ficheGarradin} | jq .results[0].email_secours | sed -e "s/\"//g") + quota=$(echo ${ficheGarradin} | jq .results[0].quota_disque | sed -e "s/\"//g") + nom=$(echo ${ficheGarradin} | jq .results[0].nom | sed -e "s/\"//g") + nom_orga=$(echo ${ficheGarradin} | jq .results[0].nom_orga | sed -e "s/\"//g") + else + mailDeSecours=${mail} + quota=1 + nom=${mail} + nom_orga="null" + fi + + if [ "${quota}" = "null" ]; then + quota=1 + fi + + # nextcloudEnabled=MAYBE + # IDENT_KAZ=$(grep -i \"${mail}\" /tmp/oc_accounts | cut -f1) + # + # if [ ! -z "${IDENT_KAZ}" ]; then # ident Kaz trouvé avec le mail Kaz + # nextcloudEnabled=TRUE + # else # pas trouvé avec le mail Kaz + # if [ "${nom_orga}" != "null" ]; then # c'est une orga, pas de NC + # IDENT_KAZ="null" + # nextcloudEnabled=FALSE + # else # pas trouvé avec le mail Kaz, pas une orga, on retente avec le mail de secours + # IDENT_KAZ=$(grep -i \"${mailDeSecours}\" /tmp/oc_accounts | cut -f1 | head -n1) + # if [ ! -z "${IDENT_KAZ}" ]; then # on a trouvé l'ident kaz chez NC avec le mail de secours + # nextcloudEnabled=TRUE + # else # pas trouvé avec le mail Kaz, pas une orga, pas trouvé avec le mail de secours + # ficheRattache=$(curl -s ${URL_GARRADIN}/api/sql -d "SELECT * from membres where emails_rattaches LIKE '%${mail}%';" | jq ".results | length") + # if [ $ficheRattache != "0" ]; then # c'est un mail rattaché, pas de NC c'est normal + # IDENT_KAZ="null" + # nextcloudEnabled=FALSE + # else # pas trouvé, pas une orga, pas mail rattaché donc souci + # echo "Pas trouvé l'identifiant Kaz nextcloud pour ${mail} / ${mailDeSecours}, on désactive nextcloud pour ce compte" >> ${ERRORS} + # IDENT_KAZ="null" + # nextcloudEnabled=FALSE + # fi + # fi + # fi + # fi + + + echo -e "\n\ndn: cn=${mail},ou=users,${ldap_root}\n\ +changeType: add\n\ +objectClass: inetOrgPerson\n\ +sn: ${nom}\n\ +userPassword: ${pass}\n\ +\n\n\ +dn: cn=${mail},ou=users,${ldap_root}\n\ +changeType: modify\n\ +replace: objectClass\n\ +objectClass: inetOrgPerson\n\ +objectClass: kaznaute\n\ +objectClass: PostfixBookMailAccount\n\ +objectClass: nextcloudAccount\n\ +-\n\ +replace: sn\n\ +sn: ${nom}\n\ +-\n\ +replace: mail\n\ +mail: ${mail}\n\ +-\n\ +replace: mailEnabled\n\ +mailEnabled: TRUE\n\ +-\n\ +replace: mailGidNumber\n\ +mailGidNumber: 5000\n\ +-\n\ +replace: mailHomeDirectory\n\ +mailHomeDirectory: /var/mail/${domain}/${user}/\n\ +-\n\ +replace: mailQuota\n\ +mailQuota: ${quota}G\n\ +-\n\ +replace: mailStorageDirectory\n\ +mailStorageDirectory: maildir:/var/mail/${domain}/${user}/\n\ +-\n\ +replace: mailUidNumber\n\ +mailUidNumber: 5000\n\ +-\n\ +replace: nextcloudQuota\n\ +nextcloudQuota: ${quota} GB\n\ +-\n\ +replace: mailDeSecours\n\ +mailDeSecours: ${mailDeSecours}\n\ +-\n\ +replace: quota\n\ +quota: ${quota}\n\ +-\n\ +replace: agoraEnabled\n\ +agoraEnabled: TRUE\n\ +-\n\ +replace: mobilizonEnabled\n\ +mobilizonEnabled: TRUE\n\n" | tee /tmp/ldap/${mail}.ldif | ldapmodify -c -H ldap://${LDAP_IP} -D "cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}" -x -w ${ldap_LDAP_ADMIN_PASSWORD} +done + +#replace: nextcloudEnabled\n\ +#nextcloudEnabled: ${nextcloudEnabled}\n\ +#-\n\ +#replace: identifiantKaz\n\ +#identifiantKaz: ${IDENT_KAZ}\n\ +#-\n\ + +OLDIFS=${IFS} +IFS=$'\n' +# ALIASES est le fichier d'entrée +ALIASES="/kaz/dockers/postfix/config/postfix-virtual.cf" +# ALIASES_WITHLDAP est le fichier de sortie des forwards qu'on ne met pas dans le ldap +ALIASES_WITHLDAP="/kaz/dockers/postfix/config/postfix-virtual-withldap.cf" +# On vide le fichier de sortie avant de commencer +> ${ALIASES_WITHLDAP} + + +for line in `cat ${ALIASES}` +do + echo "Virtual line is $line" + if [ `grep -v "," <<< $line` ] + then + echo "Alias simple" + mail=$(echo $line | awk -F '[[:space:]]*' '{print $2}') + if [ `grep $mail ${ACCOUNTS}` ] + then + echo "Alias vers un mail local, go ldap" + LIST="" + for alias in `grep ${mail} ${ALIASES} | grep -v "," | cut -d' ' -f1` + do + LIST=${LIST}"mailAlias: $alias\n" + done + echo -e "dn: cn=${mail},ou=users,${ldap_root}\n\ +changeType: modify +replace: mailAlias\n\ +$LIST\n\n" | ldapmodify -c -H ldap://${LDAP_IP} -D "cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}" -x -w ${ldap_LDAP_ADMIN_PASSWORD} + else + echo "Alias vers un mail externe, go fichier" + echo $line >> ${ALIASES_WITHLDAP} + echo " + intégration LDAP" + src=$(echo $line | awk -F '[[:space:]]*' '{print $1}') + dst=$(echo $line | awk -F '[[:space:]]*' '{print $2}') + echo -e "\n\ndn: cn=${src},ou=mailForwardings,${ldap_root}\n\ +changeType: add\n\ +objectClass: organizationalRole\n\ +\n\n\ +dn: cn=${src},ou=mailForwardings,${ldap_root}\n\ +changeType: modify\n\ +replace: objectClass\n\ +objectClass: organizationalRole\n\ +objectClass: PostfixBookMailForward\n\ +-\n\ +replace: mailAlias\n\ +mailAlias: ${src}\n\ +-\n\ +replace: mail\n\ +mail: ${dst}\n\n" | ldapmodify -c -H ldap://${LDAP_IP} -D "cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}" -x -w ${ldap_LDAP_ADMIN_PASSWORD} + fi + else + echo "Forward vers plusieurs adresses, on met dans le fichier" + echo $line >> ${ALIASES_WITHLDAP} + echo " + intégration LDAP" + src=$(echo $line | awk -F '[[:space:]]*' '{print $1}') + dst=$(echo $line | awk -F '[[:space:]]*' '{print $2}') + OOLDIFS=${IFS} + IFS="," + LIST="" + for alias in ${dst} + do + LIST=${LIST}"mail: $alias\n" + done + IFS=${OOLDIFS} + echo -e "\n\ndn: cn=${src},ou=mailForwardings,${ldap_root}\n\ +changeType: add\n\ +objectClass: organizationalRole\n\ +\n\n\ +dn: cn=${src},ou=mailForwardings,${ldap_root}\n\ +changeType: modify\n\ +replace: objectClass\n\ +objectClass: organizationalRole\n\ +objectClass: PostfixBookMailForward\n\ +-\n\ +replace: mailAlias\n\ +mailAlias: ${src}\n\ +-\n\ +replace: mail\n\ +${LIST}\n\n" | ldapmodify -c -H ldap://${LDAP_IP} -D "cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}" -x -w ${ldap_LDAP_ADMIN_PASSWORD} + + fi +done +IFS=${OLDIFS} diff --git a/bin/ldap/tests/nc_orphans.sh b/bin/ldap/tests/nc_orphans.sh new file mode 100755 index 0000000..c4e97d6 --- /dev/null +++ b/bin/ldap/tests/nc_orphans.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +KAZ_ROOT=/kaz +. $KAZ_ROOT/bin/.commonFunctions.sh +setKazVars + +. $DOCKERS_ENV +. $KAZ_ROOT/secret/SetAllPass.sh + +LDAP_IP=$(docker inspect -f '{{.NetworkSettings.Networks.ldapNet.IPAddress}}' ldapServ) + +docker exec -i nextcloudDB mysql --user=${nextcloud_MYSQL_USER} --password=${nextcloud_MYSQL_PASSWORD} ${nextcloud_MYSQL_DATABASE} <<< "select uid from oc_users;" > /tmp/nc_users.txt + +OLDIFS=${IFS} +IFS=$'\n' +for line in `cat /tmp/nc_users.txt`; do + result=$(ldapsearch -h $LDAP_IP -D "cn=${ldap_LDAP_ADMIN_USERNAME},${ldap_root}" -w ${ldap_LDAP_ADMIN_PASSWORD} -b $ldap_root -x "(identifiantKaz=${line})" | grep numEntries) + echo "${line} ${result}" | grep -v "numEntries: 1" | grep -v "^uid" +done +IFS=${OLDIFS} diff --git a/bin/look/feminin/kaz-entier.png b/bin/look/feminin/kaz-entier.png new file mode 100644 index 0000000..3956823 Binary files /dev/null and b/bin/look/feminin/kaz-entier.png differ diff --git a/bin/look/feminin/kaz-entier.svg b/bin/look/feminin/kaz-entier.svg new file mode 100644 index 0000000..9f71f15 --- /dev/null +++ b/bin/look/feminin/kaz-entier.svg @@ -0,0 +1,90 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/bin/look/feminin/kaz-signature.png b/bin/look/feminin/kaz-signature.png new file mode 100644 index 0000000..8d027db Binary files /dev/null and b/bin/look/feminin/kaz-signature.png differ diff --git a/bin/look/feminin/kaz-tete.png b/bin/look/feminin/kaz-tete.png new file mode 100644 index 0000000..8bb18f2 Binary files /dev/null and b/bin/look/feminin/kaz-tete.png differ diff --git a/bin/look/feminin/kaz-tete.svg b/bin/look/feminin/kaz-tete.svg new file mode 100644 index 0000000..badcb34 --- /dev/null +++ b/bin/look/feminin/kaz-tete.svg @@ -0,0 +1,89 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/bin/look/feminin/kazdate.png b/bin/look/feminin/kazdate.png new file mode 100644 index 0000000..309ac06 Binary files /dev/null and b/bin/look/feminin/kazdate.png differ diff --git a/bin/look/feminin/kazmel.png b/bin/look/feminin/kazmel.png new file mode 100644 index 0000000..4be006c Binary files /dev/null and b/bin/look/feminin/kazmel.png differ diff --git a/bin/look/greve/kaz-entier.png b/bin/look/greve/kaz-entier.png new file mode 100644 index 0000000..bd9fec0 Binary files /dev/null and b/bin/look/greve/kaz-entier.png differ diff --git a/bin/look/greve/kaz-entier.svg b/bin/look/greve/kaz-entier.svg new file mode 100644 index 0000000..253dcec --- /dev/null +++ b/bin/look/greve/kaz-entier.svg @@ -0,0 +1,100 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + GRÈVE + + + diff --git a/bin/look/greve/kaz-signature.png b/bin/look/greve/kaz-signature.png new file mode 100644 index 0000000..f17cef1 Binary files /dev/null and b/bin/look/greve/kaz-signature.png differ diff --git a/bin/look/greve/kaz-tete.png b/bin/look/greve/kaz-tete.png new file mode 100644 index 0000000..3a45982 Binary files /dev/null and b/bin/look/greve/kaz-tete.png differ diff --git a/bin/look/greve/kaz-tete.svg b/bin/look/greve/kaz-tete.svg new file mode 100644 index 0000000..233b739 --- /dev/null +++ b/bin/look/greve/kaz-tete.svg @@ -0,0 +1,98 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + GRÈVE + + + + diff --git a/bin/look/greve/kazdate.png b/bin/look/greve/kazdate.png new file mode 100644 index 0000000..d2838c9 Binary files /dev/null and b/bin/look/greve/kazdate.png differ diff --git a/bin/look/greve/kazmel.png b/bin/look/greve/kazmel.png new file mode 100644 index 0000000..17795e9 Binary files /dev/null and b/bin/look/greve/kazmel.png differ diff --git a/bin/look/kaz/kaz-entier.png b/bin/look/kaz/kaz-entier.png new file mode 100644 index 0000000..4d07332 Binary files /dev/null and b/bin/look/kaz/kaz-entier.png differ diff --git a/bin/look/kaz/kaz-entier.svg b/bin/look/kaz/kaz-entier.svg new file mode 100644 index 0000000..69f82f3 --- /dev/null +++ b/bin/look/kaz/kaz-entier.svg @@ -0,0 +1,79 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/bin/look/kaz/kaz-signature.png b/bin/look/kaz/kaz-signature.png new file mode 100644 index 0000000..a1eccb6 Binary files /dev/null and b/bin/look/kaz/kaz-signature.png differ diff --git a/bin/look/kaz/kaz-tete.png b/bin/look/kaz/kaz-tete.png new file mode 100644 index 0000000..4eb6452 Binary files /dev/null and b/bin/look/kaz/kaz-tete.png differ diff --git a/bin/look/kaz/kaz-tete.svg b/bin/look/kaz/kaz-tete.svg new file mode 100644 index 0000000..81d82ce --- /dev/null +++ b/bin/look/kaz/kaz-tete.svg @@ -0,0 +1,66 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/bin/look/kaz/kazdate.png b/bin/look/kaz/kazdate.png new file mode 100644 index 0000000..c6c482b Binary files /dev/null and b/bin/look/kaz/kazdate.png differ diff --git a/bin/look/kaz/kazmel.png b/bin/look/kaz/kazmel.png new file mode 100644 index 0000000..17d49e3 Binary files /dev/null and b/bin/look/kaz/kazmel.png differ diff --git a/bin/look/noel/kaz-entier.png b/bin/look/noel/kaz-entier.png new file mode 100644 index 0000000..026f5e2 Binary files /dev/null and b/bin/look/noel/kaz-entier.png differ diff --git a/bin/look/noel/kaz-entier.svg b/bin/look/noel/kaz-entier.svg new file mode 100644 index 0000000..6b90a52 --- /dev/null +++ b/bin/look/noel/kaz-entier.svg @@ -0,0 +1,87 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/bin/look/noel/kaz-signature.png b/bin/look/noel/kaz-signature.png new file mode 100644 index 0000000..eb21a01 Binary files /dev/null and b/bin/look/noel/kaz-signature.png differ diff --git a/bin/look/noel/kaz-tete.png b/bin/look/noel/kaz-tete.png new file mode 100644 index 0000000..43de43a Binary files /dev/null and b/bin/look/noel/kaz-tete.png differ diff --git a/bin/look/noel/kaz-tete.svg b/bin/look/noel/kaz-tete.svg new file mode 100644 index 0000000..58e120f --- /dev/null +++ b/bin/look/noel/kaz-tete.svg @@ -0,0 +1,75 @@ + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/bin/look/noel/kazdate.png b/bin/look/noel/kazdate.png new file mode 100644 index 0000000..19b7439 Binary files /dev/null and b/bin/look/noel/kazdate.png differ diff --git a/bin/look/noel/kazmel.png b/bin/look/noel/kazmel.png new file mode 100644 index 0000000..f2891f3 Binary files /dev/null and b/bin/look/noel/kazmel.png differ diff --git a/bin/manageAgora.sh b/bin/manageAgora.sh new file mode 100755 index 0000000..5505802 --- /dev/null +++ b/bin/manageAgora.sh @@ -0,0 +1,180 @@ +#!/bin/bash +# Script de manipulation d'un mattermost' +# init /versions / restart ... +# + +KAZ_ROOT=$(cd "$(dirname $0)"/..; pwd) +. $KAZ_ROOT/bin/.commonFunctions.sh +setKazVars +. $DOCKERS_ENV +. $KAZ_ROOT/secret/SetAllPass.sh + +#GLOBAL VARS +PRG=$(basename $0) + +availableOrga=($(getList "${KAZ_CONF_DIR}/container-orga.list")) +AVAILABLE_ORGAS=${availableOrga[*]//-orga/} + +QUIET="1" +ONNAS= + +AGORACOMMUN="OUI_PAR_DEFAUT" +DockerServName=${mattermostServName} + +declare -A Posts + +usage() { +echo "${PRG} [OPTION] [COMMANDES] [ORGA] +Manipulation d'un mattermost + +OPTIONS + -h|--help Cette aide :-) + -n|--simu SIMULATION + -q|--quiet On ne parle pas (utile avec le -n pour avoir que les commandes) + --nas L'orga se trouve sur le NAS ! + +COMMANDES (on peut en mettre plusieurs dans l'ordre souhaité) + -I|--install L'initialisation du mattermost + -v|--version Donne la version du mattermost et signale les MàJ + + + -mmctl \"command\" Envoie une commande via mmctl ** SPECIFIQUES ** + -p|--post \"team\" \"message\" Poste un message dans une team agora ** AGORA ** + +ORGA parmi : ${AVAILABLE_ORGAS} + ou vide si mattermost commun +" +} + + +Init(){ + NOM=$ORGA + if [ -n "$AGORACOMMUN" ] ; then NOM="KAZ" ; fi + CONF_FILE="${DOCK_VOL}/orga_${ORGA}-matterConfig/_data/config.json" + if [ -n "${AGORACOMMUN}" ]; then + CONF_FILE="${DOCK_VOL}/mattermost_matterConfig/_data/config.json" + elif [ -n "${ONNAS}" ]; then + CONF_FILE="${NAS_VOL}/orga_${ORGA}-matterConfig/_data/config.json" + fi + + + ${SIMU} sed -i \ + -e 's|"SiteURL": ".*"|"SiteURL": "'${MATTER_URL}'"|g' \ + -e 's|"ListenAddress": ".*"|"ListenAddress": ":'${matterPort}'"|g' \ + -e 's|"WebsocketURL": ".*"|"WebsocketURL": "wss://'${MATTER_URI}'"|g' \ + -e 's|"AllowCorsFrom": ".*"|"AllowCorsFrom": "'${domain}' '${MATTER_URI}':443 '${MATTER_URI}'"|g' \ + -e 's|"ConsoleLevel": ".*"|"ConsoleLevel": "ERROR"|g' \ + -e 's|"SendEmailNotifications": false|"SendEmailNotifications": true|g' \ + -e 's|"FeedbackEmail": ".*"|"FeedbackEmail": "admin@'${domain}'"|g' \ + -e 's|"FeedbackOrganization": ".*"|"FeedbackOrganization": "Cochez la KAZ du libre !"|g' \ + -e 's|"ReplyToAddress": ".*"|"ReplyToAddress": "admin@'${domain}'"|g' \ + -e 's|"SMTPServer": ".*"|"SMTPServer": "mail.'${domain}'"|g' \ + -e 's|"SMTPPort": ".*"|"SMTPPort": "25"|g' \ + -e 's|"DefaultServerLocale": ".*"|"DefaultServerLocale": "fr"|g' \ + -e 's|"DefaultClientLocale": ".*"|"DefaultClientLocale": "fr"|g' \ + -e 's|"AvailableLocales": ".*"|"AvailableLocales": "fr"|g' \ + ${CONF_FILE} + + # on redémarre pour prendre en compte (changement de port) + ${SIMU} docker restart "${DockerServName}" + [ $? -ne 0 ] && printKazError "$DockerServName est down : impossible de terminer l'install" && return 1 >& $QUIET + + ${SIMU} waitUrl "$MATTER_URL" 300 + [ $? -ne 0 ] && printKazError "$DockerServName ne parvient pas à démarrer correctement : impossible de terminer l'install" && return 1 >& $QUIET + + # creation compte admin + ${SIMU} curl -i -d "{\"email\":\"${mattermost_MM_ADMIN_EMAIL}\",\"username\":\"${mattermost_user}\",\"password\":\"${mattermost_pass}\",\"allow_marketing\":true}" "${MATTER_URL}/api/v4/users" + + MM_TOKEN=$(_getMMToken ${MATTER_URL}) + + #on crée la team + ${SIMU} curl -i -H "Authorization: Bearer ${MM_TOKEN}" -d "{\"display_name\":\"${NOM}\",\"name\":\"${NOM,,}\",\"type\":\"O\"}" "${MATTER_URL}/api/v4/teams" +} + +Version(){ + VERSION=$(docker exec "$DockerServName" bin/mmctl version | grep -i version:) + echo "Version $DockerServName : ${GREEN}${VERSION}${NC}" +} + +_getMMToken(){ + #$1 MATTER_URL + ${SIMU} curl -i -s -d "{\"login_id\":\"${mattermost_user}\",\"password\":\"${mattermost_pass}\"}" "${1}/api/v4/users/login" | grep 'token' | sed 's/token:\s*\(.*\)\s*/\1/' | tr -d '\r' +} + +PostMessage(){ + printKazMsg "Envoi à $TEAM : $MESSAGE" >& $QUIET + + ${SIMU} docker exec -ti "${DockerServName}" bin/mmctl auth login "${MATTER_URL}" --name local-server --username ${mattermost_user} --password ${mattermost_pass} + ${SIMU} docker exec -ti "${DockerServName}" bin/mmctl post create "${TEAM}" --message "${MESSAGE}" +} + +MmctlCommand(){ + # $1 command + ${SIMU} docker exec -u 33 "$DockerServName" bin/mmctl $1 +} + + +########## Main ################# +for ARG in "$@"; do + if [ -n "${GETMMCTLCOMAND}" ]; then # après un -mmctl + MMCTLCOMAND="${ARG}" + GETMMCTLCOMAND= + elif [ -n "${GETTEAM}" ]; then # après un --post + GETMESSAGE="now" + GETTEAM="" + TEAM="${ARG}" + elif [ -n "${GETMESSAGE}" ]; then # après un --post "team:channel" + if [[ $TEAM == "-*" && ${#TEAM} -le 5 ]]; then echo "J'envoie mon message à \"${TEAM}\" ?? Arf, ça me plait pas j'ai l'impression que tu t'es planté sur la commande."; usage ; exit 1 ; fi + if [[ $ARG == "-*" && ${#ARG} -le 5 ]]; then echo "J'envoie le message \"${ARG}\" ?? Arf, ça me plait pas j'ai l'impression que tu t'es planté sur la commande."; usage ; exit 1 ; fi + if [[ ! $TEAM =~ .*:.+ ]]; then echo "Il faut mettre un destinataire sous la forme team:channel. Recommence !"; usage ; exit 1 ; fi + MESSAGE="$ARG" + GETMESSAGE="" + else + case "${ARG}" in + '-h' | '--help' ) + usage && exit ;; + '-n' | '--simu') + SIMU="echo" ;; + '-q' ) + QUIET="/dev/null" ;; + '--nas' | '-nas' ) + ONNAS="SURNAS" ;; + '-v' | '--version') + COMMANDS="$(echo "${COMMANDS} VERSION" | sed "s/\s/\n/g" | sort | uniq)" ;; + '-I' | '--install' ) + COMMANDS="$(echo "${COMMANDS} INIT" | sed "s/\s/\n/g" | sort | uniq)" ;; # le sed sort uniq, c'est pour pas l'avoir en double + '--mmctl' | '-mmctl' ) + COMMANDS="$(echo "${COMMANDS} RUN-AGORA-MMCTL" | sed "s/\s/\n/g" | sort | uniq)" + GETMMCTLCOMAND="now" ;; + '-p' | '--post' ) + COMMANDS="$(echo "${COMMANDS} POST-AGORA" | sed "s/\s/\n/g" | sort | uniq)" + GETTEAM="now" ;; + '-*' ) # ignore + ;; + *) + ORGA="${ARG%-orga}" + DockerServName="${ORGA}-${mattermostServName}" + AGORACOMMUN= + ;; + esac + fi +done + +if [ -z "${COMMANDS}" ]; then usage && exit ; fi + +MATTER_URI="${ORGA}-${matterHost}.${domain}" +if [ -n "$AGORACOMMUN" ]; then MATTER_URI="${matterHost}.${domain}" ; fi +MATTER_URL="${httpProto}://${MATTER_URI}" + +for COMMAND in ${COMMANDS}; do + case "${COMMAND}" in + 'VERSION' ) + Version && exit ;; + 'INIT' ) + Init ;; + 'RUN-AGORA-MMCTL' ) + MmctlCommand "$MMCTLCOMAND" ;; + 'POST-AGORA' ) + PostMessage ;; + esac +done diff --git a/bin/manageCastopod.sh b/bin/manageCastopod.sh new file mode 100755 index 0000000..a7e0f58 --- /dev/null +++ b/bin/manageCastopod.sh @@ -0,0 +1,117 @@ +#!/bin/bash +# Script de manipulation d'un wordpress' +# init /versions / restart ... +# + +KAZ_ROOT=$(cd "$(dirname $0)"/..; pwd) +. $KAZ_ROOT/bin/.commonFunctions.sh +setKazVars +. $DOCKERS_ENV +. $KAZ_ROOT/secret/SetAllPass.sh + +#GLOBAL VARS +PRG=$(basename $0) + +availableOrga=($(getList "${KAZ_CONF_DIR}/container-orga.list")) +AVAILABLE_ORGAS=${availableOrga[*]//-orga/} + +QUIET="1" +ONNAS= + + +CASTOPOD_COMMUN="OUI_PAR_DEFAUT" +DockerServName=${castopodServName} + +declare -A Posts + +usage() { +echo "${PRG} [OPTION] [COMMANDES] [ORGA] +Manipulation d'un castopod + +OPTIONS + -h|--help Cette aide :-) + -n|--simu SIMULATION + -q|--quiet On ne parle pas (utile avec le -n pour avoir que les commandes) + --nas L'orga se trouve sur le NAS ! + +COMMANDES (on peut en mettre plusieurs dans l'ordre souhaité) + -I|--install L'initialisation du castopod + -v|--version Donne la version du castopod et signale les MàJ + +ORGA parmi : ${AVAILABLE_ORGAS} + ou vide si castopod commun +" +} + + + +Init(){ + + POD_URL="${httpProto}://${ORGA}-${castopodHost}.${domain}" + [ -n "${CASTOPOD_COMMUN}" ] && POD_URL="${httpProto}://${castopodHost}.${domain}" + + if ! [[ "$(docker ps -f name=${DockerServName} | grep -w ${DockerServName})" ]]; then + printKazError "Castopod not running... abort" + exit + fi + + echo "\n *** Premier lancement de Castopod" >& $QUIET + + ${SIMU} waitUrl "${POD_URL}" + CI_SESSION=$(echo ${headers} | grep "ci_session" | sed "s/.*ci_session=//") + + cookies=$(curl -c - ${POD_URL}) + CSRF_TOKEN=$(curl --cookie <(echo "$cookies") ${POD_URL}/cp-install | grep "csrf_test_name" | sed "s/.*value=.//" | sed "s/.>//") + + #echo ${CSRF_TOKEN} + ${SIMU} curl --cookie <(echo "$cookies") -X POST \ + -d "username=${castopod_ADMIN_USER}" \ + -d "password=${castopod_ADMIN_PASSWORD}" \ + -d "email=${castopod_ADMIN_MAIL}" \ + -d "csrf_test_name=${CSRF_TOKEN}" \ + "${POD_URL}/cp-install/create-superadmin" + +} + +Version(){ + VERSION="TODO" + echo "Version $DockerServName : ${GREEN}${VERSION}${NC}" +} + + + +########## Main ################# +for ARG in "$@"; do + case "${ARG}" in + '-h' | '--help' ) + usage && exit ;; + '-n' | '--simu') + SIMU="echo" ;; + '-q' ) + QUIET="/dev/null" ;; + '--nas' | '-nas' ) + ONNAS="SURNAS" ;; + '-v' | '--version') + COMMANDS="$(echo "${COMMANDS} VERSION" | sed "s/\s/\n/g" | sort | uniq)" ;; + '-I' | '--install' ) + COMMANDS="$(echo "${COMMANDS} INIT" | sed "s/\s/\n/g" | sort | uniq)" ;; # le sed sort uniq, c'est pour pas l'avoir en double + '-*' ) # ignore + ;; + *) + ORGA="${ARG%-orga}" + DockerServName="${ORGA}-${castopodServName}" + CASTOPOD_COMMUN= + ;; + esac +done + +if [ -z "${COMMANDS}" ]; then usage && exit ; fi + +for COMMAND in ${COMMANDS}; do + case "${COMMAND}" in + 'VERSION' ) + Version && exit ;; + 'INIT' ) + Init ;; + esac +done diff --git a/bin/manageCloud.sh b/bin/manageCloud.sh new file mode 100755 index 0000000..5a5a263 --- /dev/null +++ b/bin/manageCloud.sh @@ -0,0 +1,393 @@ +#!/bin/bash +# Script de manipulation d'un cloud' +# init /versions / restart ... +# + +KAZ_ROOT=$(cd "$(dirname $0)"/..; pwd) +. $KAZ_ROOT/bin/.commonFunctions.sh +setKazVars +. $DOCKERS_ENV +. $KAZ_ROOT/secret/SetAllPass.sh + +#GLOBAL VARS +PRG=$(basename $0) + +availableOrga=($(getList "${KAZ_CONF_DIR}/container-orga.list")) +AVAILABLE_ORGAS=${availableOrga[*]//-orga/} + +# CLOUD +APPLIS_PAR_DEFAUT="tasks calendar contacts bookmarks mail richdocuments external drawio snappymail ransomware_protection" #rainloop richdocumentscode +QUIET="1" +ONNAS= + +CLOUDCOMMUN="OUI_PAR_DEFAUT" +DockerServName=${nextcloudServName} + +usage() { +echo "${PRG} [OPTION] [COMMANDES] [ORGA] +Manipulation d'un cloud + +OPTIONS + -h|--help Cette aide :-) + -n|--simu SIMULATION + -q|--quiet On ne parle pas (utile avec le -n pour avoir que les commandes) + --nas L'orga se trouve sur le NAS ! + +COMMANDES (on peut en mettre plusieurs dans l'ordre souhaité) + -I|--install L'initialisation du cloud + -v|--version Donne la version du cloud et signale les MàJ + + --optim Lance la procédure Nextcloud pour optimiser les performances ** ** + -occ \"command\" Envoie une commande via occ ** ** + -u Mets à jour les applis ** SPECIFIQUES ** + -i Install des applis ** CLOUD ** + -a \"app1 app2 ...\" Choix des appli à installer ou mettre à jour (entre guillemets) ** ** + -U|--upgrade Upgrade des clouds ** ** + -O|--officeURL MAJ le office de ce nextcloud ** ** + +ORGA parmi : ${AVAILABLE_ORGAS} + ou vide si cloud commun +" +} + + +################################## +############### CLOUD ############ +################################## + +Init(){ + NOM=$ORGA + [ -n "${CLOUDCOMMUN}" ] && NOM="commun" + if [ -z "${LISTE_APPS}" ]; then + printKazMsg "Aucune appli n'est précisée, j'installerais les applis par défaut : ${APPLIS_PAR_DEFAUT}" >& $QUIET + LISTE_APPS="${APPLIS_PAR_DEFAUT}" + fi + + checkDockerRunning "$DockerServName" "$NOM" + [ $? -ne 0 ] && echo "${CYAN}\n $DockerServName est down : impossible de terminer l'install${NC}" && return 1 >& $QUIET + + CONF_FILE="${DOCK_VOL}/orga_${ORGA}-cloudConfig/_data/config.php" + CLOUD_URL="https://${ORGA}-${cloudHost}.${domain}" + if [ -n "$CLOUDCOMMUN" ]; then + CONF_FILE="${DOCK_VOL}/cloud-cloudConfig/_data/config.php" + CLOUD_URL="https://${cloudHost}.${domain}" + elif [ -n "${ONNAS}" ]; then + CONF_FILE="${NAS_VOL}/orga_${ORGA}-cloudConfig/_data/config.php" + fi + + firstInstall "$CLOUD_URL" "$CONF_FILE" " NextCloud de $NOM" + updatePhpConf "$CONF_FILE" + InstallApplis + echo "${CYAN} *** Paramétrage richdocuments pour $ORGA${NC}" >& $QUIET + setOfficeUrl + occCommand "config:app:set --value 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 richdocuments wopi_allowlist" + occCommand "config:system:set overwrite.cli.url --value=$CLOUD_URL" + occCommand "config:system:set disable_certificate_verification --value=true" + + if [ -n "$CLOUDCOMMUN" ]; then initLdap "$NOM" ; fi +} + +Version(){ + VERSION=$(docker exec -u 33 ${DockerServName} /var/www/html/occ status | grep -i version:) + VERSION_UPDATE=$(docker exec -u 33 ${DockerServName} /var/www/html/occ update:check | grep -i "available\." | cut -c 1-17) + versionSTR="Version ${DockerServName} : ${GREEN}${VERSION}${NC}" + [ -n "${VERSION_UPDATE}" ] && versionSTR="$versionSTR -- Disponible : ${RED} ${VERSION_UPDATE} ${NC}" + echo "$versionSTR" +} + +firstInstall(){ + # $1 CLOUD_URL + # $2 phpConfFile + # $3 orga + if ! grep -q "'installed' => true," "$2" 2> /dev/null; then + printKazMsg "\n *** Premier lancement de $3" >& $QUIET + + ${SIMU} waitUrl "$1" + + ${SIMU} curl -X POST \ + -d "install=true" \ + -d "adminlogin=${nextcloud_NEXTCLOUD_ADMIN_USER}" \ + -d "adminpass=${nextcloud_NEXTCLOUD_ADMIN_PASSWORD}" \ + -d "directory=/var/www/html/data" \ + -d "dbtype=mysql" \ + -d "dbuser=${nextcloud_MYSQL_USER}" \ + -d "dbpass=${nextcloud_MYSQL_PASSWORD}" \ + -d "dbname=${nextcloud_MYSQL_DATABASE}" \ + -d "dbhost=${nextcloud_MYSQL_HOST}" \ + -d "install-recommended-apps=true" \ + "$1" + fi +} + +setOfficeUrl(){ + OFFICE_URL="https://${officeHost}.${domain}" + if [ ! "${site}" = "prod1" ]; then + OFFICE_URL="https://${site}-${officeHost}.${domain}" + fi + occCommand "config:app:set --value $OFFICE_URL richdocuments public_wopi_url" + occCommand "config:app:set --value $OFFICE_URL richdocuments wopi_url" + occCommand "config:app:set --value $OFFICE_URL richdocuments disable_certificate_verification" +} + +initLdap(){ + # $1 Nom du cloud + echo "${CYAN} *** Installation LDAP pour $1${NC}" >& $QUIET + occCommand "app:enable user_ldap" "${DockerServName}" + occCommand "ldap:delete-config s01" "${DockerServName}" + occCommand "ldap:create-empty-config" "${DockerServName}" + occCommand "ldap:set-config s01 ldapAgentName cn=cloud,ou=applications,${ldap_root}" "${DockerServName}" + occCommand "ldap:set-config s01 ldapAgentPassword ${ldap_LDAP_CLOUD_PASSWORD}" "${DockerServName}" + occCommand "ldap:set-config s01 ldapBase ${ldap_root}" "${DockerServName}" + occCommand "ldap:set-config s01 ldapBaseGroups ${ldap_root}" "${DockerServName}" + occCommand "ldap:set-config s01 ldapBaseUsers ou=users,${ldap_root}" "${DockerServName}" + occCommand "ldap:set-config s01 ldapExpertUsernameAttr identifiantKaz" "${DockerServName}" + occCommand "ldap:set-config s01 ldapHost ${ldapServName}" "${DockerServName}" + occCommand "ldap:set-config s01 ldapPort 389" "${DockerServName}" + occCommand "ldap:set-config s01 ldapTLS 0" "${DockerServName}" + occCommand "ldap:set-config s01 ldapLoginFilter \"(&(objectclass=nextcloudAccount)(|(cn=%uid)(identifiantKaz=%uid)))\"" "${DockerServName}" + occCommand "ldap:set-config s01 ldapQuotaAttribute nextcloudQuota" "${DockerServName}" + occCommand "ldap:set-config s01 ldapUserFilter \"(&(objectclass=nextcloudAccount)(nextcloudEnabled=TRUE))\"" "${DockerServName}" + occCommand "ldap:set-config s01 ldapUserFilterObjectclass nextcloudAccount" "${DockerServName}" + occCommand "ldap:set-config s01 ldapEmailAttribute mail" "${DockerServName}" + occCommand "ldap:set-config s01 ldapUserDisplayName cn" "${DockerServName}" + occCommand "ldap:set-config s01 ldapUserFilterMode 1" "${DockerServName}" + occCommand "ldap:set-config s01 ldapConfigurationActive 1" "${DockerServName}" + + # Dans le mariadb, pour permettre au ldap de reprendre la main : delete from oc_users where uid<>'admin'; + # docker exec -i nextcloudDB mysql --user= --password= <<< "delete from oc_users where uid<>'admin';" + + # Doc : https://help.nextcloud.com/t/migration-to-ldap-keeping-users-and-data/13205 + + # Exemple de table/clés : + # +-------------------------------+----------------------------------------------------------+ + # | Configuration | s01 | + # +-------------------------------+----------------------------------------------------------+ + # | hasMemberOfFilterSupport | 0 | + # | homeFolderNamingRule | | + # | lastJpegPhotoLookup | 0 | + # | ldapAgentName | cn=cloud,ou=applications,dc=kaz,dc=sns | + # | ldapAgentPassword | *** | + # | ldapAttributesForGroupSearch | | + # | ldapAttributesForUserSearch | | + # | ldapBackgroundHost | | + # | ldapBackgroundPort | | + # | ldapBackupHost | | + # | ldapBackupPort | | + # | ldapBase | ou=users,dc=kaz,dc=sns | + # | ldapBaseGroups | ou=users,dc=kaz,dc=sns | + # | ldapBaseUsers | ou=users,dc=kaz,dc=sns | + # | ldapCacheTTL | 600 | + # | ldapConfigurationActive | 1 | + # | ldapConnectionTimeout | 15 | + # | ldapDefaultPPolicyDN | | + # | ldapDynamicGroupMemberURL | | + # | ldapEmailAttribute | mail | + # | ldapExperiencedAdmin | 0 | + # | ldapExpertUUIDGroupAttr | | + # | ldapExpertUUIDUserAttr | | + # | ldapExpertUsernameAttr | uid | + # | ldapExtStorageHomeAttribute | | + # | ldapGidNumber | gidNumber | + # | ldapGroupDisplayName | cn | + # | ldapGroupFilter | | + # | ldapGroupFilterGroups | | + # | ldapGroupFilterMode | 0 | + # | ldapGroupFilterObjectclass | | + # | ldapGroupMemberAssocAttr | | + # | ldapHost | ldap | + # | ldapIgnoreNamingRules | | + # | ldapLoginFilter | (&(|(objectclass=nextcloudAccount))(cn=%uid)) | + # | ldapLoginFilterAttributes | | + # | ldapLoginFilterEmail | 0 | + # | ldapLoginFilterMode | 0 | + # | ldapLoginFilterUsername | 1 | + # | ldapMatchingRuleInChainState | unknown | + # | ldapNestedGroups | 0 | + # | ldapOverrideMainServer | | + # | ldapPagingSize | 500 | + # | ldapPort | 389 | + # | ldapQuotaAttribute | nextcloudQuota | + # | ldapQuotaDefault | | + # | ldapTLS | 0 | + # | ldapUserAvatarRule | default | + # | ldapUserDisplayName | cn | + # | ldapUserDisplayName2 | | + # | ldapUserFilter | (&(objectclass=nextcloudAccount)(nextcloudEnabled=TRUE)) | + # | ldapUserFilterGroups | | + # | ldapUserFilterMode | 1 | + # | ldapUserFilterObjectclass | nextcloudAccount | + # | ldapUuidGroupAttribute | auto | + # | ldapUuidUserAttribute | auto | + # | turnOffCertCheck | 0 | + # | turnOnPasswordChange | 0 | + # | useMemberOfToDetectMembership | 1 | + # +-------------------------------+----------------------------------------------------------+ +} + +updatePhpConf(){ + # $1 php_conf_file + if [ $# -ne 1 ]; then + echo "${RED}#Je ne sais pas ou écrire la conf php !${NC}" + return 1 + fi + + echo "${CYAN} *** Maj de la conf $1${NC}" >& $QUIET + PHPCONF="$1" + _addVarAfterInConf "default_language" " 'default_language' => 'fr'," "CONFIG = array (" "${PHPCONF}" + _addVarAfterInConf "theme" " 'theme' => ''," "'installed' => true," "${PHPCONF}" + _addVarAfterInConf "default_phone_region" " 'default_phone_region' => 'FR'," "'installed' => true," "${PHPCONF}" + _addVarAfterInConf "loglevel" " 'loglevel' => 2," "'installed' => true," "${PHPCONF}" + _addVarAfterInConf "maintenance" " 'maintenance' => false," "'installed' => true," "${PHPCONF}" + _addVarAfterInConf "app_install_overwrite" " 'app_install_overwrite' => \n array (\n 0 => 'documents',\n )," "'installed' => true," "${PHPCONF}" + _addVarAfterInConf "overwriteprotocol" " 'overwriteprotocol' => 'https'," "'installed' => true," "${PHPCONF}" + _addVarAfterInConf "mail_domain" " 'mail_domain' => '${domain}'," "'installed' => true," "${PHPCONF}" + _addVarAfterInConf "mail_from_address" " 'mail_from_address' => 'admin'," "'installed' => true," "${PHPCONF}" + _addVarAfterInConf "mail_smtpport" " 'mail_smtpport' => '25'," "'installed' => true," "${PHPCONF}" + _addVarAfterInConf "mail_sendmailmode" " 'mail_sendmailmode' => 'smtp'," "'installed' => true," "${PHPCONF}" + _addVarAfterInConf "mail_smtphost" " 'mail_smtphost' => '${smtpHost}.${domain}'," "'installed' => true," "${PHPCONF}" + _addVarAfterInConf "mail_smtpmode" " 'mail_smtpmode' => 'smtp'," "'installed' => true," "${PHPCONF}" + _addVarAfterInConf "enable_previews" " 'enable_previews' => true," "'installed' => true," "${PHPCONF}" + _addVarAfterInConf "trashbin_retention_obligation" " 'trashbin_retention_obligation' => '30, auto'," "'installed' => true," "${PHPCONF}" + #pour supprimer le message "obtenir un compte gratuit" dans le footer + _addVarAfterInConf "simpleSignUpLink.shown" " 'simpleSignUpLink.shown' => false," "'installed' => true," "${PHPCONF}" + _addVarAfterInConf "trusted_proxies" " 'trusted_proxies' => array( 0 => '10.0.0.0/8', 1 => '172.16.0.0/12', 2 => '192.168.0.0/16' )," "'installed' => true," "${PHPCONF}" +} + +UpgradeClouds() { + echo "${NC}--------------------------------------------------------" >& $QUIET + echo "UPGRADE des cloud" >& $QUIET + echo "--------------------------------------------------------" >& $QUIET + occCommand "upgrade" +} + +OptimiseClouds() { + occCommand "db:add-missing-indices" "db:convert-filecache-bigint --no-interaction" +} + + +UpdateApplis() { + printKazMsg "UPDATE DES APPLIS du cloud ${DockerServName} : ${LISTE_APPS}" >& $QUIET + if [ -z "${LISTE_APPS}" ]; then + occCommand "app:update --all" + return + fi + echo "Mise à jour de ${LISTE_APPS}" >& $QUIET + for app in ${LISTE_APPS} + do + occCommand "app:update ${app}" + done +} + +InstallApplis(){ + if [ -z "${LISTE_APPS}" ]; then + printKazMsg "Aucune appli n'est précisée, j'installe les applis par défaut : ${APPLIS_PAR_DEFAUT}" >& $QUIET + LISTE_APPS="${APPLIS_PAR_DEFAUT}" + fi + apps=$LISTE_APPS + + if ! [[ "$(docker ps -f name=${DockerServName} | grep -w ${DockerServName})" ]]; then + printKazError "${RED}# ${DockerServName} not running... impossible d'installer les applis${NC}" >& $QUIET + return 1 + fi + LIST_ALL=$(docker exec -ti -u 33 "${DockerServName}" /var/www/html/occ app:list | + awk 'BEGIN {cp=0} + /Enabled:/ {cp=1 ; next}; + /Disabled:/ {cp=0; next}; + {if (cp) print $0};') + + for app in $apps + do + grep -wq "${app}" <<<"${LIST_ALL}" 2>/dev/null && echo "${app} dejà installée" >& $QUIET && continue + echo " install ${app}" >& $QUIET + occCommand "app:install ${app}" + done +} + +occCommand(){ + # $1 Command + ${SIMU} docker exec -u 33 $DockerServName /var/www/html/occ $1 +} + +_addVarAfterInConf(){ + # $1 key + # $2 val + # $3 where + # $4 confFile + if ! grep -q "$1" "${4}" ; then + echo -n " ${CYAN}${BOLD}$1${NC}" >& $QUIET + ${SIMU} sed -i -e "/$3/a\ $2" "$4" + fi +} + +########## Main ################# +for ARG in "$@"; do + if [ -n "${GETOCCCOMAND}" ]; then # après un -occ + OCCCOMAND="${ARG}" + GETOCCCOMAND= + elif [ -n "${GETAPPS}" ]; then # après un -a + LISTE_APPS="${LISTE_APPS} ${ARG}" + GETAPPS="" + else + case "${ARG}" in + '-h' | '--help' ) + usage && exit ;; + '-n' | '--simu') + SIMU="echo" ;; + '-q' ) + QUIET="/dev/null" ;; + '--nas' | '-nas' ) + ONNAS="SURNAS" ;; + '-v' | '--version') + COMMANDS="$(echo "${COMMANDS} VERSION" | sed "s/\s/\n/g" | sort | uniq)" ;; + '-I' | '--install' ) + COMMANDS="$(echo "${COMMANDS} INIT" | sed "s/\s/\n/g" | sort | uniq)" ;; # le sed sort uniq, c'est pour pas l'avoir en double + '-U' | '--upgrade') + COMMANDS="$(echo "${COMMANDS} UPGRADE" | sed "s/\s/\n/g" | sort | uniq)" ;; + '-O' | '--officeURL') + COMMANDS="$(echo "${COMMANDS} OFFICEURL" | sed "s/\s/\n/g" | sort | uniq)" ;; + '--optim' | '-optim' ) + COMMANDS="$(echo "${COMMANDS} OPTIMISE-CLOUD" | sed "s/\s/\n/g" | sort | uniq)" ;; + '-u' ) + COMMANDS="$(echo "${COMMANDS} UPDATE-CLOUD-APP" | sed "s/\s/\n/g" | sort | uniq)" ;; + '-i' ) + COMMANDS="$(echo "${COMMANDS} INSTALL-CLOUD-APP" | sed "s/\s/\n/g" | sort | uniq)" ;; + '-a' ) + GETAPPS="now" ;; + '--occ' | '-occ' ) + COMMANDS="$(echo "${COMMANDS} RUN-CLOUD-OCC" | sed "s/\s/\n/g" | sort | uniq)" + GETOCCCOMAND="now" ;; + '-*' ) # ignore + ;; + *) + ORGA="${ARG%-orga}" + DockerServName="${ORGA}-${nextcloudServName}" + CLOUDCOMMUN= + ;; + esac + fi +done + +if [ -z "${COMMANDS}" ]; then + usage && exit +fi + +for COMMAND in ${COMMANDS}; do + case "${COMMAND}" in + 'VERSION' ) + Version && exit ;; + 'OPTIMISE-CLOUD' ) + OptimiseClouds ;; + 'UPDATE-CLOUD-APP' ) + UpdateApplis ;; + 'UPGRADE' ) + UpgradeClouds ;; + 'INIT' ) + Init ;; + 'INSTALL-CLOUD-APP' ) + InstallApplis ;; + 'OFFICEURL' ) + setOfficeUrl ;; + 'RUN-CLOUD-OCC' ) + occCommand "${OCCCOMAND}";; + esac +done \ No newline at end of file diff --git a/bin/manageWiki.sh b/bin/manageWiki.sh new file mode 100755 index 0000000..f17ca81 --- /dev/null +++ b/bin/manageWiki.sh @@ -0,0 +1,177 @@ +#!/bin/bash +# Script de manipulation d'un dokuwiki' +# init /versions / restart ... +# + +KAZ_ROOT=$(cd "$(dirname $0)"/..; pwd) +. $KAZ_ROOT/bin/.commonFunctions.sh +setKazVars +. $DOCKERS_ENV +. $KAZ_ROOT/secret/SetAllPass.sh + +#GLOBAL VARS +PRG=$(basename $0) + +availableOrga=($(getList "${KAZ_CONF_DIR}/container-orga.list")) +AVAILABLE_ORGAS=${availableOrga[*]//-orga/} +DNLD_DIR="${KAZ_DNLD_DIR}/dokuwiki" + +QUIET="1" +ONNAS= + +WIKICOMMUN="OUI_PAR_DEFAUT" +DockerServName=${dokuwikiServName} + +declare -A Posts + +usage() { +echo "${PRG} [OPTION] [COMMANDES] [ORGA] +Manipulation d'un dokuwiki + +OPTIONS + -h|--help Cette aide :-) + -n|--simu SIMULATION + -q|--quiet On ne parle pas (utile avec le -n pour avoir que les commandes) + --nas L'orga se trouve sur le NAS ! + +COMMANDES (on peut en mettre plusieurs dans l'ordre souhaité) + -I|--install L'initialisation du dokuwiki + -v|--version Donne la version du dokuwiki et signale les MàJ + + --reload kill lighthttpd + +ORGA parmi : ${AVAILABLE_ORGAS} + ou vide si dokuwiki commun +" +} + + + +Init(){ + + NOM=$ORGA + if [ -n "$WIKICOMMUN" ] ; then NOM="KAZ" ; fi + TPL_DIR="${VOL_PREFIX}wikiLibtpl/_data" + PLG_DIR="${VOL_PREFIX}wikiPlugins/_data" + CONF_DIR="${VOL_PREFIX}wikiConf/_data" + + # Gael, j'avais ajouté ça mais j'ai pas test alors je laisse comme avant ... + # A charge au prochain qui monte un wiki de faire qque chose + #WIKI_ROOT="${dokuwiki_WIKI_ROOT}" + #WIKI_EMAIL="${dokuwiki_WIKI_EMAIL}" + #WIKI_PASS="${dokuwiki_WIKI_PASSWORD}" + + WIKI_ROOT=Kaz + WIKI_EMAIL=wiki@kaz.local + WIKI_PASS=azerty + + ${SIMU} checkDockerRunning "${DockerServName}" "${NOM}" || exit + + if [ ! -f "${CONF_DIR}/local.php" ] ; then + echo "\n *** Premier lancement de Dokuwiki ${NOM}" >& $QUIET + + ${SIMU} waitUrl "${WIKI_URL}" + + ${SIMU} curl -X POST \ + -A "Mozilla/5.0 (X11; Linux x86_64)" \ + -d "l=fr" \ + -d "d[title]=${NOM}" \ + -d "d[acl]=true" \ + -d "d[superuser]=${WIKI_ROOT}" \ + -d "d[fullname]=Admin"\ + -d "d[email]=${WIKI_EMAIL}" \ + -d "d[password]=${WIKI_PASS}" \ + -d "d[confirm]=${WIKI_PASS}" \ + -d "d[policy]=1" \ + -d "d[allowreg]=false" \ + -d "d[license]=0" \ + -d "d[pop]=false" \ + -d "submit=Enregistrer" \ + "${WIKI_URL}/install.php" + + # XXX initialiser admin::admin::admin,user + #${SIMU} rsync -auHAX local.php users.auth.php acl.auth.php "${CONF_DIR}/" + + ${SIMU} sed -i "${CONF_DIR}/local.php" \ + -e "s|\(.*conf\['title'\].*=.*'\).*';|\1${NOM}';|g" \ + -e "s|\(.*conf\['title'\].*=.*'\).*';|\1${NOM}';|g" \ + -e "/conf\['template'\]/d" \ + -e '$a\'"\$conf['template'] = 'docnavwiki';"'' + + ${SIMU} sed -i -e "s|\(.*conf\['lang'\].*=.*'\)en';|\1fr';|g" "${CONF_DIR}/dokuwiki.php" + + ${SIMU} chown -R www-data: "${CONF_DIR}/" + fi + + ${SIMU} unzipInDir "${DNLD_DIR}/docnavwiki.zip" "${TPL_DIR}/" + ${SIMU} chown -R www-data: "${TPL_DIR}/" + # ckgedit : bof + for plugin in captcha smtp todo wrap wrapadd; do + ${SIMU} unzipInDir "${DNLD_DIR}/${plugin}.zip" "${PLG_DIR}" + done + ${SIMU} chown -R www-data: "${PLG_DIR}/" +} + +Version(){ + # $1 ContainerName + VERSION=$(docker exec $1 cat /dokuwiki/VERSION) + echo "Version $1 : ${GREEN}${VERSION}${NC}" +} + + +Reload(){ + # $1 ContainerName + if [ -f "${VOL_PREFIX}wikiData/_data/farms/init.sh" ]; then + ${SIMU} docker exec -ti "${1}" /dokuwiki/data/farms/init.sh + ${SIMU} pkill -KILL lighttpd + fi +} + +########## Main ################# +for ARG in "$@"; do + case "${ARG}" in + '-h' | '--help' ) + usage && exit ;; + '-n' | '--simu') + SIMU="echo" ;; + '-q' ) + QUIET="/dev/null" ;; + '--nas' | '-nas' ) + ONNAS="SURNAS" ;; + '-v' | '--version') + COMMANDS="$(echo "${COMMANDS} VERSION" | sed "s/\s/\n/g" | sort | uniq)" ;; + '--reload' ) + COMMANDS="$(echo "${COMMANDS} RELOAD" | sed "s/\s/\n/g" | sort | uniq)" ;; # le sed sort uniq, c'est pour pas l'avoir en double + '-I' | '--install' ) + COMMANDS="$(echo "${COMMANDS} INIT" | sed "s/\s/\n/g" | sort | uniq)" ;; # le sed sort uniq, c'est pour pas l'avoir en double + '-*' ) # ignore + ;; + *) + ORGA="${ARG%-orga}" + DockerServName="${ORGA}-${dokuwikiServName}" + WIKICOMMUN= + ;; + esac +done + +if [ -z "${COMMANDS}" ]; then usage && exit ; fi + +VOL_PREFIX="${DOCK_VOL}/orga_${ORGA}-" +WIKI_URL="${httpProto}://${ORGA}-${dokuwikiHost}.${domain}" +if [ -n "${WIKICOMMUN}" ]; then + VOL_PREFIX="${DOCK_VOL}/dokuwiki_doku" + WIKI_URL="${httpProto}://${dokuwikiHost}.${domain}" +elif [ -n "${ONNAS}" ]; then + VOL_PREFIX="${NAS_VOL}/orga_${ORGA}-" +fi + +for COMMAND in ${COMMANDS}; do + case "${COMMAND}" in + 'VERSION' ) + Version "${DockerServName}" && exit ;; + 'INIT' ) + Init "${DockerServName}" ;; + 'RELOAD' ) + Reload "${DockerServName}";; + esac +done \ No newline at end of file diff --git a/bin/manageWp.sh b/bin/manageWp.sh new file mode 100755 index 0000000..ba016f7 --- /dev/null +++ b/bin/manageWp.sh @@ -0,0 +1,130 @@ +#!/bin/bash +# Script de manipulation d'un wordpress' +# init /versions / restart ... +# + +KAZ_ROOT=$(cd "$(dirname $0)"/..; pwd) +. $KAZ_ROOT/bin/.commonFunctions.sh +setKazVars +. $DOCKERS_ENV +. $KAZ_ROOT/secret/SetAllPass.sh + +#GLOBAL VARS +PRG=$(basename $0) + +availableOrga=($(getList "${KAZ_CONF_DIR}/container-orga.list")) +AVAILABLE_ORGAS=${availableOrga[*]//-orga/} + +QUIET="1" +ONNAS= + +WPCOMMUN="OUI_PAR_DEFAUT" +DockerServName=${wordpressServName} + +declare -A Posts + +usage() { +echo "${PRG} [OPTION] [COMMANDES] [ORGA] +Manipulation d'un wordpress + +OPTIONS + -h|--help Cette aide :-) + -n|--simu SIMULATION + -q|--quiet On ne parle pas (utile avec le -n pour avoir que les commandes) + --nas L'orga se trouve sur le NAS ! + +COMMANDES (on peut en mettre plusieurs dans l'ordre souhaité) + -I|--install L'initialisation du wordpress + -v|--version Donne la version du wordpress et signale les MàJ + +ORGA parmi : ${AVAILABLE_ORGAS} + ou vide si wordpress commun +" +} + + + +Init(){ + PHP_CONF="${DOCK_VOL}/orga_${ORGA}-wordpress/_data/wp-config.php" + WP_URL="${httpProto}://${ORGA}-${wordpressHost}.${domain}" + + if [ -n "${ONNAS}" ]; then + PHP_CONF="${NAS_VOL}/orga_${ORGA}-wordpress/_data/wp-config.php" + fi + + if ! [[ "$(docker ps -f name=${DockerServName} | grep -w ${DockerServName})" ]]; then + printKazError "Wordpress not running... abort" + exit + fi + + # XXX trouver un test du genre if ! grep -q "'installed' => true," "${PHP_CONF}" 2> /dev/null; then + echo "\n *** Premier lancement de WP" >& $QUIET + + ${SIMU} waitUrl "${WP_URL}" + + ${SIMU} curl -X POST \ + -d "user_name=${wp_WORDPRESS_ADMIN_USER}" \ + -d "admin_password=${wp_WORDPRESS_ADMIN_PASSWORD}" \ + -d "admin_password2=${wp_WORDPRESS_ADMIN_PASSWORD}" \ + -d "pw_weak=true" \ + -d "admin_email=admin@kaz.bzh" \ + -d "blog_public=0" \ + -d "language=fr_FR" \ + "${WP_URL}/wp-admin/install.php?step=2" + + #/* pour forcer les maj autrement qu'en ftp */ + _addVarBeforeInConf "FS_METHOD" "define('FS_METHOD', 'direct');" "\/\* That's all, stop editing! Happy publishing. \*\/" "$PHP_CONF" +} + +Version(){ + VERSION=$(docker exec $DockerServName cat /var/www/html/wp-includes/version.php | grep "wp_version " | sed -e "s/.*version\s*=\s*[\"\']//" | sed "s/[\"\'].*//") + echo "Version $DockerServName : ${GREEN}${VERSION}${NC}" +} + + +_addVarBeforeInConf(){ + # $1 key + # $2 ligne à ajouter avant la ligne + # $3 where + # $4 fichier de conf php + if ! grep -q "$1" "${4}" ; then + echo -n " ${CYAN}${BOLD}$1${NC}" >& $QUIET + ${SIMU} sed -i -e "s/$3/$2\\n$3/" "${4}" + fi +} + +########## Main ################# +for ARG in "$@"; do + case "${ARG}" in + '-h' | '--help' ) + usage && exit ;; + '-n' | '--simu') + SIMU="echo" ;; + '-q' ) + QUIET="/dev/null" ;; + '--nas' | '-nas' ) + ONNAS="SURNAS" ;; + '-v' | '--version') + COMMANDS="$(echo "${COMMANDS} VERSION" | sed "s/\s/\n/g" | sort | uniq)" ;; + '-I' | '--install' ) + COMMANDS="$(echo "${COMMANDS} INIT" | sed "s/\s/\n/g" | sort | uniq)" ;; # le sed sort uniq, c'est pour pas l'avoir en double + '-*' ) # ignore + ;; + *) + ORGA="${ARG%-orga}" + DockerServName="${ORGA}-${wordpressServName}" + WPCOMMUN= + ;; + esac +done + +if [ -z "${COMMANDS}" ]; then usage && exit ; fi + +for COMMAND in ${COMMANDS}; do + case "${COMMAND}" in + 'VERSION' ) + Version && exit ;; + 'INIT' ) + Init ;; + esac +done \ No newline at end of file diff --git a/bin/migVersProdX.sh b/bin/migVersProdX.sh new file mode 100755 index 0000000..a06c11f --- /dev/null +++ b/bin/migVersProdX.sh @@ -0,0 +1,148 @@ +#!/bin/bash + +#koi: pouvoir migrer une orga (data+dns) depuis PROD1 vers PRODx +#kan: 07/12/2023 +#ki: françois puis fab (un peu) + +KAZ_ROOT=$(cd "$(dirname $0)/.."; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +. $DOCKERS_ENV +. $KAZ_ROOT/secret/SetAllPass.sh + + +NAS_VOL="/mnt/disk-nas1/docker/volumes/" + +#TODO: ce tab doit être construit à partir de la liste des machines dispos et pas en dur +tab_sites_destinations_possibles=("kazoulet" "prod2") + +#par défaut, on prend le premier site +SITE_DST="${tab_sites_destinations_possibles[0]}" + +declare -a availableOrga +availableOrga=($(getList "${KAZ_CONF_DIR}/container-orga.list")) + +export SIMU="" +export COPY="" + +usage () { + echo "Usage: $0 [-n] [-d host_distant] [-c] [orga]...[orga]" + echo " -h : this help" + echo " -d host_distant : ${SITE_DST} par défaut" + echo " -n : simulation" + echo " -c : only copy data but doesn't stop" + echo " [orgas] : in ${availableOrga[@]}" + echo " example : migVersProdX.sh -d kazoulet -c splann-orga && migVersProdX.sh -d kazoulet splann-orga" + exit 1 +} + +while getopts "hncd:" option; do + case ${option} in + h) + usage + exit 0 + ;; + n) + SIMU="echo" + ;; + c) + COPY="true" + ;; + d) + SITE_DST=${OPTARG} + ;; + esac +done + +# site distant autorisé ? +if [[ " ${tab_sites_destinations_possibles[*]} " == *" $SITE_DST "* ]]; then + true +else + echo + echo "${RED}${BOLD}Sites distants possibles : ${tab_sites_destinations_possibles[@]}${NC}" + echo + usage + exit 0 +fi + +# Récupérer les orgas dans un tableau +shift $((OPTIND-1)) +Orgas=("$@") + +#ces orgas existent-elles sur PROD1 ? +for orga in "${Orgas[@]}"; do + if [[ ! " ${availableOrga[@]} " =~ " ${orga} " ]]; then + echo + echo "Unknown orga: ${RED}${BOLD}${ARG}${orga}${NC}" + echo + usage + exit 0 + fi +done + +echo +echo "Site distant: ${GREEN}${BOLD}${SITE_DST}${NC}" +echo + +#for orgaLong in ${Orgas}; do +# echo ${Orgas} +#done +#exit + +for orgaLong in ${Orgas}; do + orgaCourt="${orgaLong%-orga}" + orgaLong="${orgaCourt}-orga" + echo "${BLUE}${BOLD}migration de ${orgaCourt}${NC}" + +# if [ -d "${DOCK_VOL_PAHEKO_ORGA}/${orgaCourt}" ]; then +# if ! ssh -p 2201 root@${SITE_DST}.${domain} "test -d ${DOCK_VOL_PAHEKO_ORGA}/${orgaCourt}"; then +# echo "${RED}${BOLD} ... can't move paheko to ${SITE_DST}${NC}" +# echo " intall paheko in ${SITE_DST}.${domain} before!" +# continue +# fi +# fi + + #on créé le répertoire de l'orga pour paheko sur SITE_DST s'il n'existe pas + #pratique quand paheko n'est pas encore installé sur PROD1 mais commandé + if [ -f "${KAZ_COMP_DIR}/${orgaLong}/usePaheko" ]; then + ${SIMU} ssh -p 2201 root@${SITE_DST}.${domain} "mkdir -p ${DOCK_VOL_PAHEKO_ORGA}/${orgaCourt} && chown www-data:www-data ${DOCK_VOL_PAHEKO_ORGA}/${orgaCourt}" + + #ensuite, on peut refaire la liste des routes paheko pour traefik + ${SIMU} ssh -p 2201 root@${SITE_DST}.${domain} "cd ${KAZ_COMP_DIR}/paheko/ && ./docker-compose-gen.sh" + fi + + if [ -z "${COPY}" ]; then + cd "${KAZ_COMP_DIR}/${orgaLong}" + docker-compose logs --tail 100| grep $(date "+ %Y-%m-%d") + checkContinue + ${SIMU} docker-compose down + fi + + if [ $(ls -d ${NAS_VOL}/orga_${orgaCourt}-* 2>/dev/null | wc -l) -gt 0 ]; then + echo "${BLUE}${BOLD} ... depuis nas${NC}" + ${SIMU} rsync -aAhHX --info=progress2 --delete ${NAS_VOL}/orga_${orgaCourt}-* -e "ssh -p 2201" root@${SITE_DST}.${domain}:${DOCK_VOL} + else + echo "${BLUE}${BOLD} ... depuis disque${NC}" + ${SIMU} rsync -aAhHX --info=progress2 --delete ${DOCK_VOL}/orga_${orgaCourt}-* -e "ssh -p 2201" root@${SITE_DST}.${domain}:${DOCK_VOL} + fi + + if [ -z "${COPY}" ]; then + echo "${BLUE}${BOLD} ... config${NC}" + if [ -d "${DOCK_VOL_PAHEKO_ORGA}/${orgaCourt}" ]; then + ${SIMU} rsync -aAhHX --info=progress2 --delete "${DOCK_VOL_PAHEKO_ORGA}/${orgaCourt}" -e "ssh -p 2201" root@${SITE_DST}.${domain}:"${DOCK_VOL_PAHEKO_ORGA}/" + fi + ${SIMU} rsync -aAhHX --info=progress2 --delete ${KAZ_COMP_DIR}/${orgaLong} -e "ssh -p 2201" root@${SITE_DST}.${domain}:${KAZ_COMP_DIR}/ + ${SIMU} ssh -p 2201 root@${SITE_DST}.${domain} "grep -q '^${orgaLong}\$' /kaz/config/container-orga.list || echo ${orgaLong} >> /kaz/config/container-orga.list" + ${SIMU} ssh -p 2201 root@${SITE_DST}.${domain} ${KAZ_COMP_DIR}/${orgaLong}/init-volume.sh + + cd "${KAZ_COMP_DIR}/${orgaLong}" + ${SIMU} ./orga-rm.sh + ${SIMU} ssh -p 2201 root@${SITE_DST}.${domain} "${KAZ_COMP_DIR}/${orgaLong}/orga-gen.sh" --create + ${SIMU} ssh -p 2201 root@${SITE_DST}.${domain} "${KAZ_BIN_DIR}/container.sh" start "${orgaLong}" + + ${SIMU} ssh -p 2201 root@${SITE_DST}.${domain} "${KAZ_BIN_DIR}/manageCloud.sh" --officeURL "${orgaCourt}" + fi + + +done diff --git a/bin/migration.sh b/bin/migration.sh new file mode 100755 index 0000000..8e48f76 --- /dev/null +++ b/bin/migration.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +CV1=/kaz-old/bin/container.sh +DV1=/kaz-old/dockers +EV1=/kaz-old/config +SV1=/kaz-old/secret + +BV2=/kaz/bin +DV2=/kaz/dockers +EV2=/kaz/config +SV2=/kaz/secret +OV2=/kaz/config/orgaTmpl/orga-gen.sh + +[ -x "${CV1}" ] || exit +[ -d "${BV2}" ] || exit + +SIMU="echo SIMU" + +${SIMU} "${CV1}" stop orga +${SIMU} "${CV1}" stop + +${SIMU} rsync "${EV1}/dockers.env" "${EV2}/" +${SIMU} rsync "${SV1}/SetAllPass.sh" "${SV2}/" +${SIMU} "${BV2}/updateDockerPassword.sh" + +# XXX ? rsync /kaz/secret/allow_admin_ip /kaz-git/secret/allow_admin_ip + +${SIMU} "${BV2}/container.sh" start cloud dokuwiki ethercalc etherpad framadate paheko gitea jirafeau mattermost postfix proxy roundcube web + +${SIMU} rsync -aAHXh --info=progress2 "${DV1}/web/html/" "/var/lib/docker/volumes/web_html/_data/" +${SIMU} chown -R www-data: "/var/lib/docker/volumes/web_html/_data/" + +${SIMU} cd "${DV1}" +cd "${DV1}" +for ORGA_DIR in *-orga; do + services=$(echo $([ -x "${ORGA_DIR}/tmpl-gen.sh" ] && "${ORGA_DIR}/tmpl-gen.sh" -l)) + if [ -n "${services}" ]; then + ORGA="${ORGA_DIR%-orga}" + + echo " * ${ORGA}: ${services}" + ${SIMU} "${OV2}" "${ORGA}" $(for s in ${services}; do echo "+${s}"; done) + fi +done + diff --git a/bin/mvOrga2Nas.sh b/bin/mvOrga2Nas.sh new file mode 100755 index 0000000..0c55269 --- /dev/null +++ b/bin/mvOrga2Nas.sh @@ -0,0 +1,172 @@ +#!/bin/bash + +# déplace des orga de +# /var/lib/docker/volumes/ +# vers +# /mnt/disk-nas1/docker/volumes/ + +KAZ_ROOT=$(cd "$(dirname $0)"/..; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars +DOCK_NAS="/mnt/disk-nas1/docker/volumes" + +DOCK_SRC="${DOCK_VOL}" +DOCK_DST="${DOCK_NAS}" + +export PRG="$0" +cd $(dirname $0) + +. "${DOCKERS_ENV}" + +declare -a availableOrga +availableOrga=($(sed -e "s/\(.*\)[ \t]*#.*$/\1/" -e "s/^[ \t]*\(.*\)-orga$/\1/" -e "/^$/d" "${KAZ_CONF_DIR}/container-orga.list")) + +# no more export in .env +export $(set | grep "domain=") + +export SIMU="" +export ONLY_SYNC="" +export NO_SYNC="" +export FORCE="" +export REVERSE="" + +usage(){ + echo "Usage: ${PRG} [orga...]" + echo " -h help" + echo " -n simulation" + echo " -y force" + echo " -s phase1 only" + echo " -r reverse (${DOCK_NAS} to ${DOCK_VOL})" + echo " -ns no pre sync" + exit 1 +} + +for ARG in $@; do + case "${ARG}" in + '-h' | '-help' ) + usage + ;; + '-n' ) + shift + export SIMU="echo" + ;; + '-y' ) + shift + export FORCE="yes" + ;; + '-s' ) + shift + export ONLY_SYNC="yes" + ;; + '-r' ) + shift + export REVERSE="yes" + ;; + '-ns' ) + shift + export NO_SYNC="yes" + ;; + *) + break + ;; + esac +done + +[[ -z "$1" ]] && usage + +for ARG in $@; do + [[ ! " ${availableOrga[*]} " =~ " ${ARG} " ]] && echo "${RED}${ARG}${NC} is not an orga" && usage +done + +######################################## + +# on copie dans new pour que le changement soit atomique +NEW="" +if [ -n "${REVERSE}" ]; then + DOCK_SRC="${DOCK_NAS}" + DOCK_DST="${DOCK_VOL}" + NEW="/new" + ${SIMU} mkdir -p "${DOCK_DST}${NEW}" +fi +# on garde une copie dans back au cas où +BACK="${DOCK_SRC}/old" + +echo " move from ${BLUE}${BOLD}${DOCK_SRC}${NC} to ${BLUE}${BOLD}${DOCK_DST}${NC}" +checkContinue + +cd "${DOCK_SRC}" +volext=$(ls -d orga* | sed 's%.*-%%' | sort -u) +declare -a orgaPhase2 + +# Pour que l'interruption de service soit la plus courte possible, +# on pré-copie toutes les infos de puis la création de l'orga +echo -n "${BLUE}Phase 1: pre sync.${NC} " +[[ -z "${FORCE}" ]] && [[ -z "${NO_SYNC}" ]] && checkContinue +echo +for ARG in $@; do + for EXT in ${volext}; do + vol="orga_${ARG}-${EXT}" + # test le service existe + [ -e "${DOCK_SRC}/${vol}" ] || continue + # si c'est un lien sur /var/lib c'est déjà fait + [ -z "${REVERSE}" ] && [ -L "${DOCK_SRC}/${vol}" ] && echo "${GREEN}${vol}${NC} : done" && continue + # si c'est un lien sur le NAS c'est un problème + [ -n "${REVERSE}" ] && [ -L "${DOCK_SRC}/${vol}" ] && echo "${GREEN}${vol}${NC} : bug" && continue + # si c'est pas un répertoire c'est un problème + ! [ -d "${DOCK_SRC}/${vol}" ] && echo "${RED}${vol}${NC} : done ?" && continue + # si transfert est déjà fait + if [ -n "${REVERSE}" ]; then + ! [ -L "${DOCK_DST}/${vol}" ] && echo "${RED}${vol}${NC} : done" && continue + fi + echo " - ${YELLOW}${vol}${NC}" + [[ -z "${NO_SYNC}" ]] && ${SIMU} rsync -auHAX --info=progress2 "${DOCK_SRC}/${vol}/" "${DOCK_DST}${NEW}/${vol}/" + [[ " ${orgaPhase2[@]} " =~ " ${ARG} " ]] || orgaPhase2+=( "${ARG}" ) + done +done + +[ -n "${ONLY_SYNC}" ] && exit 0 + +if (( ${#orgaPhase2[@]} == 0 )); then + exit 0 +fi + +echo -n "${BLUE}Phase 2: mv.${NC} " +[[ -z "${FORCE}" ]] && checkContinue +echo +mkdir -p "${BACK}" +for ARG in "${orgaPhase2[@]}"; do + cd "${KAZ_ROOT}" + cd "${KAZ_COMP_DIR}/${ARG}-orga" + ! [ -e "docker-compose.yml" ] && echo "no docker-compose.yml for ${RED}${ARG}${NC}" && continue + ${SIMU} docker-compose down + # L'arrêt ne durera que le temps de copier les modifications depuis la phase 1. + for EXT in ${volext}; do + vol="orga_${ARG}-${EXT}" + # test le service existe + [ -e "${DOCK_SRC}/${vol}" ] || continue + # si c'est un lien sur /var/lib c'est déjà fait + [ -z "${REVERSE}" ] && [ -L "${DOCK_SRC}/${vol}" ] && echo "${GREEN}${vol}${NC} : done" && continue + # si c'est un lien sur le NAS c'est un problème + [ -n "${REVERSE}" ] && [ -L "${DOCK_SRC}/${vol}" ] && echo "${GREEN}${vol}${NC} : bug" && continue + # si c'est pas un répertoire c'est un problème + ! [ -d "${DOCK_SRC}/${vol}" ] && echo "${RED}${vol}${NC} : done ?" && continue + # si transfert est déjà fait + if [ -n "${REVERSE}" ]; then + ! [ -L "${DOCK_DST}/${vol}" ] && echo "${RED}${vol}${NC} : done" && continue + fi + echo " - ${YELLOW}${vol}${NC}" + ${SIMU} rsync -auHAX --info=progress2 --delete "${DOCK_SRC}/${vol}/" "${DOCK_DST}${NEW}/${vol}/" || exit 1 + ${SIMU} mv "${DOCK_SRC}/${vol}" "${BACK}/" + if [ -z "${REVERSE}" ]; then + # cas de /var/lib vers NAS + ${SIMU} ln -sf "${DOCK_DST}/${vol}" "${DOCK_SRC}/" + else + # cas NAS vers /var/lib + ${SIMU} rm -f "${DOCK_SRC}/${vol}" + ${SIMU} mv "${DOCK_DST}${NEW}/${vol}" "${DOCK_DST}/" + fi + done + ${SIMU} docker-compose up -d + [[ -x "reload.sh" ]] && "./reload.sh" + echo +done diff --git a/bin/nettoie b/bin/nettoie new file mode 100755 index 0000000..3201a21 --- /dev/null +++ b/bin/nettoie @@ -0,0 +1,83 @@ +#!/bin/bash + +POUBELLE="${HOME}/tmp/POUBELLE" +mkdir -p "${POUBELLE}" + +usage () { + echo `basename "$0"` " [-] [-h] [-help] [-clean] [-wipe] [-n] [directory ...]" + echo " remove temporaries files" + echo " - Treat the following arguments as filenames \`-\' so that" + echo " you can specify filenames starting with a minus." + echo " -h" + echo " -help Display this help." + echo " -n Simulate the remove (juste print files)." + echo " directories are the roots where the purge had to be done. If no" + echo " roots are given, the root is the home directory." +} + +DETRUIT="" +ANT_OPT="" +ANT_CMD="" +case "$1" in + '-' ) + shift;; + '-n' ) + DETRUIT="echo" + ANT_OPT="-p" + shift;; + '-clean' ) + ANT_CMD="clean" + shift;; + '-wipe' ) + ANT_CMD="wipe" + shift;; + '-h' | '-help' ) + usage + shift + exit;; +esac + +DIRS=$* +if test "$#" -le 1 +then + DIRS="$*" + if test -z "$1" -o -d "$1" + then + cd $1 || exit + DIRS=. + fi +fi + +if test "${ANT_CMD}" != "" +then + find $DIRS -type f -name build.xml -execdir ant -f {} "${ANT_CMD}" \; + find $DIRS -type f -name Makefile\* -execdir make -f {} "${ANT_CMD}" \; + exit +fi + +find $DIRS -type d -name .xvpics -exec $DETRUIT rm -r {} \; -prune + +find $DIRS '(' \ + -type d -name POUBELLE -prune \ + -o \ + -type f '(' \ + -name core -o -name '*.BAK' -o -name '*.bak' -o -name '*.CKP' \ + -o -name '.*.BAK' -o -name '.*.bak' -o -name '.*.CKP' \ + -o -name '.*.back' -o -name '*.back' \ + -o -name '*.backup' -o -name '*.backup ' \ + -o -name '.*.backup' -o -name '.*.backup ' \ + -o -name .make.state \ + -o -name 'untitled*' -o -name 'Sansnom' \ + -o -name '.emacs_*' -o -name '.wi_*' \ + -o -name 'ws_ftp.log' -o -name 'hs_err*.log' \ + -o -name '#*' -o -name '*~' -o -name '.*~' -o -name junk \ + -o -name '.~lock.*#' \ + -o -name '*%' -o -name '.*%' \ + ')'\ + -print -exec $DETRUIT mv -f '{}' "${POUBELLE}" \; \ + ')' + +# -o -name '*.ps' -o -name '.*.ps' \ +# -o -name '*.i' -o -name '*.ixx' \ +# -o -name '.*.sav' -o -name '*.sav' \ + diff --git a/bin/nextcloud_maintenance.sh b/bin/nextcloud_maintenance.sh new file mode 100755 index 0000000..0823fbb --- /dev/null +++ b/bin/nextcloud_maintenance.sh @@ -0,0 +1,24 @@ +#!/bin/bash +#on récupère toutes les variables et mdp +KAZ_ROOT=/kaz +. $KAZ_ROOT/bin/.commonFunctions.sh +setKazVars +. $DOCKERS_ENV +. $KAZ_ROOT/secret/SetAllPass.sh + +URL_AGORA=https://$matterHost.$domain/api/v4 +EQUIPE=kaz + +PostMattermost() { + PostM=$1 + CHANNEL=$2 + TEAMID=$(curl -s -H "Authorization: Bearer ${mattermost_token}" "${URL_AGORA}/teams/name/${EQUIPE}" | jq .id | sed -e 's/"//g') + CHANNELID=$(curl -s -H "Authorization: Bearer ${mattermost_token}" ${URL_AGORA}/teams/${TEAMID}/channels/name/${CHANNEL} | jq .id | sed -e 's/"//g') + curl -s i-X POST -i -H "Authorization: Bearer ${mattermost_token}" -d "{\"channel_id\":\"${CHANNELID}\",\"message\":\"${PostM}\"}" "${URL_AGORA}/posts" >/dev/null 2>&1 +} + +LISTEORGA=$(ls -F1 /var/lib/docker/volumes/ | grep cloudData | sed -e 's/^orga_//g' -e 's/-cloudData\///g') +for CLOUD in ${LISTEORGA} +do + /kaz/bin/gestContainers.sh -cloud -occ "maintenance:mode" ${CLOUD} | grep -i enable && PostMattermost "ERREUR : Le cloud ${CLOUD} sur ${site} est en mode maintenance" "Sysadmin-alertes" +done diff --git a/bin/postfix-superviz.sh b/bin/postfix-superviz.sh new file mode 100755 index 0000000..c8c5eeb --- /dev/null +++ b/bin/postfix-superviz.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# supervision de sympa +#KAZ_ROOT=$(cd "$(dirname $0)"/..; pwd) +KAZ_ROOT=/kaz +. $KAZ_ROOT/bin/.commonFunctions.sh +setKazVars + +. $DOCKERS_ENV +. $KAZ_ROOT/secret/SetAllPass.sh + +URL_AGORA=$(echo $matterHost).$(echo $domain) +MAX_QUEUE=50 + +OLDIFS=$IFS +IFS=" " +COUNT_MAILQ=$(docker exec -ti mailServ mailq | tail -n1 | gawk '{print $5}') + +docker exec ${mattermostServName} bin/mmctl --suppress-warnings auth login $httpProto://$URL_AGORA --name local-server --username $mattermost_user --password $mattermost_pass >/dev/null 2>&1 + +if [ "${COUNT_MAILQ}" -gt "${MAX_QUEUE}" ]; then + echo "---------------------------------------------------------- " + echo -e "Mail queue Postfix ALert, Messages: ${RED}${COUNT_MAILQ}${NC}" + echo "---------------------------------------------------------- " + docker exec mattermostServ bin/mmctl post create kaz:Sysadmin-alertes --message "Alerte mailq Postfix : La file d' attente est de ${COUNT_MAILQ} messages" >/dev/null 2>&1 +fi + +IFS=${OLDIFS} diff --git a/bin/runAlertings.sh b/bin/runAlertings.sh new file mode 100755 index 0000000..40e3a95 --- /dev/null +++ b/bin/runAlertings.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +KAZ_ROOT=$(cd "$(dirname $0)/.."; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +. "${DOCKERS_ENV}" + +for dockerToTest in $KAZ_ROOT/dockers/*/alerting/; do + for aTest in $dockerToTest/*; do + res=$($aTest) + if [ -n "$res" ]; then + echo $res + docker exec -ti mattermostServ bin/mmctl post create kaz:Sysadmin-alertes --message "${res:0:1000}" + fi + done +done diff --git a/bin/sauve_memory.sh b/bin/sauve_memory.sh new file mode 100755 index 0000000..a83bd45 --- /dev/null +++ b/bin/sauve_memory.sh @@ -0,0 +1,37 @@ +#! /bin/sh +# date: 30/03/2022 +# koi: récupérer du swap et de la ram (uniquement sur les services qui laissent filer la mémoire) +# ki: fab + +#pour vérifier les process qui prennent du swap : for file in /proc/*/status ; do awk '/Tgid|VmSwap|Name/{printf $2 " " $3}END{ print ""}' $file; done | grep kB | sort -k 3 -n + +# Ces commandes donnent le nom du process, son PID et la taille mémoire en swap. Par exemple : +# dans /proc//status y'a un VMSwap qui est la taille de swap utilisée par le process. + +#calc +docker restart ethercalcDB ethercalcServ + +#sympa +#docker restart sympaServ +#/kaz/dockers/sympa/reload.sh +# --> bof, ça arrête mal le bazar (4 mails d'ano autour de sympa_msg.pl / bounced.pl / task_manager.pl / bulk.pl / archived.pl) + +#sympa +# restart de sympa et relance du script de copie des librairies du filtre de messages +#docker exec -it sympaServ service sympa stop +#sleep 5 +#docker exec -it sympaServ service sympa start +#sleep 5 +#/kaz/dockers/sympa/reload.sh +#sleep 2 +#docker exec sympaServ chmod 777 /home/filter/filter.sh +#docker exec sympaServ sendmail -q + +#pour restart cette s.... de collabora +/kaz/bin/gestContainers.sh -office -m -r + +#postfix +docker exec -it mailServ supervisorctl restart changedetector + +#proxy +docker exec -i proxyServ bash -c "/etc/init.d/nginx reload" diff --git a/bin/sauve_serveur.sh b/bin/sauve_serveur.sh new file mode 100755 index 0000000..e0fd36c --- /dev/null +++ b/bin/sauve_serveur.sh @@ -0,0 +1,18 @@ +#! /bin/sh +# date: 12/11/2020 + +#PATH=/bin:/sbin:/usr/bin:/usr/sbin +PATH_SAUVE="/home/sauve/" + +iptables-save > $PATH_SAUVE/iptables.sav + +dpkg --get-selections > $PATH_SAUVE/dpkg_selection +tar -clzf $PATH_SAUVE/etc_sauve.tgz /etc 1> /dev/null 2> /dev/null +tar -clzf $PATH_SAUVE/var_spool.tgz /var/spool 1> /dev/null 2> /dev/null +tar -clzf $PATH_SAUVE/root.tgz /root 1> /dev/null 2> /dev/null +tar -clzf $PATH_SAUVE/kaz.tgz /kaz 1> /dev/null 2> /dev/null +rsync -a /var/spool/cron/crontabs $PATH_SAUVE + +#sauve les bases +/kaz/bin/container.sh save + diff --git a/bin/scriptBorg.sh b/bin/scriptBorg.sh new file mode 100755 index 0000000..410546c --- /dev/null +++ b/bin/scriptBorg.sh @@ -0,0 +1,385 @@ +#!/bin/bash +# -------------------------------------------------------------------------------------- +# Didier +# +# Script de sauvegarde avec BorgBackup +# la commande de creation du dépot est : borg init --encryption=repokey /mnt/backup-nas1/BorgRepo +# la conf de borg est dans /root/.config/borg +# Le repository peut etre distant: BORG_REPO='ssh://user@host:port/path/to/repo' +# la clé ssh devra être copiée sur le site distant et l' init se fera sous la forme +# borg init --encryption=repokey ssh://user@host:port/path/to/repo +# la clé est modifiable avec la commande borg key change-passphrase +# toutes les variables sont dans la config générale de KAZ +# scripts PRE et Post +# les script pre et post doivent s' appelle pre_xxxxx.sh ou post_xxxx.sh +# La variable BORGSCRIPTS est le chemin du repertoire des scripts dans la config générale de Kaz +##################################################### +#KAZ_ROOT=$(cd "$(dirname $0)"/..; pwd) +KAZ_ROOT=/kaz +. $KAZ_ROOT/bin/.commonFunctions.sh +setKazVars +. $DOCKERS_ENV +. $KAZ_ROOT/secret/SetAllPass.sh + +VERSION="V-18-05-2024" +PRG=$(basename $0) +RACINE=$(echo $PRG | awk '{print $1}') +#IFS=' ' +##################################################### +# Variables générales +##################################################### +# le volume monté ou sera le repo +# impérativement laisser vide dans le cas d' un repo distant +VOLUME_SAUVEGARDES=${borg_VOLUME_SAUVEGARDES} +# le repo Borg +export BORG_REPO=${borg_BORG_REPO} +# Le mot de passe du repo +export BORG_PASSPHRASE=${borg_BORG_PASSPHRASE} +# les personnes qui recevront le rapport de sauvegarde +MAIL_RAPPORT=${borg_MAIL_RAPPORT} +# Recevoir un mail quand la sauvegarde est OK ? +MAILOK=${borg_MAILOK} +MAILWARNING=${borg_MAILWARNING} +MAILDETAIL=${borg_MAILDETAIL} +# repertoire de montage des sauvegardes pour les restaurations +BORGMOUNT=${borg_BORGMOUNT} +# - la liste des repertoires à sauver séparés par un espace +LISTREPSAUV=${borg_LISTREPSAUV} +# - Les sauvegardes à garder jour, semaines, mois +NB_BACKUPS_JOUR=${borg_NB_BACKUPS_JOUR} +NB_BACKUPS_SEM=${borg_NB_BACKUPS_SEM} +NB_BACKUPS_MOIS=${borg_NB_BACKUPS_MOIS} +# Le Repertoire ou sont les pré traitement +BORGSCRIPTS=${borg_BORGSCRIPTS} +BORGLOG="${borg_BORGLOG}/BorgBackup-$(date +%d-%m-%Y-%H-%M-%S).log" +DEBUG=false + +##################################################### +# +FICLOG="/var/log/${PRG}.log" + +##################################################### + +trap 'LogFic "script stoppé sur un SIGTERM ou SIGINT" >&2; exit 2' INT TERM + +LogFic() { + [ ! -w ${FICLOG} ] && { echo "Probleme d' ecriture dans $FICLOG" ; exit 1 ;} + echo "$(date +%d-%m-%Y-%H-%M-%S) : $1" >> ${FICLOG} +} +# +ExpMail() { + MAIL_SOURCE=$1 + MAIL_SUJET=$2 + MAIL_DEST=$3 + MAIL_TEXTE=$4 + # a mettre ailleurs + mailexp=${borg_MAILEXP} + mailpassword=${borg_MAILPASSWORD} + mailserveur=${borg_MAILSERVEUR} + # + #sendemail -t ${MAIL_DEST} -u ${MAIL_SUJET} -m ${MAIL_TEXTE} -f $mailexp -s $mailserveur:587 -xu $mailexp -xp $mailpassword -o tls=yes >/dev/null 2>&1 + printf "Subject:${MAIL_SUJET}\n${MAIL_TEXTE}" | msmtp ${MAIL_DEST} + #docker exec -i mailServ mailx -a 'Content-Type: text/plain; charset="UTF-8"' -r ${MAIL_SOURCE} -s "${MAIL_SUJET}" ${MAIL_DEST} << EOF + #${MAIL_TEXTE} +#EOF +} + +Pre_Sauvegarde() { + if [ -d ${BORGSCRIPTS} ] + then + cd ${BORGSCRIPTS} + for FicPre in $(ls ) + do + if [ -x ${FicPre} ] && [ $(echo ${FicPre} | grep -i ^pre_) ] + then + LogFic " - Pré traitement de la sauvegarde : ${FicPre}" + [ "$DEBUG" = true ] && echo " - Pré traitement de la sauvegarde : ${FicPre}" + ./${FicPre} + fi + done + fi +} + +Post_Sauvegarde() { + if [ -d ${BORGSCRIPTS} ] + then + cd ${BORGSCRIPTS} + for FicPre in $(ls ) + do + if [ -x ${FicPre} ] && [ $(echo ${FicPre} | grep -i ^post_) ] + then + LogFic " - Post traitement de la sauvegarde : ${FicPre}" + [ "$DEBUG" = true ] && echo " - Post traitement de la sauvegarde : ${FicPre}" + ./${FicPre} + fi + done + fi + +} + +Sauvegarde() { + Pre_Sauvegarde + BACKUP_PRE=$? + borg create \ + --filter AME \ + --exclude-caches \ + --stats \ + --show-rc \ + --exclude 'home/*/.cache/*' \ + --exclude 'var/tmp/*' \ + ::$(date +%Y-%m-%d-%H-%M-%S-%A)-{hostname} \ + ${LISTREPSAUV} >>${BORGLOG} 2>>${BORGLOG} + BACKUP_EXIT=$? + Post_Sauvegarde + BACKUP_POST=$? +} + +Purge() { +borg prune \ + --prefix '{hostname}-' \ + --keep-daily ${NB_BACKUPS_JOUR} \ + --keep-weekly ${NB_BACKUPS_SEM} \ + --keep-monthly ${NB_BACKUPS_MOIS} \ + +PRUNE_EXIT=$? +} + +Compact() { + +borg compact --progress ${BORG_REPO} + +} + +usage() { + +echo "-h : Usage" +echo "-c : Permet de compacter ${BORG_REPO}" +echo "-d : Permet de verifier les variables de sauvegarde" +echo "-i : Mode interractif" +echo "-l : Liste les sauvegardes sans monter ${BORG_REPO}" +echo "-m : Monte le REPO (${BORG_REPO} sur ${BORGMOUNT})" +echo "-p : Permet de lancer la phase de purge des backup en fonctions des variables: jour=${NB_BACKUPS_JOUR},semaine=${NB_BACKUPS_SEM},mois=${NB_BACKUPS_MOIS}" +echo "-s : Lance la sauvegarde" +echo "-u : Demonte le REPO (${BORG_REPO} de ${BORGMOUNT})" +echo "-v : Version" +exit +} + +Borgvariables() { + echo "-----------------------------------------------------------" + echo " Variables applicatives pour le site ${site}" + echo "-----------------------------------------------------------" + for borgvar in $(set | grep borg_ | sed -e 's/borg_//' -e 's/=.*$//' | grep ^[A-Z]) + do + echo "$borgvar=${!borgvar}" + done + if grep borgfs /etc/mtab >/dev/null 2>&1 + then + echo -e "${RED}WARNING${NC}: ${BORG_REPO} est monté sur ${BORGMOUNT}" + fi +} + +Borgmount() { + LogFic "Montage du repo ${BORG_REPO} sur ${BORGMOUNT} .. " + echo -en "Montage du repo ${BORG_REPO} sur ${BORGMOUNT} .. " + borg mount ${BORG_REPO} ${BORGMOUNT} >/dev/null 2>&1 + if [ $? = 0 ] + then + LogFic "Ok" + echo -e "${GREEN}Ok${NC}" + else + LogFic "Error" + echo -e "${RED}Error $?${NC}" + fi + exit +} + +Borgumount() { + LogFic "Demontage du repo ${BORG_REPO} sur ${BORGMOUNT} .. " + echo -en "Demontage du repo ${BORG_REPO} sur ${BORGMOUNT} .. " + borg umount ${BORGMOUNT} >/dev/null 2>&1 + if [ $? = 0 ] + then + LogFic "Ok" + echo -e "${GREEN}Ok${NC}" + else + LogFic "Error" + echo -e "${RED}Error $?${NC}" + fi + exit +} + +Borglist() { + LogFic "Borg list demandé" + borg list --short ${BORG_REPO} + exit +} + +main() { + +# ****************************************************** Main ******************************************************************* +# Création du fichier de log +touch ${FICLOG} +type -P sendemail || { echo "sendemail non trouvé";exit 1;} +# +LogFic "#########################################################################################################################" +LogFic " *************** ${PRG} Version ${VERSION} ***************" +LogFic "#########################################################################################################################" +# test si les variables importantes sont renseignées et sortie si tel n' est pas le cas +if [ -z "${VOLUME_SAUVEGARDES}" ] && [ -z "${BORG_REPO}" ] || [ -z "${BORG_REPO}" ] || [ -z "${BORG_PASSPHRASE}" ] || [ -z "${MAIL_RAPPORT}" ] +then + echo "Les variables VOLUME_SAUVEGARDES, BORG_REPO, BORG_PASSPHRASE, MAIL_RAPPORT sont à verifier" + LogFic "Les variables VOLUME_SAUVEGARDES, BORG_REPO, BORG_PASSPHRASE, MAIL_RAPPORT sont à verifier" + LogFic "Sortie du script" + exit 1 +fi +# test si le volume de sauvegarde est ok +if [ ! -z ${VOLUME_SAUVEGARDES} ] +then + [ !$(grep "${VOLUME_SAUVEGARDES}" /etc/mtab >/dev/null 2>&1) ] || { echo "le volume de sauvegarde ${VOLUME_SAUVEGARDES} n' est pas monté"; LogFic "Erreur de montage du volume ${VOLUME_SAUVEGARDES} de sauvegarde" ; exit 1;} +else + [ ! $(echo ${BORG_REPO} | grep -i ssh 2>/dev/null) ] && { echo "Problème avec le repo distant ";exit 1;} +fi + +# Test si le REPO est monté : on sort +if grep borgfs /etc/mtab >/dev/null 2>&1 +then + echo "le REPO : ${BORG_REPO} est monté , je sors" + LogFic "le REPO : ${BORG_REPO} est monté , je sors" + ExpMail borg@${domain} "${site} : Sauvegarde en Erreur !!!!" ${MAIL_RAPPORT} "le REPO : ${BORG_REPO} est monté, sauvegarde impossible" + exit 1 +fi + +# Tout se passe bien on continue +LogFic " - Repertoire a sauver : ${LISTREPSAUV}" +LogFic " - Volume Nfs monté : ${VOLUME_SAUVEGARDES}" +LogFic " - Repertoire des sauvegardes : ${BORG_REPO}" +[ ! -d ${BORGSCRIPTS} ] && LogFic "Pas de repertoire de PRE et POST" || LogFic " - Repertoire des scripts Post/Pré : ${BORGSCRIPTS}" +[ "${DEBUG}" = true ] && [ -d ${BORGSCRIPTS} ] && echo "Rep des scripts PRE/POST :${BORGSCRIPTS}" +LogFic " - Rapport par Mail : ${MAIL_RAPPORT}" +LogFic " - Backups jour : ${NB_BACKUPS_JOUR} , Backups semaines : ${NB_BACKUPS_SEM} , Backups mois : ${NB_BACKUPS_MOIS}" +[ "${DEBUG}" = true ] && echo "${LISTREPSAUV} sauvé dans ${BORG_REPO}, Rapport vers : ${MAIL_RAPPORT}" +LogFic "#########################################################################################################################" +LogFic " - Démarrage de la sauvegarde" +[ "$DEBUG" = true ] && echo "Demarrage de la sauvegarde : " +LogFic " - Log dans ${BORGLOG}" +Sauvegarde +[ "$DEBUG" = true ] && echo "code de retour de backup : ${BACKUP_EXIT}" +LogFic " - Code de retour de la commande sauvegarde : ${BACKUP_EXIT}" +LogFic " - Démarrage du nettoyage des sauvegardes" +[ "$DEBUG" = true ] && echo "Nettoyage des sauvegardes: " +Purge +LogFic " - Code retour du Nettoyage des sauvegardes (0=OK; 1=WARNING, 2=ERROR) : ${PRUNE_EXIT}" +[ "$DEBUG" = true ] && echo "code de retour prune : ${PRUNE_EXIT}" +# +######################################################################################## +# si la variable MAILDETAIL est true alors on affecte le contenu du log sinon LOGDATA est VIDE +LOGDATA="" +[ "$MAILDETAIL" = true ] && LOGDATA=$(cat ${BORGLOG}) +[ "$DEBUG" = true ] && [ "$MAILDETAIL" = true ] && echo "Envoi du mail à ${MAIL_RAPPORT}" +# On teste le code retour de la sauvegarde, on log et on envoie des mails +case "${BACKUP_EXIT}" in + '0' ) + IFS='' + MESS_SAUVE_OK=" +Salut + +La sauvegarde est ok, ce message peut être enlevé avec la variable MAILOK=false +Que la force soit avec toi + +BorgBackup + +" + LogFic " - la sauvegarde est OK" + [ "$MAILOK" = true ] && ExpMail borg@${domain} "${site} : Sauvegarde Ok" ${MAIL_RAPPORT} ${MESS_SAUVE_OK}${LOGDATA} + IFS=' ' + ;; + '1' ) + IFS='' + MESS_SAUVE_ERR=" +Salut + +La sauvegarde est en warning +Code de retour de la commande sauvegarde : ${BACKUP_EXIT} +Le log contenant les infos est ${BORGLOG} + +BorgBackup + +" + LogFic " - Sauvegarde en Warning: ${BACKUP_EXIT}" + [ "$MAILWARNING" = true ] && ExpMail borg@${domain} "${site} : Sauvegarde en Warning: ${BACKUP_EXIT}" ${MAIL_RAPPORT} ${MESS_SAUVE_ERR}${LOGDATA} + IFS=' ' + ;; + * ) + IFS='' + MESS_SAUVE_ERR=" +Salut + +La sauvegarde est en Erreur +Code de retour de la commande sauvegarde : ${BACKUP_EXIT} +Le log à consulter est ${BORGLOG} + +BorgBackup + +" + LogFic " - !!!!! Sauvegarde en Erreur !!!!! : ${BACKUP_EXIT}" + ExpMail borg@${domain} "${site} : Sauvegarde en Erreur !!!! : ${BACKUP_EXIT}" ${MAIL_RAPPORT} ${MESS_SAUVE_ERR}${LOGDATA} + IFS=' ' + ;; +esac +LogFic " - Fin de la sauvegarde" +exit +} + +[ ! "$#" -eq "0" ] || usage +# On teste les arguments pour le script +for ARG in $@; do + case "${ARG}" in + '-h' | '-help' ) + usage + ;; + '-m' ) + shift + Borgmount + ;; + '-u' ) + shift + Borgumount + ;; + '-l' ) + shift + Borglist + ;; + '-i' ) + shift + DEBUG=true + ;; + '-v' ) + shift + echo "Version : ${VERSION}" + exit + ;; + '-d' ) + shift + Borgvariables + exit + ;; + '-c' ) + shift + Compact + exit + ;; + '-s' ) + main + ;; + '-p' ) + shift + read -p "Ok pour lancer la purge en fonction de ces valeurs : jour=${NB_BACKUPS_JOUR},semaine=${NB_BACKUPS_SEM},mois=${NB_BACKUPS_MOIS} ? O/N : " READPURGE + [[ ${READPURGE} =~ ^[oO]$ ]] && Purge || echo "pas de purge" + exit + ;; + * | ' ' ) + usage + ;; + esac +done diff --git a/bin/scriptSauve.sh b/bin/scriptSauve.sh new file mode 100755 index 0000000..3533241 --- /dev/null +++ b/bin/scriptSauve.sh @@ -0,0 +1,161 @@ +#!/bin/bash +# Didier le 14 Septembre 2022 +# +# TODO : Inclure un script post et pre. +# +##################################################### +#KAZ_ROOT=$(cd "$(dirname $0)"/..; pwd) +KAZ_ROOT="/kaz" +. $KAZ_ROOT/bin/.commonFunctions.sh +setKazVars +. $DOCKERS_ENV +. $KAZ_ROOT/secret/SetAllPass.sh +VERSION="1.0" +PRG=$(basename $0) +RACINE=$(echo $PRG | awk '{print $1}') +IFS=' ' +# +##################################################### +MAILSOURCE="sauve@kaz.bzh" +VOLUME_SAUVEGARDES="/mnt/backup-nas1" +#SAUVE_REPO=${VOLUME_SAUVEGARDES}/SauveRepo +SAUVE_REPO=admin@nas-kaz1:/share/Backup/SauveRepo +MAIL_RAPPORT="didier@kaz.bzh;francois@kaz.bzh;fab@kaz.bzh;fanch@kaz.bzh" +##################################################### +SCRIPTLOG="/mnt/disk-nas1/log/${PRG}-$(date +%d-%m-%Y-%H-%M-%S).log" +FICLOG="/var/log/${PRG}.log" +##################################################### +# - la liste des repertoires à sauver séparés par un espace +LISTREPSAUV="/var/lib/docker/volumes /kaz" +##################################################### +# - Les sauvegardes à garder jour, semaines, mois +NB_BACKUPS_JOUR=15 +NB_BACKUPS_SEM=8 +NB_BACKUPS_MOIS=12 +##################################################### +# Recevoir un mail quand la sauvegarde est OK ? +MAILOK=true +##################################################### + +trap 'LogFic "script stoppé sur un SIGTERM ou SIGINT" >&2; exit 2' INT TERM + +LogFic() { + [ ! -w ${FICLOG} ] && { echo "Probleme d' ecriture dans $FICLOG" ; exit 1 ;} + echo "$(date +%d-%m-%Y-%H-%M-%S) : $1" >> ${FICLOG} +} +# +ExpMail() { + MAIL_SOURCE=$1 + MAIL_SUJET=$2 + MAIL_DEST=$3 + MAIL_TEXTE=$4 + docker exec -i mailServ mailx -a 'Content-Type: text/plain; charset="UTF-8"' -r ${MAIL_SOURCE} -s "${MAIL_SUJET}" ${MAIL_DEST} << EOF + ${MAIL_TEXTE} +EOF +} +Sauvegarde() { + #$1 est le repertoire à sauver, on créé le sous repertoire dans le repo + CODE_TMP="" + if [ -r $1 ] + then + echo "Sauvegarde $1" >>${SCRIPTLOG} + #mkdir -p ${SAUVE_REPO}/$1 >/dev/null 2>&1 + #rdiff-backup --verbosity 3 $1 ${SAUVE_REPO}/$1 >>${SCRIPTLOG} 2>>${SCRIPTLOG} + rsync -aAHXh --del --stats --exclude 'files_trashbin' $1 ${SAUVE_REPO} >>${SCRIPTLOG} 2>>${SCRIPTLOG} + CODE_TMP=$? + else + LogFic "$1 n' existe pas ou n' est pas accessible en lecture" + CODE_TMP=1 + fi + LogFic "Code Retour de la sauvegarde de $1 : ${CODE_TMP}" + BACKUP_EXIT=$(expr ${BACKUP_EXIT} + ${CODE_TMP} ) +} +# +Purge() { + echo "Commande prune de rdiff-backup" + PRUNE_EXIT=$? +} +# ****************************************************** Main ******************************************************************* +# Création du fichier de log +touch ${FICLOG} +# +LogFic "#########################################################################################################################" +LogFic " *************** ${PRG} Version ${VERSION} ***************" +LogFic "#########################################################################################################################" +# test si les variables importantes sont renseignées et sortie si tel n' est pas le cas +if [ -z "${VOLUME_SAUVEGARDES}" ] || [ -z "${SAUVE_REPO}" ] +then + echo "VOLUME_SAUVEGARDES et SAUVE_REPO à verifier" + LogFic "VOLUME_SAUVEGARDES et SAUVE_REPO à verifier" + LogFic "Sortie du script" + exit 1 +fi + +##################################################################################################################################################### +################### Mise en commentaire de cette section puisque le repo est en rsync ( voir plus tard comment gérer ça ) +# test si le volume de sauvegarde est ok +#grep "${VOLUME_SAUVEGARDES}" /etc/mtab >/dev/null 2>&1 +#if [ "$?" -ne "0" ] +#then +# echo "le volume de sauvegarde ${VOLUME_SAUVEGARDES} n' est pas monté" +# LogFic "Erreur de montage du volume ${VOLUME_SAUVEGARDES} de sauvegarde" +# exit 1 +#fi +# Test si j' ai le droit d' écrire dans le Repo +# [ ! -w ${SAUVE_REPO} ] && { echo "Verifier le droit d' écriture dans ${SAUVE_REPO}" ; LogFic "Verifier le droit d' écriture dans ${SAUVE_REPO}"; exit 1;} +##################################################################################################################################################### + +# Tout se passe bien on continue +LogFic " - Repertoire a sauver : ${LISTREPSAUV}" +#LogFic " - Volume Nfs monté : ${VOLUME_SAUVEGARDES}" +LogFic " - Destination des sauvegardes : ${SAUVE_REPO}" +LogFic " - Rapport par Mail : ${MAIL_RAPPORT}" +#LogFic " - Backups jour : ${NB_BACKUPS_JOUR} , Backups semaines : ${NB_BACKUPS_SEM} , Backups mois : ${NB_BACKUPS_MOIS}" +LogFic "#########################################################################################################################" +LogFic " - Démarrage de la sauvegarde" +LogFic " - Log dans ${SCRIPTLOG}" +BACKUP_EXIT=0 +PRUNE_EXIT=0 +for REPS in ${LISTREPSAUV} +do + LogFic "Sauvegarde de ${REPS}" + Sauvegarde ${REPS} +done +LogFic "Code retour compilé de toutes les sauvegardes : ${BACKUP_EXIT}" +################################## a gérer plus tard +#LogFic " - Démarrage du nettoyage des sauvegardes" +#Purge +#LogFic " - Code retour du Nettoyage des sauvegardes (0=OK; 1=WARNING, 2=ERROR) : ${PRUNE_EXIT}" +# +######################################################################################## +# On teste le code retour de la sauvegarde, on log et on envoie des mails +case "${BACKUP_EXIT}" in + '0' ) + IFS='' + MESS_SAUVE_OK=" +Salut + +La sauvegarde est ok, ce message peut être enlevé avec la variable MAILOK=false +Que la force soit avec toi + +Ton esclave des sauvegardes" + LogFic " - la sauvegarde est OK" + [ "$MAILOK" = true ] && ExpMail ${MAILSOURCE} "Sauvegarde Ok" ${MAIL_RAPPORT} ${MESS_SAUVE_OK} + IFS=' ' + ;; + * ) + IFS='' + MESS_SAUVE_ERR=" +Salut + +La sauvegarde est en Erreur +Le log à consulter est ${SCRIPTLOG} +Code retour de la Sauvegarde ( code Rsync ): ${BACKUP_EXIT} + +Ton esclave des sauvegardes" + LogFic " - !!!!! Sauvegarde en Erreur !!!!!" + ExpMail ${MAILSOURCE} "!!!! Sauvegarde en Erreur !!!!" ${MAIL_RAPPORT} ${MESS_SAUVE_ERR} + IFS=' ' + ;; +esac +LogFic " - Fin de la sauvegarde" diff --git a/bin/secretGen.sh b/bin/secretGen.sh new file mode 100755 index 0000000..084c050 --- /dev/null +++ b/bin/secretGen.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +KAZ_ROOT=$(cd "$(dirname $0)/.."; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +cd "${KAZ_ROOT}" + +NEW_DIR="secret" +TMPL_DIR="secret.tmpl" + +if [ ! -d "${NEW_DIR}/" ]; then + rsync -a "${TMPL_DIR}/" "${NEW_DIR}/" +fi + +NEW_FILE="${NEW_DIR}/SetAllPass-new.sh" +TMPL_FILE="${NEW_DIR}/SetAllPass.sh" + +while read line ; do + if [[ "${line}" =~ ^# ]] || [ -z "${line}" ] ; then + echo "${line}" + continue + fi + if [[ "${line}" =~ "--clean_val--" ]] ; then + case "${line}" in + *jirafeau_DATA_DIR*) + JIRAFEAU_DIR=$(getValInFile "${DOCKERS_ENV}" "jirafeauDir") + [ -z "${JIRAFEAU_DIR}" ] && + echo "${line}" || + sed "s%\(.*\)--clean_val--\(.*\)%\1${JIRAFEAU_DIR}\2%" <<< ${line} + continue + ;; + *DATABASE*) + dbName="$(sed "s/\([^_]*\)_.*/\1/" <<< ${line})_$(apg -n 1 -m 2 -M NCL | cut -c 1-2)" + sed "s/\(.*\)--clean_val--\(.*\)/\1${dbName}\2/" <<< ${line} + continue + ;; + *ROOT_PASSWORD*|*PASSWORD*) + pass="$(apg -n 1 -m 16 -M NCL)" + sed "s/\(.*\)--clean_val--\(.*\)/\1${pass}\2/" <<< ${line} + continue + ;; + *USER*) + user="$(sed "s/\([^_]*\)_.*/\1/" <<< ${line})_$(apg -n 1 -m 2 -M NCL | cut -c 1-2)" + sed "s/\(.*\)--clean_val--\(.*\)/\1${user}\2/" <<< ${line} + continue + ;; + *RAIN_LOOP*|*office_password*|*mattermost_*|*sympa_*|*gitea_*) + pass="$(apg -n 1 -m 16 -M NCL)" + sed "s/\(.*\)--clean_val--\(.*\)/\1${pass}\2/" <<< ${line} + continue + ;; + *vaultwarden_ADMIN_TOKEN*) + pass="$(apg -n 1 -m 32 -M NCL)" + sed "s/\(.*\)--clean_val--\(.*\)/\1${pass}\2/" <<< ${line} + continue + ;; + esac + else + echo "${line}" + continue + fi + printKazError "${line}" >&2 +done < "${TMPL_FILE}" > "${NEW_FILE}" + +mv "${NEW_FILE}" "${TMPL_FILE}" + +chmod a+x "${TMPL_FILE}" +. "${TMPL_FILE}" +"${KAZ_BIN_DIR}/updateDockerPassword.sh" + +exit 0 diff --git a/bin/setOwner.sh b/bin/setOwner.sh new file mode 100755 index 0000000..828ee77 --- /dev/null +++ b/bin/setOwner.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +cd $(dirname $0)/.. +KAZ=$(pwd) +owner=root + +usage(){ + echo "Usage: $0 [root|user]" + exit 1 +} + +case $# in + 0) + ;; + 1) + owner=$1 + ;; + *) + usage + ;; +esac + +#################### +# config +cd ${KAZ} +DIRS="config secret bin" + +chown -hR ${owner}: ${DIRS} +find ${DIRS} -type f -exec chmod a-x {} \; +find ${DIRS} -type f -name \*.sh -exec chmod a+x {} \; +chmod -R a+X ${DIRS} +chmod -R go= ${DIRS} + +chmod a+x bin/*.sh +chown -hR www-data: config/orgaTmpl/wiki-conf/ + +#################### +# dockers +cd ${KAZ}/dockers + +chown -h ${owner}: . * */.env */* */config/* +chmod a-x,a+r * */* +chmod a+X . * */* +chmod a+x */*.sh + +chown -hR ${owner}: \ + etherpad/etherpad-lite/ \ + paheko/extensions paheko/paheko-* \ + jirafeau/Jirafeau \ + mattermost/app + +chown -hR www-data: \ + vigilo \ + web/html + +chmod -R a+rX web/html + + diff --git a/bin/updateAllOrga.sh b/bin/updateAllOrga.sh new file mode 100755 index 0000000..37c9fc0 --- /dev/null +++ b/bin/updateAllOrga.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +KAZ_ROOT=$(cd "$(dirname $0)/.."; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +cd "${KAZ_COMP_DIR}" +for orga in *-orga +do + ${orga}/orga-gen.sh + "${KAZ_ROOT}/bin/container.sh" stop "${orga}" + "${KAZ_ROOT}/bin/container.sh" start "${orga}" +done diff --git a/bin/updateDockerPassword.sh b/bin/updateDockerPassword.sh new file mode 100755 index 0000000..b5130c4 --- /dev/null +++ b/bin/updateDockerPassword.sh @@ -0,0 +1,121 @@ +#!/bin/bash + +KAZ_ROOT=$(cd $(dirname $0)/..; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +# pour mise au point +# SIMU=echo + +# Améliorations à prévoir +# - donner en paramètre les services concernés (pour limité les modifications) +# - pour les DB si on déclare un nouveau login, alors les privilèges sont créé mais les anciens pas révoqués + +. "${DOCKERS_ENV}" +. "${KAZ_KEY_DIR}/SetAllPass.sh" + +updateEnvDB(){ + # $1 = prefix + # $2 = envName + # $3 = containerName of DB + rootPass="$1_MYSQL_ROOT_PASSWORD" + dbName="$1_MYSQL_DATABASE" + userName="$1_MYSQL_USER" + userPass="$1_MYSQL_PASSWORD" + + ${SIMU} sed -i \ + -e "s/MYSQL_ROOT_PASSWORD=.*/MYSQL_ROOT_PASSWORD=${!rootPass}/g" \ + -e "s/MYSQL_DATABASE=.*/MYSQL_DATABASE=${!dbName}/g" \ + -e "s/MYSQL_USER=.*/MYSQL_USER=${!userName}/g" \ + -e "s/MYSQL_PASSWORD=.*/MYSQL_PASSWORD=${!userPass}/g" \ + "$2" + + # seulement si pas de mdp pour root + # pb oeuf et poule (il faudrait les anciennes valeurs) : + # * si rootPass change, faire à la main + # * si dbName change, faire à la main + checkDockerRunning "$3" "$3" || return + echo "change DB pass on docker $3" + echo "grant all privileges on ${!dbName}.* to '${!userName}' identified by '${!userPass}';" | \ + docker exec -i $3 bash -c "mysql --user=root --password=${!rootPass}" +} + +updateEnv(){ + # $1 = prefix + # $2 = envName + + for varName in $(grep "^[a-zA-Z_]*=" $2 | sed "s/^\([^=]*\)=.*/\1/g") + do + srcName="$1_${varName}" + srcVal=$(echo "${!srcName}" | sed -e "s/[&]/\\\&/g") + ${SIMU} sed -i \ + -e "s%^[ ]*${varName}=.*\$%${varName}=${srcVal}%" \ + "$2" + done +} + +framadateUpdate(){ + [[ "${COMP_ENABLE}" =~ " framadate " ]] || return + if [ ! -f "${DOCK_LIB}/volumes/framadate_dateConfig/_data/config.php" ]; then + return 0 + fi + checkDockerRunning "${framadateServName}" "Framadate" && + ${SIMU} docker exec -ti "${framadateServName}" bash -c -i "htpasswd -bc /var/framadate/admin/.htpasswd ${framadate_HTTPD_USER} ${framadate_HTTPD_PASSWORD}" + ${SIMU} sed -i \ + -e "s/^#*const DB_USER[ ]*=.*$/const DB_USER= '${framadate_MYSQL_USER}';/g" \ + -e "s/^#*const DB_PASSWORD[ ]*=.*$/const DB_PASSWORD= '${framadate_MYSQL_PASSWORD}';/g" \ + "${DOCK_LIB}/volumes/framadate_dateConfig/_data/config.php" +} + +jirafeauUpdate(){ + [[ "${COMP_ENABLE}" =~ " jirafeau " ]] || return + if [ ! -f "${DOCK_LIB}/volumes/jirafeau_fileConfig/_data/config.local.php" ]; then + return 0 + fi + SHA=$(echo -n "${jirafeau_HTTPD_PASSWORD}" | sha256sum | cut -d \ -f 1) + ${SIMU} sed -i \ + -e "s/'admin_password'[ ]*=>[ ]*'[^']*'/'admin_password' => '${SHA}'/g" \ + "${DOCK_LIB}/volumes/jirafeau_fileConfig/_data/config.local.php" +} + +#################### +# main + +updateEnvDB "etherpad" "${KAZ_KEY_DIR}/env-${etherpadDBName}" "${etherpadDBName}" +updateEnvDB "framadate" "${KAZ_KEY_DIR}/env-${framadateDBName}" "${framadateDBName}" +updateEnvDB "gitea" "${KAZ_KEY_DIR}/env-${gitDBName}" "${gitDBName}" +updateEnvDB "mattermost" "${KAZ_KEY_DIR}/env-${mattermostDBName}" "${mattermostDBName}" +updateEnvDB "nextcloud" "${KAZ_KEY_DIR}/env-${nextcloudDBName}" "${nextcloudDBName}" +updateEnvDB "roundcube" "${KAZ_KEY_DIR}/env-${roundcubeDBName}" "${roundcubeDBName}" +updateEnvDB "sympa" "${KAZ_KEY_DIR}/env-${sympaDBName}" "${sympaDBName}" +updateEnvDB "vigilo" "${KAZ_KEY_DIR}/env-${vigiloDBName}" "${vigiloDBName}" +updateEnvDB "wp" "${KAZ_KEY_DIR}/env-${wordpressDBName}" "${wordpressDBName}" +updateEnvDB "vaultwarden" "${KAZ_KEY_DIR}/env-${vaultwardenDBName}" "${vaultwardenDBName}" +updateEnvDB "castopod" "${KAZ_KEY_DIR}/env-${castopodDBName}" "${castopodDBName}" + +updateEnv "apikaz" "${KAZ_KEY_DIR}/env-${apikazServName}" +updateEnv "ethercalc" "${KAZ_KEY_DIR}/env-${ethercalcServName}" +updateEnv "etherpad" "${KAZ_KEY_DIR}/env-${etherpadServName}" +updateEnv "framadate" "${KAZ_KEY_DIR}/env-${framadateServName}" +updateEnv "gandi" "${KAZ_KEY_DIR}/env-gandi" +updateEnv "gitea" "${KAZ_KEY_DIR}/env-${gitServName}" +updateEnv "jirafeau" "${KAZ_KEY_DIR}/env-${jirafeauServName}" +updateEnv "mattermost" "${KAZ_KEY_DIR}/env-${mattermostServName}" +updateEnv "nextcloud" "${KAZ_KEY_DIR}/env-${nextcloudServName}" +updateEnv "office" "${KAZ_KEY_DIR}/env-${officeServName}" +updateEnv "roundcube" "${KAZ_KEY_DIR}/env-${roundcubeServName}" +updateEnv "vigilo" "${KAZ_KEY_DIR}/env-${vigiloServName}" +updateEnv "wp" "${KAZ_KEY_DIR}/env-${wordpressServName}" +updateEnv "ldap" "${KAZ_KEY_DIR}/env-${ldapServName}" +updateEnv "sympa" "${KAZ_KEY_DIR}/env-${sympaServName}" +updateEnv "mail" "${KAZ_KEY_DIR}/env-${smtpServName}" +updateEnv "mobilizon" "${KAZ_KEY_DIR}/env-${mobilizonServName}" +updateEnv "mobilizon" "${KAZ_KEY_DIR}/env-${mobilizonDBName}" +updateEnv "vaultwarden" "${KAZ_KEY_DIR}/env-${vaultwardenServName}" +updateEnv "castopod" "${KAZ_KEY_DIR}/env-${castopodServName}" +updateEnv "ldap" "${KAZ_KEY_DIR}/env-${ldapUIName}" + + +framadateUpdate +jirafeauUpdate +exit 0 diff --git a/bin/updateGit.sh b/bin/updateGit.sh new file mode 100755 index 0000000..e1dd1ce --- /dev/null +++ b/bin/updateGit.sh @@ -0,0 +1,447 @@ +#!/bin/bash + +# l'idée et de faire un rsync dans un répertoir provisoire et de téléverser les différences. + +# initialilisation : +# cd /MonRepDeTest +# mkdir -p kazdev kazprod +# rsync -rlptDEHAX --delete --info=progress2 root@kazdev:/kaz/ ./kazdev/ +# rsync -rlptDEHAX --delete --info=progress2 root@kazprod:/kaz/ ./kazprod/ + +# exemple : +# cd /MonRepDeTest/kazdev/ +# ./dockers/rdiff.sh /MonRepDeTest/kazprod/ root@kazprod +# cd /MonRepDeTest/kazprod/ +# ./dockers/rdiff.sh /MonRepDeTest/kazdev/ root@kazdev + +export KAZ_ROOT=$(cd "$(dirname $0)/.."; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +export REF_DIRS="bin config dockers secret.tmpl" +export SIMU="" + +usage () { + echo "Usage: $0 [-n] [-h]" + echo " -h help" + echo " -n simulation" + exit 1 +} + +for ARG in $@ +do + case "${ARG}" in + '-h' | '-help' ) + usage + ;; + '-n' ) + shift + export SIMU="echo" + ;; + esac +done + +if [[ $# -ne 0 ]]; then + echo "Illegal number of parameters" + usage +fi + +######################################## +# check system + +for prg in git ssh rsync kompare; do + if ! type "${prg}" > /dev/null; then + printKazError "$0 need ${prg}" + echo "please run \"apt-get install ${prg}\"" + exit + fi +done + + +######################################## +# config + +declare -a SKIP_FILE +export SKIP_FILE=$(grep -v -e ^# -e ^$ "${KAZ_CONF_DIR}/skip-file.txt") +KAZ_CFG_UPDATED="" +KAZ_UPDATE_ENV="${KAZ_CONF_DIR}/updateGit.conf" +if [ -f "${KAZ_UPDATE_ENV}" ]; then + . "${KAZ_UPDATE_ENV}" +else + KAZ_SRC_TYPE="VAGRANT" + KAZ_VAGRANT_ROOT="~/kaz-vagrant" + KAZ_DEV_REMOTE="root@kazdev" + KAZ_DEV_ROOT="/kaz" + KAZ_PROD_REMOTE="root@kazprod" + KAZ_PROD_ROOT="/kaz" + KAZ_OTHER_REMOTE="192.168.1.1" + KAZ_OTHER_ROOT="~/git/kaz" +fi + +while : ; do + read -p " Form which tested server ([Vagrant|DEV|PROD|OTHER]) you want updaye git KAZ? [${KAZ_SRC_TYPE}]: " rep + case "${rep}" in + "") + break + ;; + [vV]*) + KAZ_SRC_TYPE="VAGRANT" + KAZ_CFG_UPDATED="true" + break + ;; + [dD]*) + KAZ_SRC_TYPE="DEV" + KAZ_CFG_UPDATED="true" + break + ;; + [pP]*) + KAZ_SRC_TYPE="PROD" + KAZ_CFG_UPDATED="true" + break + ;; + [oO]*) + KAZ_SRC_TYPE="OTHER" + KAZ_CFG_UPDATED="true" + break + ;; + * ) + printKazError "\"${rep}\" not match with [Vagrant|DEV|PROD|OTHER]." + ;; + esac +done + +case "${KAZ_SRC_TYPE}" in + VAGRANT) + while : ; do + read -p " Give kaz-vagrant root? [${KAZ_VAGRANT_ROOT}]: " vagrantPath + if [ -z "${vagrantPath}" ]; then + vagrantPath="${KAZ_VAGRANT_ROOT}" + else + KAZ_CFG_UPDATED="true" + fi + if [ ! -d "${vagrantPath/#\~/${HOME}}" ]; then + printKazError "${vagrantPath} doesn't exist" + continue + fi + KAZ_VAGRANT_ROOT="${vagrantPath}" + KAZ_VAGRANT_PAHT="$(cd "${vagrantPath/#\~/${HOME}}" 2>/dev/null; pwd)" + (for sign in .git .vagrant; do + if [ ! -d "${KAZ_VAGRANT_PAHT}/${sign}" ]; then + printKazError "${KAZ_VAGRANT_PAHT} not contains ${sign}" + exit 1 + fi + done + exit 0 + ) && break; + done + ;; + DEV|PROD|OTHER) + case "${KAZ_SRC_TYPE}" in + DEV) + remoteUser="${KAZ_DEV_REMOTE}" + remotePath="${KAZ_DEV_ROOT}" + ;; + PROD) + remoteUser="${KAZ_PROD_REMOTE}" + remotePath="${KAZ_PROD_ROOT}" + ;; + OTHER) + remoteUser="${KAZ_OTHER_REMOTE}" + remotePath="${KAZ_OTHER_ROOT}" + ;; + esac + while : ; do + read -p "Give remote access? [${remoteUser}]: " rep + case "${rep}" in + "" ) + break + ;; + * ) + if [[ "${rep}" =~ ^([a-zA-Z0-9._%+-]+@)?[a-zA-Z0-9.-]+$ ]]; then + remoteUser="${rep}" + break + else + printKazError "${rep} not match with [user@server]" + fi + ;; + esac + done + while : ; do + read -p "Give remote path? [${remotePath}]: " rep + case "${rep}" in + "" ) + break + ;; + * ) + if [[ "${rep}" =~ ^~?[a-zA-Z0-9/._-]*/$ ]]; then + remotePath="${rep}" + break + else + printKazError "${rep} not match with [path]" + fi + ;; + esac + done + case "${KAZ_SRC_TYPE}" in + DEV) + if [ "${remoteUser}" != "${KAZ_DEV_REMOTE}" ]; then + KAZ_DEV_REMOTE="${remoteUser}"; KAZ_CFG_UPDATED="true" + fi + if [ "${remotePath}" != "${KAZ_DEV_ROOT}" ]; then + KAZ_DEV_ROOT="${remotePath}"; KAZ_CFG_UPDATED="true" + fi + ;; + PROD) + if [ "${remoteUser}" != "${KAZ_PROD_REMOTE}" ]; then + KAZ_PROD_REMOTE="${remoteUser}"; KAZ_CFG_UPDATED="true" + fi + if [ "${remotePath}" != "${KAZ_PROD_ROOT}" ]; then + KAZ_PROD_ROOT="${remotePath}"; KAZ_CFG_UPDATED="true" + fi + ;; + OTHER) + if [ "${remoteUser}" != "${KAZ_OTHER_REMOTE}" ]; then + KAZ_OTHER_REMOTE="${remoteUser}"; KAZ_CFG_UPDATED="true" + fi + if [ "${remotePath}" != "${KAZ_OTHER_ROOT}" ]; then + KAZ_OTHER_ROOT="${remotePath}"; KAZ_CFG_UPDATED="true" + fi + ;; + esac + ;; +esac + +if [ -n "${KAZ_CFG_UPDATED}" ]; then + printKazMsg "Update ${KAZ_UPDATE_ENV}" + cat > "${KAZ_UPDATE_ENV}" <&1 >/dev/null)"; do + printKazError "ssh key has changed" + echo "you must call :" + echo "${YELLOW} ssh-keygen -f ~/.ssh/known_hosts -R \"[127.0.0.1]:2222\"${NC}" + checkContinue + done + # XXX remote root + ${SIMU} rsync -rlptDEHAX --no-o --delete --info=progress2 \ + -e "ssh -p 2222 -i ${KAZ_VAGRANT_ROOT/#\~/${HOME}}/.vagrant/machines/default/virtualbox/private_key" \ + $(for i in ${REF_DIRS} git download ; do echo "vagrant@127.0.0.1:/kaz/$i" ; done) \ + "${TESTED_DIR}" + ;; + DEV|PROD|OTHER) + # remoteUser is already set + case "${KAZ_SRC_TYPE}" in + DEV) + remoteUser="${KAZ_DEV_REMOTE}"; remotePath="${KAZ_DEV_ROOT}" + ;; + PROD) + remoteUser="${KAZ_PROD_REMOTE}"; remotePath="${KAZ_PROD_ROOT}" + ;; + OTHER) + remoteUser="${KAZ_OTHER_REMOTE}"; remotePath="${KAZ_OTHER_ROOT}" + ;; + esac + + ${SIMU} rsync -rlptDEHAX --no-o --delete --info=progress2 \ + $(for i in ${REF_DIRS} ; do echo "${remoteUser}:${remotePath}$i" ; done) \ + "${TESTED_DIR}" + ;; +esac + + +cd "${TESTED_DIR}" + +badName(){ + [[ -z "$1" ]] && return 0 + for item in ${SKIP_FILE[@]}; do + [[ "$1/" =~ "${item}" ]] && return 0 + done + return 1 +} + + +declare -a CHANGED_DIRS +CHANGED_DIRS=$(find ${REF_DIRS} -type d ! -exec /bin/test -d "${KAZ_ROOT}/{}" \; -print -prune) +for file in ${CHANGED_DIRS[@]}; do + if badName "${file}" ; then + echo SKIP ${file} + continue + fi + printKazMsg "New dir ${file}" + while true; do + read -p "Synchronize ${GREEN}${file}/${NC} to ${GREEN}${KAZ_ROOT}/${file}/${NC}? [y/n/i/help]: " yn + case $yn in + [Yy]*) + ${SIMU} rsync -rlptDEHAX --info=progress2 "${file}/" "${KAZ_ROOT}/${file}/" + (cd "${KAZ_ROOT}" ; git add "${file}/" ) + break + ;; + ""|[Nn]*) + break + ;; + [Ii]*) + # add to skip + echo "${file}" >> "${KAZ_CONF_DIR}/skip-file.txt" + break + ;; + *) + echo -e \ + " yes: add all subdir ${file} in git\n" \ + " no: don't update git\n" \ + " ignore: never ask this question\n" \ + " help: print this help" + ;; + esac + done +done + +declare -a NEW_FILES +NEW_FILES=$(find ${REF_DIRS} '(' -type d ! -exec /bin/test -d "${KAZ_ROOT}/{}" \; -prune ')' -o '(' -type f ! -exec /bin/test -f "${KAZ_ROOT}/{}" \; -print ')') +for file in ${NEW_FILES[@]}; do + if badName "${file}" ; then + echo SKIP ${file} + continue + fi + echo "New file ${file}" + while true; do + read -p "Synchronize ${GREEN}${file}${NC} to ${GREEN}${KAZ_ROOT}/${file}${NC}? [y/n/i/help]: " yn + case $yn in + [Yy]*) + ${SIMU} rsync -rlptDEHAX --info=progress2 "${file}" "${KAZ_ROOT}/${file}" + (cd "${KAZ_ROOT}" ; git add "${file}" ) + break + ;; + [Nn]*) + break + ;; + [Ii]*) + # add to skip + echo "${file}" >> "${KAZ_CONF_DIR}/skip-file.txt" + break + ;; + *) + echo -e \ + " yes: add all subdir ${file} in git\n" \ + " no: don't update git\n" \ + " ignore: never ask this question\n" \ + " help: print this help" + ;; + esac + done +done + +trap 'rm -f "${TMPFILE}"' EXIT +export TMPFILE="$(mktemp)" || exit 1 + +CHANGED_FILES=$(find ${REF_DIRS} '(' -type d ! -exec /bin/test -d "${KAZ_ROOT}/{}" \; -prune ')' -o '(' -type f -exec /bin/test -f "${KAZ_ROOT}/{}" \; ! -exec cmp -s "{}" "${KAZ_ROOT}/{}" \; -print ')') +for file in ${CHANGED_FILES[@]} ; do + if badName "${file}" ; then + echo SKIP ${file} + continue + fi + echo "TEST ${file}" + kompare "${file}" "${KAZ_ROOT}/${file}" + if [ "${KAZ_ROOT}/${file}" -ot "${TMPFILE}" ]; then + echo "No change of ${KAZ_ROOT}/${file}" + continue + fi + chmod --reference="${file}" "${KAZ_ROOT}/${file}" +done + +echo +while : ; do + read -p "Do you want to keep ${TESTED_DIR} to speed up next download? [yes]" rep + case "${rep}" in + ""|[yYoO]* ) + break + ;; + [Nn]* ) + rm -r "${TESTED_DIR}" + ;; + * ) + echo "Please answer yes no." + ;; + esac +done + +cd "${KAZ_ROOT}" +echo -e "\nThe git will now commit in ${CURRENT_BRANCH}" +checkContinue +git commit -a + +echo -e "\nThe git will now pull in ${CURRENT_BRANCH}" +checkContinue +git pull + +printKazError "\nCheck if any confict" +#XXX check conflict + +echo -e "\nThe git will now push in ${CURRENT_BRANCH}" +checkContinue +git push + +printKazMsg "\nYou have to logged on ${KAZ_SRC_TYPE}, and launch:\n" +echo -e \ + " ssh root@host\n" \ + " cd /kaz\n" \ + " git reset --hard\n" \ + " git pull" + +printKazMsg "\nIf you want to promote in master branch:\n" \ +echo -e \ + " git checkout master\n" \ + " git pull\n" \ + " git merge develop\n" \ + "${RED}check if confict${NC}\n" \ + " git commit -a \n" \ + " git push\n" diff --git a/bin/updateLook.sh b/bin/updateLook.sh new file mode 100755 index 0000000..981a855 --- /dev/null +++ b/bin/updateLook.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +KAZ_ROOT=$(cd "$(dirname $0)"/..; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +cd "${KAZ_BIN_DIR}/look" + +THEMES=$(ls -F -- '.' | grep '/$' | sed 's%/%%' | tr '\n' '|' | sed 's%|$%%') + +usage () { + echo "usage $0 {${THEMES}}" + exit +} + +[ -z "$1" ] && usage +[ -d "$1" ] || usage + +cd $1 + +docker cp kaz-tete.png jirafeauServ:/var/jirafeau/media/kaz/kaz.png +docker cp kazdate.png framadateServ:/var/framadate/images/logo-framadate.png +docker cp kazmel.png roundcubeServ:/var/www/html/skins/elastic/images/kazmel.png +docker cp kaz-tete.png sympaServ:/usr/share/sympa/static_content/icons/logo_sympa.png +docker cp kaz-tete.png dokuwikiServ:/dokuwiki/lib/tpl/docnavwiki/images/logo.png +docker cp kaz-tete.png ldapUI:/var/www/html/images/ltb-logo.png +docker cp kaz-entier.svg webServ:/usr/share/nginx/html/images/logo.svg +docker cp kaz-signature.png webServ:/usr/share/nginx/html/m/logo.png + +for cloud in nextcloudServ kaz-nextcloudServ; do + docker cp kaz-entier.svg "${cloud}":/var/www/html/themes/kaz-entier.svg + docker cp kaz-tete.svg "${cloud}":/var/www/html/themes/kaz-tete.svg + docker exec -ti -u 33 "${cloud}" /var/www/html/occ theming:config logo /var/www/html/themes/kaz-tete.svg # tete + docker exec -ti -u 33 "${cloud}" /var/www/html/occ theming:config logoheader /var/www/html/themes/kaz-entier.svg # entier + # non #exec -ti -u 33 "${cloud}" /var/www/html/occ theming:config favicon /var/www/html/themes/kaz-patte.svg # patte +done diff --git a/bin/upgradeDockerCompose.sh b/bin/upgradeDockerCompose.sh new file mode 100755 index 0000000..d17fb17 --- /dev/null +++ b/bin/upgradeDockerCompose.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +OLDVERSION=$(docker-compose -v | sed -En 's/.*version ([a-z0-9\.]*).*/\1/p') +DOCKERCOMPOSE_VERSION="v2.17.3" + +if [ "$OLDVERSION" = "$DOCKERCOMPOSE_VERSION" ] +then + echo -e "Docker Compose déjà en version $DOCKERCOMPOSE_VERSION" + exit +fi + +curl -SL https://github.com/docker/compose/releases/download/$DOCKERCOMPOSE_VERSION/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose +chmod +x /usr/local/bin/docker-compose diff --git a/bin/verifExistenceMails.sh b/bin/verifExistenceMails.sh new file mode 100755 index 0000000..d87ff63 --- /dev/null +++ b/bin/verifExistenceMails.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +#Koi: on vérifie que chaque email possède son répertoire et vice et versa (on supprime sinon) +#Kan: 20/06/2022 +#Ki: fab + +#on récupère toutes les variables et mdp +# on prend comme source des repertoire le dossier du dessus ( /kaz dans notre cas ) +KAZ_ROOT=$(cd "$(dirname $0)"/..; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +cd $(dirname $0)/.. +. "${DOCKERS_ENV}" +. "${KAZ_KEY_DIR}/SetAllPass.sh" + +DOCK_DIR=$KAZ_COMP_DIR + +SETUP_MAIL="docker exec -ti mailServ setup" + +#on détermine le script appelant, le fichier log et le fichier source, tous issus de la même racine +PRG=$(basename $0) +RACINE=${PRG%.sh} + +# emails et les alias KAZ déjà créés +TFILE_EMAIL=$(mktemp /tmp/test_email.XXXXXXXXX.TFILE_EMAIL) + +#on stocke les emails et alias déjà créés +( + ${SETUP_MAIL} email list | cut -d ' ' -f 2 | grep @ + ${SETUP_MAIL} alias list | cut -d ' ' -f 2 | grep @ +) > ${TFILE_EMAIL} + + +#did on supprime le ^M en fin de fichier pour pas faire planter les grep +sed -i -e 's/\r//g' ${TFILE_EMAIL} + +rep_email="/var/lib/docker/volumes/postfix_mailData/_data" + +#étape n°1: pour chaque répertoire, on vérifie que l'email existe +echo "Début Etape n°1: on liste les répertoires des emails et on vérifie que les emails correspondant existent" + +ls -Ifilter -Itmp ${rep_email} | while read fin_email; do + ls ${rep_email}/${fin_email} | while read debut_email; do + email=`echo ${debut_email}@${fin_email}` + #est-ce que l'email existe ? + nb_ligne=$(grep "^${email}$" ${TFILE_EMAIL} | wc -l) + if [ ${nb_ligne} -gt 0 ];then + false + else + #suppression du répertoire + echo rm ${rep_email}/${fin_email}/${debut_email} -rf + fi + done + #si le répertoire domaine est vide, on le supprime + find ${rep_email}/${fin_email} -maxdepth 0 -type d -empty -delete +done +echo "aucune commande n'a été lancée, possible de le faire à la main" +echo "Fin Etape n°1" + +#Etape n°2: pour chaque email, on vérifie que le répertoire existe +echo "Début Etape n°2 n°2: on liste les emails et on vérifie que les répertoires correspondant existent" +cat ${TFILE_EMAIL} | while read email; do + debut_email=$(echo ${email} | awk -F '@' '{print $1}') + fin_email=$(echo ${email} | awk -F '@' '{print $2}') + if [ -d ${rep_email}/${fin_email}/${debut_email} ];then + true + else + echo "Attention, le répertoire ${fin_email}/${debut_email} n'existe pas alors que l'email existe!" + fi +done +echo "Fin Etape n°2" diff --git a/bin/vide_poubelle b/bin/vide_poubelle new file mode 100755 index 0000000..2f1acbf --- /dev/null +++ b/bin/vide_poubelle @@ -0,0 +1,11 @@ +#!/bin/sh + +cd "${HOME}/tmp/POUBELLE" >/dev/null 2>&1 + +if test "$?" -eq 0 +then + rm -f * .* 2>/dev/null +else + echo "$0 pas de poubelle a vider !" +fi + diff --git a/config/container-mail.list.tmpl b/config/container-mail.list.tmpl new file mode 100644 index 0000000..c70a440 --- /dev/null +++ b/config/container-mail.list.tmpl @@ -0,0 +1,4 @@ +# e-mail server composer +ldap +postfix +sympa diff --git a/config/container-orga.list.tmpl b/config/container-orga.list.tmpl new file mode 100644 index 0000000..799bea5 --- /dev/null +++ b/config/container-orga.list.tmpl @@ -0,0 +1 @@ +# orga composer diff --git a/config/container-proxy.list.tmpl b/config/container-proxy.list.tmpl new file mode 100644 index 0000000..1e85afe --- /dev/null +++ b/config/container-proxy.list.tmpl @@ -0,0 +1,2 @@ +proxy +#traefik diff --git a/config/container-withMail.list.tmpl b/config/container-withMail.list.tmpl new file mode 100644 index 0000000..6918d6e --- /dev/null +++ b/config/container-withMail.list.tmpl @@ -0,0 +1,12 @@ +cloud +dokuwiki +#framadate +paheko +gitea +jirafeau +mattermost +roundcube +mobilizon +vaultwarden +ldap +apikaz \ No newline at end of file diff --git a/config/container-withoutMail.list.tmpl b/config/container-withoutMail.list.tmpl new file mode 100644 index 0000000..13013ff --- /dev/null +++ b/config/container-withoutMail.list.tmpl @@ -0,0 +1,6 @@ +jirafeau +ethercalc +collabora +etherpad +web +imapsync diff --git a/config/dockers.tmpl.env b/config/dockers.tmpl.env new file mode 100644 index 0000000..9a02522 --- /dev/null +++ b/config/dockers.tmpl.env @@ -0,0 +1,153 @@ +# Les variables d'environnements utilisées +# par les dockers via le lien : +# .env -> ../../config/dockers.env + +####################################### +# prod / dev / local +mode= + +######################################## +# choix du domaine +# prod=kaz.bzh +domain= + +######################################## +# choix du domaine des mails sympa +# prod=listes.kaz.bzh +domain_sympa= + +######################################## +# Pour paheko qui met en "dur" dans +# sa config l'URL pour l'atteindre + +# prod=https +httpProto= + +# prod=89.234.186.111 +MAIN_IP= + +# prod=89.234.186.151 +SYMPA_IP= + +# prod1=prod1 +site=prod1 + +######################################## +# choix du domaine ldap +# prod dc=kaz,dc=bzh +ldap_root= + +######################################## +# devrait être dans env-jirafeauServ +# mais seuls les variables de ".env" sont +# utilisables pour le montage des volumes + +jirafeauDir= + +# idem, devrait être dans le env-castopodServ mais c'est utilisé directement dans le docker-compose.yml +castopodRedisPassword= + + +######################################## +# politique de redémarrage +# prod=always +restartPolicy= + +######################################## +# sites multiples +# prod=prod1 +site= + +######################################## +# URL de l'API ACME pour les certifs +# prod=https://acme-v02.api.letsencrypt.org/directory +acme_server= + + +######################################## +# noms des services + +# ou www (mais bof) +webHost= + +calcHost=tableur +cloudHost=cloud +dateHost=sondage +dokuwikiHost=wiki +fileHost=depot +pahekoHost=paheko +gitHost=git +gravHost=grav +matterHost=agora +officeHost=office +padHost=pad +smtpHost=smtp +ldapHost=ldap +ldapUIHost=mdp +sympaHost=listes +vigiloHost=vigilo +webmailHost=webmail +wordpressHost=wp +mobilizonHost=mobilizon +vaultwardenHost=koffre +traefikHost=dashboard +imapsyncHost=imapsync +castopodHost=pod +apikazHost=apikaz + +######################################## +# ports internes + +matterPort=8000 +imapsyncPort=8080 +apikaz=5000 + +######################################## +# noms des containers + +dokuwikiServName=dokuwikiServ +ethercalcServName=ethercalcServ +etherpadServName=etherpadServ +framadateServName=framadateServ +pahekoServName=pahekoServ +gitServName=gitServ +gravServName=gravServ +jirafeauServName=jirafeauServ +mattermostServName=mattermostServ +nextcloudServName=nextcloudServ +officeServName=officeServ +proxyServName=proxyServ +roundcubeServName=roundcubeServ +smtpServName=mailServ +ldapServName=ldapServ +sympaServName=sympaServ +vigiloServName=vigiloServ +webServName=webServ +wordpressServName=wpServ +mobilizonServName=mobilizonServ +vaultwardenServName=vaultwardenServ +traefikServName=traefikServ +prometheusServName=prometheusServ +grafanaServName=grafanaServ +ethercalcDBName=ethercalcDB +etherpadDBName=etherpadDB +framadateDBName=framadateDB +gitDBName=gitDB +mattermostDBName=mattermostDB +nextcloudDBName=nextcloudDB +roundcubeDBName=roundcubeDB +sympaDBName=sympaDB +vigiloDBName=vigiloDB +wordpressDBName=wpDB +mobilizonDBName=mobilizonDB +vaultwardenDBName=vaultwardenDB +ldapUIName=ldapUI +imapsyncServName=imapsyncServ +castopodDBName=castopodDB +castopodServName=castopodServ +apikazServName=apikazServ + +######################################## +# services activés par container.sh +# variables d'environneements utilisées +# pour le tmpl du mandataire (proxy) diff --git a/config/orgaTmpl/.env b/config/orgaTmpl/.env new file mode 120000 index 0000000..406acd1 --- /dev/null +++ b/config/orgaTmpl/.env @@ -0,0 +1 @@ +../../config/dockers.env \ No newline at end of file diff --git a/config/orgaTmpl/app/Dockerfile b/config/orgaTmpl/app/Dockerfile new file mode 100644 index 0000000..539d978 --- /dev/null +++ b/config/orgaTmpl/app/Dockerfile @@ -0,0 +1,58 @@ +FROM alpine:3.17 + +# Some ENV variables +ENV PATH="/mattermost/bin:${PATH}" +#ENV MM_VERSION=5.32.0 +ENV MM_VERSION=6.1.0 +ENV MM_INSTALL_TYPE=docker + +# Build argument to set Mattermost edition +ARG edition=enterprise +ARG PUID=2000 +ARG PGID=2000 +ARG MM_BINARY= + + +# Install some needed packages +RUN apk add --no-cache \ + ca-certificates \ + curl \ + jq \ + libc6-compat \ + libffi-dev \ + libcap \ + linux-headers \ + mailcap \ + netcat-openbsd \ + xmlsec-dev \ + tzdata \ + && rm -rf /tmp/* + +# Get Mattermost +RUN mkdir -p /mattermost/data /mattermost/plugins /mattermost/client/plugins \ + && if [ ! -z "$MM_BINARY" ]; then curl $MM_BINARY | tar -xvz ; \ + elif [ "$edition" = "team" ] ; then curl https://releases.mattermost.com/$MM_VERSION/mattermost-team-$MM_VERSION-linux-amd64.tar.gz?src=docker-app | tar -xvz ; \ + else curl https://releases.mattermost.com/$MM_VERSION/mattermost-$MM_VERSION-linux-amd64.tar.gz?src=docker-app | tar -xvz ; fi \ + && cp /mattermost/config/config.json /config.json.save \ + && rm -rf /mattermost/config/config.json \ + && addgroup -g ${PGID} mattermost \ + && adduser -D -u ${PUID} -G mattermost -h /mattermost -D mattermost \ + && chown -R mattermost:mattermost /mattermost /config.json.save /mattermost/plugins /mattermost/client/plugins \ + && setcap cap_net_bind_service=+ep /mattermost/bin/mattermost + +USER mattermost + +#Healthcheck to make sure container is ready +HEALTHCHECK CMD curl --fail http://localhost:8000 || exit 1 + +# Configure entrypoint and command +COPY entrypoint.sh / +ENTRYPOINT ["/entrypoint.sh"] +WORKDIR /mattermost +CMD ["mattermost"] + +# Expose port 8000 of the container +EXPOSE 8000 + +# Declare volumes for mount point directories +VOLUME ["/mattermost/data", "/mattermost/logs", "/mattermost/config", "/mattermost/plugins", "/mattermost/client/plugins"] diff --git a/config/orgaTmpl/app/entrypoint.sh b/config/orgaTmpl/app/entrypoint.sh new file mode 100755 index 0000000..f58bc71 --- /dev/null +++ b/config/orgaTmpl/app/entrypoint.sh @@ -0,0 +1,82 @@ +#!/bin/sh + +# Function to generate a random salt +generate_salt() { + tr -dc 'a-zA-Z0-9' < /dev/urandom | fold -w 48 | head -n 1 +} + +# Read environment variables or set default values +DB_HOST=${DB_HOST:-db} +DB_PORT_NUMBER=${DB_PORT_NUMBER:-5432} +# see https://www.postgresql.org/docs/current/libpq-ssl.html +# for usage when database connection requires encryption +# filenames should be escaped if they contain spaces +# i.e. $(printf %s ${MY_ENV_VAR:-''} | jq -s -R -r @uri) +# the location of the CA file can be set using environment var PGSSLROOTCERT +# the location of the CRL file can be set using PGSSLCRL +# The URL syntax for connection string does not support the parameters +# sslrootcert and sslcrl reliably, so use these PostgreSQL-specified variables +# to set names if using a location other than default +DB_USE_SSL=${DB_USE_SSL:-disable} +MM_DBNAME=${MM_DBNAME:-mattermost} +MM_CONFIG=${MM_CONFIG:-/mattermost/config/config.json} + +_1=$(echo "$1" | awk '{ s=substr($0, 0, 1); print s; }' ) +if [ "$_1" = '-' ]; then + set -- mattermost "$@" +fi + +if [ "$1" = 'mattermost' ]; then + # Check CLI args for a -config option + for ARG in "$@"; do + case "$ARG" in + -config=*) MM_CONFIG=${ARG#*=};; + esac + done + + if [ ! -f "$MM_CONFIG" ]; then + # If there is no configuration file, create it with some default values + echo "No configuration file $MM_CONFIG" + echo "Creating a new one" + # Copy default configuration file + cp /config.json.save "$MM_CONFIG" + # Substitute some parameters with jq + jq '.ServiceSettings.ListenAddress = ":8000"' "$MM_CONFIG" > "$MM_CONFIG.tmp" && mv "$MM_CONFIG.tmp" "$MM_CONFIG" + jq '.LogSettings.EnableConsole = true' "$MM_CONFIG" > "$MM_CONFIG.tmp" && mv "$MM_CONFIG.tmp" "$MM_CONFIG" + jq '.LogSettings.ConsoleLevel = "ERROR"' "$MM_CONFIG" > "$MM_CONFIG.tmp" && mv "$MM_CONFIG.tmp" "$MM_CONFIG" + jq '.FileSettings.Directory = "/mattermost/data/"' "$MM_CONFIG" > "$MM_CONFIG.tmp" && mv "$MM_CONFIG.tmp" "$MM_CONFIG" + jq '.FileSettings.EnablePublicLink = true' "$MM_CONFIG" > "$MM_CONFIG.tmp" && mv "$MM_CONFIG.tmp" "$MM_CONFIG" + jq ".FileSettings.PublicLinkSalt = \"$(generate_salt)\"" "$MM_CONFIG" > "$MM_CONFIG.tmp" && mv "$MM_CONFIG.tmp" "$MM_CONFIG" + jq '.EmailSettings.SendEmailNotifications = false' "$MM_CONFIG" > "$MM_CONFIG.tmp" && mv "$MM_CONFIG.tmp" "$MM_CONFIG" + jq '.EmailSettings.FeedbackEmail = ""' "$MM_CONFIG" > "$MM_CONFIG.tmp" && mv "$MM_CONFIG.tmp" "$MM_CONFIG" + jq '.EmailSettings.SMTPServer = ""' "$MM_CONFIG" > "$MM_CONFIG.tmp" && mv "$MM_CONFIG.tmp" "$MM_CONFIG" + jq '.EmailSettings.SMTPPort = ""' "$MM_CONFIG" > "$MM_CONFIG.tmp" && mv "$MM_CONFIG.tmp" "$MM_CONFIG" + jq ".EmailSettings.InviteSalt = \"$(generate_salt)\"" "$MM_CONFIG" > "$MM_CONFIG.tmp" && mv "$MM_CONFIG.tmp" "$MM_CONFIG" + jq ".EmailSettings.PasswordResetSalt = \"$(generate_salt)\"" "$MM_CONFIG" > "$MM_CONFIG.tmp" && mv "$MM_CONFIG.tmp" "$MM_CONFIG" + jq '.RateLimitSettings.Enable = true' "$MM_CONFIG" > "$MM_CONFIG.tmp" && mv "$MM_CONFIG.tmp" "$MM_CONFIG" + jq '.SqlSettings.DriverName = "postgres"' "$MM_CONFIG" > "$MM_CONFIG.tmp" && mv "$MM_CONFIG.tmp" "$MM_CONFIG" + jq ".SqlSettings.AtRestEncryptKey = \"$(generate_salt)\"" "$MM_CONFIG" > "$MM_CONFIG.tmp" && mv "$MM_CONFIG.tmp" "$MM_CONFIG" + jq '.PluginSettings.Directory = "/mattermost/plugins/"' "$MM_CONFIG" > "$MM_CONFIG.tmp" && mv "$MM_CONFIG.tmp" "$MM_CONFIG" + else + echo "Using existing config file $MM_CONFIG" + fi + + # Configure database access + if [ -z "$MM_SQLSETTINGS_DATASOURCE" ] && [ -n "$MM_USERNAME" ] && [ -n "$MM_PASSWORD" ]; then + echo "Configure database connection..." + # URLEncode the password, allowing for special characters + ENCODED_PASSWORD=$(printf %s "$MM_PASSWORD" | jq -s -R -r @uri) + export MM_SQLSETTINGS_DATASOURCE="postgres://$MM_USERNAME:$ENCODED_PASSWORD@$DB_HOST:$DB_PORT_NUMBER/$MM_DBNAME?sslmode=$DB_USE_SSL&connect_timeout=10" + echo "OK" + else + echo "Using existing database connection" + fi + + # Wait another second for the database to be properly started. + # Necessary to avoid "panic: Failed to open sql connection pq: the database system is starting up" + sleep 1 + + echo "Starting mattermost" +fi + +exec "$@" diff --git a/config/orgaTmpl/docker-compose.yml b/config/orgaTmpl/docker-compose.yml new file mode 100644 index 0000000..1c01e7b --- /dev/null +++ b/config/orgaTmpl/docker-compose.yml @@ -0,0 +1,307 @@ +version: '3.3' + +#{{services +services: +#}} +#{{db + db: + image: mariadb:10.5 + container_name: ${orga}DB + #disk_quota: 10G + command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW + restart: ${restartPolicy} + volumes: + - ./initdb.d:/docker-entrypoint-initdb.d:ro + - orgaDB:/var/lib/mysql + - /etc/localtime:/etc/localtime:ro + - /etc/timezone:/etc/timezone:ro + env_file: + - ../../secret/env-${nextcloudDBName} +# - ../../secret/env-${mattermostDBName} + - ../../secret/env-${wordpressDBName} + networks: + - orgaNet + healthcheck: # utilisé par init-db.sh pour la créa d'orga + test: ["CMD", 'mysqladmin', 'ping', '-h', 'localhost', '-u', 'root', '-p$$MYSQL_ROOT_PASSWORD' ] + interval: 30s + timeout: 30s + retries: 5 +#}} +#{{cloud + cloud: + image: nextcloud + container_name: ${orga}${nextcloudServName} + #disk_quota: 10G + restart: ${restartPolicy} + networks: + - orgaNet +# - postfixNet + depends_on: + - db + #db: + # condition: service_healthy # on peut mais ca va ralentir le process + links: + - db + external_links: + - ${smtpServName}:${smtpHost} + labels: + - "traefik.enable=true" + - "traefik.http.routers.${orga}${nextcloudServName}.rule=Host(`${orga}${cloudHost}.${domain}`){{FOREIGN_NC}}" + - "traefik.http.routers.${orga}${nextcloudServName}.middlewares=nextcloud-redirectregex1@file,nextcloud-redirectregex2@file" + volumes: + - cloudMain:/var/www/html + - cloudData:/var/www/html/data + - cloudConfig:/var/www/html/config + - cloudApps:/var/www/html/apps + - cloudCustomApps:/var/www/html/custom_apps + - cloudThemes:/var/www/html/themes/ + - cloudPhp:/usr/local/etc/php/conf.d/ + - /etc/localtime:/etc/localtime:ro + - /etc/timezone:/etc/timezone:ro + env_file: + - ../../secret/env-${nextcloudServName} + - ../../secret/env-${nextcloudDBName} + environment: + - NEXTCLOUD_TRUSTED_DOMAINS=${orga}${cloudHost}.${domain} + - SMTP_HOST=${smtpHost} + - SMTP_PORT=25 + - MAIL_DOMAIN=${domain} +#}} +#{{agora + agora: + build: + context: app + # uncomment following lines for team edition or change UID/GID + args: + - edition=team + - PUID=1000 + - PGID=1000 + container_name: ${orga}${mattermostServName} + #disk_quota: 10G + restart: ${restartPolicy} + # memory: 1G + networks: + - orgaNet +# - postfixNet + expose: + - ${matterPort} + depends_on: + - db + #db: + # condition: service_healthy # on peut mais ca va ralentir le process + links: + - db + external_links: + - ${smtpServName}:${smtpHost}.${domain} + volumes: + - matterConfig:/mattermost/config:rw + - matterData:/mattermost/data:rw + - matterLogs:/mattermost/logs:rw + - matterPlugins:/mattermost/plugins:rw + - matterClientPlugins:/mattermost/client/plugins:rw + - matterIcons:/mattermost/client/images:ro + - /etc/ssl:/etc/ssl:ro + - /etc/localtime:/etc/localtime:ro + - /etc/timezone:/etc/timezone:ro + - /etc/environment:/etc/environment:ro + env_file: + - ../../secret/env-${mattermostServName} + environment: + - VIRTUAL_HOST=${orga}${matterHost}.${domain} + # in case your config is not in default location + #- MM_CONFIG=/mattermost/config/config.json + + labels: + - "traefik.enable=true" + - "traefik.http.routers.${orga}${mattermostServName}.rule=Host(`${orga}${matterHost}.${domain}`)" +#}} +#{{wp + wordpress: + image: wordpress + container_name: ${orga}${wordpressServName} + restart: ${restartPolicy} + networks: + - orgaNet +# - postfixNet + depends_on: + - db + #db: + # condition: service_healthy # on peut mais ca va ralentir le process + links: + - db + external_links: + - ${smtpServName}:${smtpHost}.${domain} + env_file: + - ../../secret/env-${wordpressServName} + environment: + - WORDPRESS_SMTP_HOST=${smtpHost}.${domain} + - WORDPRESS_SMTP_PORT=25 + # - WORDPRESS_SMTP_USERNAME + # - WORDPRESS_SMTP_PASSWORD + # - WORDPRESS_SMTP_FROM=${orga} + - WORDPRESS_SMTP_FROM_NAME=${orga} + labels: + - "traefik.enable=true" + - "traefik.http.routers.${orga}${wordpressServName}.rule=Host(`${orga}${wordpressHost}.${domain}`){{FOREIGN_WP}}" + volumes: + - wordpress:/var/www/html +# - ../../config/orgaTmpl/wp:/usr/local/bin/wp:ro +#}} +#{{wiki + dokuwiki: + image: mprasil/dokuwiki + container_name: ${orga}${dokuwikiServName} + #disk_quota: 10G + restart: ${restartPolicy} + labels: + - "traefik.enable=true" + - "traefik.http.routers.${orga}${dokuwikiServName}.rule=Host(`${orga}${dokuwikiHost}.${domain}`){{FOREIGN_DW}}" + volumes: + - wikiData:/dokuwiki/data + - wikiConf:/dokuwiki/conf + - wikiPlugins:/dokuwiki/lib/plugins + - wikiLibtpl:/dokuwiki/lib/tpl + - wikiLogs:/var/log + networks: + - orgaNet +# - postfixNet + external_links: + - ${smtpServName}:${smtpHost}.${domain} +#}} +#{{castopod + castopod: + image: castopod/castopod:latest + container_name: ${orga}${castopodServName} + #disk_quota: 10G + restart: ${restartPolicy} + # memory: 1G + networks: + - orgaNet +# - postfixNet + expose: + - 8000 + depends_on: + - db + links: + - db + external_links: + - ${smtpServName}:${smtpHost}.${domain} + volumes: + - castopodMedia:/var/www/castopod/public/media + environment: + CP_BASEURL: "https://${orga}${castopodHost}.${domain}" + CP_ANALYTICS_SALT: qldsgfliuzrbhgmkjbdbmkvb + VIRTUAL_PORT: 8000 + CP_CACHE_HANDLER: redis + CP_REDIS_HOST: redis + CP_DATABASE_HOSTNAME: db + env_file: + - ../../secret/env-${castopodServName} + - ../../secret/env-${castopodDBName} + labels: + - "traefik.enable=true" + - "traefik.http.routers.${orga}${castopodServName}.rule=Host(`${orga}${castopodHost}.${domain}`){{FOREIGN_POD}}" + redis: + image: redis:7.0-alpine + container_name: ${orga}castopodCache + volumes: + - castopodCache:/data + networks: + - orgaNet + env_file: + - ../../secret/env-${castopodServName} + command: --requirepass ${castopodRedisPassword} +#}} + + + +#{{services +volumes: +#}} +#{{db + orgaDB: + external: true + name: orga_${orga}orgaDB +#}} +#{{agora + matterConfig: + external: true + name: orga_${orga}matterConfig + matterData: + external: true + name: orga_${orga}matterData + matterLogs: + external: true + name: orga_${orga}matterLogs + matterPlugins: + external: true + name: orga_${orga}matterPlugins + matterClientPlugins: + external: true + name: orga_${orga}matterClientPlugins + matterIcons: + external: true + name: matterIcons +#{{cloud + cloudMain: + external: true + name: orga_${orga}cloudMain + cloudData: + external: true + name: orga_${orga}cloudData + cloudConfig: + external: true + name: orga_${orga}cloudConfig + cloudApps: + external: true + name: orga_${orga}cloudApps + cloudCustomApps: + external: true + name: orga_${orga}cloudCustomApps + cloudThemes: + external: true + name: orga_${orga}cloudThemes + cloudPhp: + external: true + name: orga_${orga}cloudPhp +#}} +#{{wiki + wikiData: + external: true + name: orga_${orga}wikiData + wikiConf: + external: true + name: orga_${orga}wikiConf + wikiPlugins: + external: true + name: orga_${orga}wikiPlugins + wikiLibtpl: + external: true + name: orga_${orga}wikiLibtpl + wikiLogs: + external: true + name: orga_${orga}wikiLogs +#}} +#{{wp + wordpress: + external: true + name: orga_${orga}wordpress +#}} +#{{castopod + castopodMedia: + external: true + name: orga_${orga}castopodMedia + castopodCache: + external: true + name: orga_${orga}castopodCache + +#}} + + +networks: + orgaNet: + external: true + name: ${orga}orgaNet + # postfixNet: + # external: + # name: postfixNet diff --git a/config/orgaTmpl/init-db.sh b/config/orgaTmpl/init-db.sh new file mode 100755 index 0000000..944d451 --- /dev/null +++ b/config/orgaTmpl/init-db.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +KAZ_ROOT=$(cd $(dirname $0)/../..; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars +. "${DOCKERS_ENV}" +. "${KAZ_KEY_DIR}/SetAllPass.sh" + +cd $(dirname $0) +ORGA_DIR="$(basename "$(pwd)")" + +ORGA=${ORGA_DIR%-orga} +if [[ -z "${ORGA}" ]] +then + printKazError "it's not an orga dir" + exit +fi + + +waitContainerHealthy "${ORGA}-DB" 180 +[ $? -ne 0 ] && printKazError "\n La base de donnée démarre pas : impossible de terminer l'install" && exit + +SQL="" + +for ARG in "$@"; do + case "${ARG}" in + 'cloud' ) + SQL="$SQL +CREATE DATABASE IF NOT EXISTS ${nextcloud_MYSQL_DATABASE}; + +DROP USER IF EXISTS '${nextcloud_MYSQL_USER}'; +CREATE USER '${nextcloud_MYSQL_USER}'@'%'; + +GRANT ALL ON ${nextcloud_MYSQL_DATABASE}.* TO '${nextcloud_MYSQL_USER}'@'%' IDENTIFIED BY '${nextcloud_MYSQL_PASSWORD}'; + +FLUSH PRIVILEGES;" + ;; + 'agora' ) + SQL="$SQL +CREATE DATABASE IF NOT EXISTS ${mattermost_MYSQL_DATABASE}; + +DROP USER IF EXISTS '${mattermost_MYSQL_USER}'; +CREATE USER '${mattermost_MYSQL_USER}'@'%'; + +GRANT ALL ON ${mattermost_MYSQL_DATABASE}.* TO '${mattermost_MYSQL_USER}'@'%' IDENTIFIED BY '${mattermost_MYSQL_PASSWORD}'; + +FLUSH PRIVILEGES;" + ;; + 'wp' ) + SQL="$SQL +CREATE DATABASE IF NOT EXISTS ${wp_MYSQL_DATABASE}; + +DROP USER IF EXISTS '${wp_MYSQL_USER}'; +CREATE USER '${wp_MYSQL_USER}'@'%'; + +GRANT ALL ON ${wp_MYSQL_DATABASE}.* TO '${wp_MYSQL_USER}'@'%' IDENTIFIED BY '${wp_MYSQL_PASSWORD}'; + +FLUSH PRIVILEGES;" + ;; + 'castopod' ) + SQL="$SQL +CREATE DATABASE IF NOT EXISTS ${castopod_MYSQL_DATABASE}; + +DROP USER IF EXISTS '${castopod_MYSQL_USER}'; +CREATE USER '${castopod_MYSQL_USER}'@'%'; + +GRANT ALL ON ${castopod_MYSQL_DATABASE}.* TO '${castopod_MYSQL_USER}'@'%' IDENTIFIED BY '${castopod_MYSQL_PASSWORD}'; + +FLUSH PRIVILEGES;" + ;; + + esac +done + +echo $SQL | docker exec -i ${ORGA}-DB bash -c "mysql --user=root --password=${wp_MYSQL_ROOT_PASSWORD}" diff --git a/config/orgaTmpl/init-paheko.sh b/config/orgaTmpl/init-paheko.sh new file mode 100755 index 0000000..d9ca734 --- /dev/null +++ b/config/orgaTmpl/init-paheko.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +KAZ_ROOT=$(cd $(dirname $0)/../..; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +. "${DOCKERS_ENV}" + +cd $(dirname $0) +PWD=$(pwd) +ORGA_DIR=$(basename ${PWD}) + +if [[ "${ORGA_DIR}" != *"-orga" ]] +then + printKazError "it's not an orga dir" + exit +fi +ORGA=${ORGA_DIR%-orga} +if [[ -z "${ORGA}" ]] +then + printKazError "it's not an orga dir" + exit +fi + +printKazMsg "init paheko ${ORGA}" + +#${KAZ_COMP_DIR}/paheko/installPlugins.sh ${ORGA} diff --git a/config/orgaTmpl/init-volume.sh b/config/orgaTmpl/init-volume.sh new file mode 100755 index 0000000..0c6cdb9 --- /dev/null +++ b/config/orgaTmpl/init-volume.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +#docker network create postfix_mailNet + +#{{db +docker volume create --name=orga_${orga}orgaDB +#}} +#{{agora +docker volume create --name=orga_${orga}matterConfig +docker volume create --name=orga_${orga}matterData +docker volume create --name=orga_${orga}matterLogs +docker volume create --name=orga_${orga}matterPlugins +docker volume create --name=orga_${orga}matterClientPlugins +docker volume create --name=matterIcons +#}} +#{{cloud +docker volume create --name=orga_${orga}cloudMain +docker volume create --name=orga_${orga}cloudData +docker volume create --name=orga_${orga}cloudConfig +docker volume create --name=orga_${orga}cloudApps +docker volume create --name=orga_${orga}cloudCustomApps +docker volume create --name=orga_${orga}cloudThemes +docker volume create --name=orga_${orga}cloudPhp +chown 33:33 /var/lib/docker/volumes/orga_${orga}cloud*/_data +#}} +#{{wiki +docker volume create --name=orga_${orga}wikiData +docker volume create --name=orga_${orga}wikiConf +docker volume create --name=orga_${orga}wikiPlugins +docker volume create --name=orga_${orga}wikiLibtpl +docker volume create --name=orga_${orga}wikiLogs +#}} +#{{wp +docker volume create --name=orga_${orga}wordpress +#}} +#{{castopod +docker volume create --name=orga_${orga}castopodCache +docker volume create --name=orga_${orga}castopodMedia +#}} diff --git a/config/orgaTmpl/initdb.d/orga.sql b/config/orgaTmpl/initdb.d/orga.sql new file mode 100644 index 0000000..6fc5ea0 --- /dev/null +++ b/config/orgaTmpl/initdb.d/orga.sql @@ -0,0 +1,3 @@ +CREATE DATABASE IF NOT EXISTS nextcloud; +CREATE DATABASE IF NOT EXISTS mattermost; +CREATE DATABASE IF NOT EXISTS wpdb; diff --git a/config/orgaTmpl/orga-gen.sh b/config/orgaTmpl/orga-gen.sh new file mode 100755 index 0000000..9f3e85a --- /dev/null +++ b/config/orgaTmpl/orga-gen.sh @@ -0,0 +1,486 @@ +#!/bin/bash + +# XXX pb arret des services retiré + +PRG=$(basename $0) +KAZ_ROOT=$(cd "$(dirname $0)/../.."; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +cd $(dirname $0) +ORGA_DIR="$(basename "$(pwd)")" +TIMESTAMP=YES +TMPL_PROXY_COMPOSE="${KAZ_COMP_DIR}/proxy/docker-compose.tmpl.yml" +TMPL_TRAEFIK_COMPOSE="${KAZ_COMP_DIR}/traefik/docker-compose.tmpl.yml" +ORGA_LIST="${KAZ_CONF_DIR}/container-orga.list" +LIST="" + +STAGE_DEFAULT=true +STAGE_CREATE= +STAGE_INIT= + +usage(){ + echo "Usage: $0 [-h] [-l] [+/-paheko] [-/+cloud [-/+collabora}]] [+/-agora] [+/-wiki] [+/-wp] [+/-pod] [x{G/M/k}] OrgaName" + echo " -h|--help : this help" + echo " -l|--list : list service" + + echo " --create : only create (before start)" + echo " --init : only init (after start)" + + echo " +/- paheko : on/off paheko" + echo " +/- cloud : on/off cloud" + echo " +/- coll* : on/off collabora" + echo " +/- matter*|agora : on/off agora" + echo " +/- wiki : on/off wiki" + echo " +/- wp|word* : on/off wp" + echo " +/- casto*|pod : on/off castopod" + echo " x[GMk] : set quota" + echo " OrgaName : name must contain a-z0-9_\-" +} + +for ARG in "$@"; do + case "${ARG}" in + '-h' | '--help' ) + usage + exit 0;; + '-l' | '--list' ) + ;; + '--create' ) + STAGE_DEFAULT= + STAGE_CREATE=true + if [[ -n "${STAGE_INIT}" ]]; then + usage + echo " Incompatible options (--create --init)" + exit 1 + fi + ;; + '--init' ) + STAGE_DEFAULT= + STAGE_INIT=true + if [[ -n "${STAGE_CREATE}" ]]; then + usage + echo " Incompatible options (--create --init)" + exit 1 + fi + ;; + '-'* ) + ;; + '+'* ) + ;; + [.0-9]*[GMk] ) + ;; + * ) + if [[ "${ORGA_DIR}" = "orgaTmpl" ]]; then + if [[ "${ARG}" =~ ^[a-z0-9_\-]+$ ]]; then + printKazMsg "create ${ARG}" + ORGA_PATH="${KAZ_COMP_DIR}/${ARG}-orga" + mkdir -p "${ORGA_PATH}" + cd "${ORGA_PATH}" + ORGA_DIR="$(basename "$(pwd)")" + ln -sf ../../config/dockers.env .env + ln -sf ../../config/orgaTmpl/orga-gen.sh + ln -sf ../../config/orgaTmpl/orga-rm.sh + ln -sf ../../config/orgaTmpl/reload.sh + else + printKazError "Name must contains only a-z0-9_\-" + usage + exit 1 + fi + else + if [[ "${ARG}-orga" != "${ORGA_DIR}" ]]; then + printKazError "Can't cross config ${ARG}-orga with ${ORGA_DIR}" + usage + exit 1 + fi + fi + ;; + esac +done + +if [[ "${ORGA_DIR}" = "orgaTmpl" ]] +then + printKazError "You must give orga name" + usage + exit 1 +fi + +if [[ "${ORGA_DIR}" != *"-orga" ]] +then + printKazError "it's not an orga dir" + exit +fi +ORGA=${ORGA_DIR%-orga} + +if [[ -z "${ORGA}" ]] +then + printKazError "it's not an orga dir" + exit +fi + +# default values +flagInCompose(){ + # $1 filename (docker-compose.yml) + # $2 service name + # $3 default value + if [[ ! -f "$1" ]] + then + echo "$3" + else + if grep -q "$2" docker-compose.yml + then + echo on + else + echo off + fi + fi +} + +export cloud=$(flagInCompose docker-compose.yml cloud: off) +export collabora=$(flagInCompose docker-compose.yml collabora: off) +export agora=$(flagInCompose docker-compose.yml agora: off) +export wiki=$(flagInCompose docker-compose.yml dokuwiki: off) +export wp=$(flagInCompose docker-compose.yml wordpress: off) +export castopod=$(flagInCompose docker-compose.yml castopod: off) +export db="off" +export services="off" +export paheko=$([[ -f usePaheko ]] && echo "on" || echo "off") +export quota=10G +if [[ -f docker-compose.yml ]]; then + if grep -q "storage_opt" docker-compose.yml + then + quota=$(grep "storage_opt" docker-compose.yml | cut -d : -f 2 | tail -n 1) + fi +fi + +DBaInitialiser="" +INITCMD2="--install" + + +for ARG in "$@"; do + case "${ARG}" in + '-show' ) + for i in cloud collabora agora wiki wp castopod db; do + echo "${i}=${!i}" + done + exit;; + '-h' | '--help' ) + usage + exit 1 + ;; + '-l' | '--list' ) + LIST="list" + ;; + '-time'* ) + TIMESTAMP=YES + ;; + '-paheko'* ) + paheko="off" + ;; + '-cloud' ) + cloud="off" + collabora="off" + ;; + '-coll'* | '-offi'* ) + collabora="off" + ;; + '-matter'* | '-agora') + agora="off" + ;; + '-wiki' ) + wiki="off" + ;; + '-wp' | '-word'* ) + wp="off" + ;; + '-pod' | '-casto'* ) + castopod="off" + ;; + '+paheko' ) + paheko="on" + ;; + '+cloud' ) + cloud="on" + DBaInitialiser="$DBaInitialiser cloud" + INITCMD2="$INITCMD2 -cloud" + ;; + '+coll'* | '+offi'* ) + collabora="on" + INITCMD2="$INITCMD2 -office" + ;; + '+matter'* | '+agora' ) + agora="on" + DBaInitialiser="$DBaInitialiser agora" + INITCMD2="$INITCMD2 -agora" + ;; + '+wiki' ) + wiki="on" + ;; + '+wp' | '+word'* ) + wp="on" + DBaInitialiser="$DBaInitialiser wp" + INITCMD2="$INITCMD2 -wp" + ;; + '+pod' | '+casto'* ) + castopod="on" + DBaInitialiser="$DBaInitialiser castopod" + INITCMD2="$INITCMD2 -pod" + ;; + [.0-9]*[GMk] ) + quota="${ARG}" + ;; + esac +done + +if [ "${cloud}" = "on" -o "${agora}" = "on" -o "${wp}" = "on" -o "${castopod}" = "on" ] +then + db="on" +fi +if [ "${db}" = "on" -o "${wiki}" = "on" ] +then + services="on" +fi + +. "${DOCKERS_ENV}" +ORGA_FLAG=${ORGA_DIR//-/_} +DOMAIN_AREA="{{${ORGA_FLAG}\n" +ADD_DOMAIN="" +DEL_DOMAIN="" + +listServ () { + for serv in $(getAvailableServices); do + if [[ "${!serv}" == "on" ]]; then + echo "${serv}" + fi + done +} + +if [[ -n "${LIST}" ]] ; then + listServ + exit +fi + +if [[ "${paheko}" = "on" ]]; then + touch usePaheko + ADD_DOMAIN+="${ORGA}-${pahekoHost} " +else + rm -f usePaheko + DEL_DOMAIN+="${ORGA}-${pahekoHost} " +fi +if [[ "${cloud}" = "on" ]]; then + DOMAIN_AREA+=" - ${ORGA}-\${nextcloudServName}:${ORGA}-\${cloudHost}.\${domain}\n" + ADD_DOMAIN+="${ORGA}-${cloudHost} " +else + DEL_DOMAIN+="${ORGA}-${cloudHost} " +fi +if [[ "${collabora}" = "on" ]]; then + DOMAIN_AREA+=" - ${ORGA}-\${officeServName}:${ORGA}-\${officeHost}.\${domain}\n" + ADD_DOMAIN+="${ORGA}-${officeHost} " +else + DEL_DOMAIN+="${ORGA}-${officeHost} " +fi +if [[ "${agora}" = "on" ]]; then + DOMAIN_AREA+=" - ${ORGA}-\${mattermostServName}:${ORGA}-\${matterHost}.\${domain}\n" + ADD_DOMAIN+="${ORGA}-${matterHost} " +else + DEL_DOMAIN+="${ORGA}-${matterHost} " +fi +if [[ "${wiki}" = "on" ]]; then + DOMAIN_AREA+=" - ${ORGA}-\${dokuwikiServName}:${ORGA}-\${dokuwikiHost}.\${domain}\n" + ADD_DOMAIN+="${ORGA}-${dokuwikiHost} " +else + DEL_DOMAIN+="${ORGA}-${dokuwikiHost} " +fi +if [[ "${wp}" = "on" ]]; then + DOMAIN_AREA+=" - ${ORGA}-\${wordpressServName}:${ORGA}-\${wordpressHost}.\${domain}\n" + ADD_DOMAIN+="${ORGA}-${wordpressHost} " +else + DEL_DOMAIN+="${ORGA}-${wordpressHost} " +fi +if [[ "${castopod}" = "on" ]]; then + DOMAIN_AREA+=" - ${ORGA}-\${castopodServName}:${ORGA}-\${castopodHost}.\${domain}\n" + ADD_DOMAIN+="${ORGA}-${castopodHost} " +else + DEL_DOMAIN+="${ORGA}-${castopodHost} " +fi +DOMAIN_AREA+="}}\n" + +if [[ -n "${STAGE_DEFAULT}${STAGE_CREATE}" ]]; then + if [[ -f "${TMPL_TRAEFIK_COMPOSE}" ]]; then + # ########## update traefik/docker-compose.tmpl.yml + printKazMsg "Update ${TMPL_TRAEFIK_COMPOSE}" + if grep -q "^{{${ORGA_FLAG}" "${TMPL_TRAEFIK_COMPOSE}" 2> /dev/null ; then + sed -i -e "/^{{${ORGA_FLAG}/,/^}}/d" "${TMPL_TRAEFIK_COMPOSE}" + fi + # use net + sed "s/^#### END ORGA USE_NET/{{${ORGA_FLAG}\n - ${ORGA}Net\n}}\n#### END ORGA USE_NET/" -i "${TMPL_TRAEFIK_COMPOSE}" + # def net + sed "s/^#### END ORGA DEF_NET/{{${ORGA_FLAG}\n ${ORGA}Net:\n external: true\n name: ${ORGA}-orgaNet\n}}\n#### END ORGA DEF_NET/" -i "${TMPL_TRAEFIK_COMPOSE}" + fi + if [[ -f "${TMPL_PROXY_COMPOSE}" ]]; then + # ########## update proxy/docker-compose.tmpl.yml + printKazMsg "Update ${TMPL_PROXY_COMPOSE}" + if grep -q "^{{${ORGA_FLAG}" "${TMPL_PROXY_COMPOSE}" 2> /dev/null ; then + sed -i -e "/^{{${ORGA_FLAG}/,/^}}/d" "${TMPL_PROXY_COMPOSE}" + fi + # domaine + sed "s/^#### END ORGA HOST/${DOMAIN_AREA}#### END ORGA HOST/" -i "${TMPL_PROXY_COMPOSE}" + # use net + sed "s/^#### END ORGA USE_NET/{{${ORGA_FLAG}\n - ${ORGA}Net\n}}\n#### END ORGA USE_NET/" -i "${TMPL_PROXY_COMPOSE}" + # def net + sed "s/^#### END ORGA DEF_NET/{{${ORGA_FLAG}\n ${ORGA}Net:\n external: true\n name: ${ORGA}-orgaNet\n}}\n#### END ORGA DEF_NET/" -i "${TMPL_PROXY_COMPOSE}" + fi +fi + +if [[ -n "${STAGE_DEFAULT}${STAGE_CREATE}" ]]; then + # ########## update DNS + printKazMsg "Update DNS" + ${KAZ_BIN_DIR}/dns.sh add ${ADD_DOMAIN} + ${KAZ_BIN_DIR}/dns.sh del ${DEL_DOMAIN} +fi + +update() { + ( + # $1 = template + # $2 = target + if [ "${TIMESTAMP}" == "YES" ]; then + echo "# Generated by $(pwd)$(basename $0)" + echo "# à partir du modèle $1" + echo "#" $(date "+%x %X") + echo + fi + FOREIGN_WP=$(grep " ${ORGA};" "${KAZ_CONF_PROXY_DIR}/wp_kaz_map" 2>/dev/null | \ + sed "s/\([^ ]*\) ${ORGA};/ \|\| Host(\`\1\`)/" | tr -d "\r\n") + FOREIGN_NC=$(grep " ${ORGA};" "${KAZ_CONF_PROXY_DIR}/cloud_kaz_map" 2>/dev/null | \ + sed "s/\([^ ]*\) ${ORGA};/ \|\| Host(\`\1\`)/" | tr -d "\r\n") + FOREIGN_DW=$(grep " ${ORGA};" "${KAZ_CONF_PROXY_DIR}/wiki_kaz_map" 2>/dev/null | \ + sed "s/\([^ ]*\) ${ORGA};/ \|\| Host(\`\1\`)/" | tr -d "\r\n") + FOREIGN_POD=$(grep " ${ORGA};" "${KAZ_CONF_PROXY_DIR}/pod_kaz_map" 2>/dev/null | \ + sed "s/\([^ ]*\) ${ORGA};/ \|\| Host(\`\1\`)/" | tr -d "\r\n") + awk ' + BEGIN {cp=1} + /#}}/ {cp=1 ; next}; + /#{{on/ {cp=1; next}; + /#{{off/ {cp=0; next}; + match($0, /#{{[a-zA-Z0-9_]+/) {cp=(ENVIRON[substr($0,RSTART+3,RLENGTH)] == "on"); next}; + {if (cp) print $0};' $1 | sed \ + -e "/^[ \t]*$/d"\ + -e "/^[ ]*#.*$/d"\ + -e "s/{{FOREIGN_WP}}/${FOREIGN_WP}/"\ + -e "s/{{FOREIGN_NC}}/${FOREIGN_NC}/"\ + -e "s/{{FOREIGN_DW}}/${FOREIGN_DW}/"\ + -e "s/{{FOREIGN_POD}}/${FOREIGN_POD}/"\ + -e "s|\${orga}|${ORGA}-|g" + ) > "$2" + sed "s/storage_opt:.*/storage_opt: ${quota}/g" -i "$2" +} + +if [[ -n "${STAGE_DEFAULT}${STAGE_CREATE}" ]]; then + # ########## update ${ORGA_DIR}/docker-compose.tmpl.yml + printKazMsg "update docker-compose.yml ${ORGA}" + update ${KAZ_CONF_DIR}/orgaTmpl/docker-compose.yml docker-compose.yml + printKazMsg "Service enabled:" + for service in $(listServ) ; do + printKazMsg " * ${service}" + done +fi + +if [[ -n "${STAGE_DEFAULT}${STAGE_CREATE}" ]]; then + # ########## update ${ORGA_DIR}/init-volume.sh + update ${KAZ_CONF_DIR}/orgaTmpl/init-volume.sh init-volume.sh + chmod a+x init-volume.sh + + ln -sf ../../config/orgaTmpl/orga-gen.sh + ln -sf ../../config/orgaTmpl/orga-rm.sh + ln -sf ../../config/orgaTmpl/init-paheko.sh + ln -sf ../../config/orgaTmpl/initdb.d/ + ln -sf ../../config/orgaTmpl/app/ + ln -sf ../../config/orgaTmpl/wiki-conf/ + ln -sf ../../config/orgaTmpl/reload.sh + ln -sf ../../config/orgaTmpl/init-db.sh +fi + +if [[ -n "${STAGE_DEFAULT}${STAGE_CREATE}" ]]; then + # ########## update ${DOCKERS_ENV} + if ! grep -q "proxy_orga=" .env 2> /dev/null + then + echo "proxy_orga=on" >> .env + fi + + if ! grep -q "proxy_${ORGA_FLAG}=" .env 2> /dev/null + then + echo "proxy_${ORGA_FLAG}=off" >> .env + fi + touch "${ORGA_LIST}" + if ! grep -qx "${ORGA}-orga" "${ORGA_LIST}" 2> /dev/null + then + echo "${ORGA}-orga" >> "${ORGA_LIST}" + fi +fi + +if [[ -n "${STAGE_DEFAULT}${STAGE_CREATE}" ]]; then + # ########## create volume + ./init-volume.sh +fi + +if [[ -n "${STAGE_CREATE}" ]]; then + # ########## start docker + docker-compose up -d +fi +if [[ -n "${STAGE_DEFAULT}" ]]; then + # ########## start docker proxy + ${KAZ_BIN_DIR}/container.sh start ${ORGA}-orga +fi + +if [[ -n "${STAGE_DEFAULT}" ]]; then + # ########## stop ${DOCKERS_ENV} + for service in $("${KAZ_BIN_DIR}/kazList.sh" service disable ${ORGA}-orga); do + DockerServName= + case "${service}" in + agora) + DockerServName="${ORGA}-${mattermostServName}" + ;; + paheko) + continue + ;; + cloud) + DockerServName="${ORGA}-${nextcloudServName}" + ;; + collabora) + DockerServName="${ORGA}-${officeServName}" + ;; + wiki) + DockerServName="${ORGA}-${dokuwikiServName}" + ;; + wp) + DockerServName="${ORGA}-${wordpressServName}" + ;; + esac + if checkDockerRunning "${DockerServName}"; then + printKazMsg " - stop ${service}" + docker rm -f "${DockerServName}" 2>/dev/null + fi + done +fi + +if [[ -n "${STAGE_DEFAULT}${STAGE_INIT}" ]]; then + [ -z "$DBaInitialiser" ] || ./init-db.sh $DBaInitialiser + # ########## init services + [[ "${paheko}" = "on" ]] && ./init-paheko.sh + # initCmd="--install" + # # XXX risque d'écraser user DB + # [[ "${cloud}" = "on" ]] && initCmd="$initCmd -cloud" + # # XXX risque d'écraser user DB + # [[ "${wp}" = "on" ]] && initCmd="$initCmd -wp" + # [[ "${wiki}" = "on" ]] && initCmd="$initCmd -wiki" + # # XXX risque d'écraser user DB + # [[ "${agora}" = "on" ]] && initCmd="$initCmd -agora" + # [[ "${castopod}" = "on" ]] && initCmd="$initCmd -castopod" + + # on initialise que si il y a au moins un truc à initialiser + [[ "${INITCMD2}" != "--install" ]] && ${KAZ_BIN_DIR}/gestContainers.sh $INITCMD2 "${ORGA}" +fi + +if [[ -n "${STAGE_DEFAULT}" ]]; then + # ########## update status web page + ${KAZ_COMP_DIR}/web/web-gen.sh +fi diff --git a/config/orgaTmpl/orga-rm.sh b/config/orgaTmpl/orga-rm.sh new file mode 100755 index 0000000..df11806 --- /dev/null +++ b/config/orgaTmpl/orga-rm.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +KAZ_ROOT=$(cd "$(dirname $0)/../.."; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +cd $(dirname $0) + +PWD=$(pwd) +ORGA_DIR=$(basename ${PWD}) +availableProxyComposes=($(getList "${KAZ_CONF_DIR}/container-proxy.list")) +TMPL_PROXY_COMPOSES=() +for item in "${availableProxyComposes[@]}"; do + TMPL_PROXY_COMPOSES+=("${KAZ_COMP_DIR}/${item}/docker-compose.tmpl.yml") +done +ORGA_LIST="${KAZ_CONF_DIR}/container-orga.list" + +remove () { + while : ; do + read -r -p "Are you sure remove ${ORGA}? [y/N] " response + case "$response" in + [oO][uU][iI] | [yY][eE][sS] | [yY] | [oO] ) + + echo "remove ${ORGA}" + . .env + ORGA_FLAG=${ORGA//-/_}_orga + for item in "${TMPL_PROXY_COMPOSES[@]}"; do + echo "Update ${item}" + if grep -q "^{{${ORGA_FLAG}" "${item}" 2> /dev/null ; then + sed -i -e "/^{{${ORGA_FLAG}/,/^}}/d" "${item}" + fi + done + DEL_DOMAIN="" + for serv in ${pahekoHost} ${cloudHost} ${officeHost} ${dokuwikiHost} ${wordpressHost} ${matterHost} + do + DEL_DOMAIN+="${ORGA}-${serv} " + done + ${KAZ_BIN_DIR}/dns.sh del ${DEL_DOMAIN} + ${KAZ_BIN_DIR}/container.sh stop ${ORGA}-orga + sed -i -e "/proxy_${ORGA_FLAG}=/d" "${DOCKERS_ENV}" + sed -i -e "/^${ORGA}-orga$/d" "${ORGA_LIST}" + rm -fr "${KAZ_COMP_DIR}/${ORGA}-orga" + exit;; + [Nn]* ) + + exit;; + * ) + + echo "Please answer yes or no." + ;; + esac + done +} + +if [[ "${ORGA_DIR}" = "orgaTmpl" ]] +then + while : + do + echo -n "Give new organization name ? " + read ORGA + [[ "${ORGA}" =~ ^[a-zA-Z0-9_\-]+$ ]] && [[ ! -z "${ORGA}" ]] && break + echo "Name must contains only a-zA-Z0-9_\-" + done + remove + exit +fi + +if [[ "${ORGA_DIR}" != *"-orga" ]] +then + echo "it's not an orga dir" + exit +fi + +ORGA=${ORGA_DIR%-orga} +remove diff --git a/config/orgaTmpl/reload.sh b/config/orgaTmpl/reload.sh new file mode 100755 index 0000000..7488206 --- /dev/null +++ b/config/orgaTmpl/reload.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +PRG=$(basename $0) +KAZ_ROOT=$(cd "$(dirname $0)/../.."; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars +. "${DOCKERS_ENV}" + +cd $(dirname $0) +PWD=$(pwd) +ORGA_DIR=$(basename ${PWD}) +ORGA=${ORGA_DIR%-orga} + + +${KAZ_BIN_DIR}/manageWiki.sh --reload $ORGA \ No newline at end of file diff --git a/config/orgaTmpl/wiki-conf/acl.auth.php b/config/orgaTmpl/wiki-conf/acl.auth.php new file mode 100644 index 0000000..11b3b5d --- /dev/null +++ b/config/orgaTmpl/wiki-conf/acl.auth.php @@ -0,0 +1,10 @@ +# acl.auth.php +# +# Don't modify the lines above +# +# Access Control Lists +# +# Auto-generated by install script +# Date: Sat, 13 Feb 2021 17:42:28 +0000 +* @ALL 1 +* @user 8 diff --git a/config/orgaTmpl/wiki-conf/local.php b/config/orgaTmpl/wiki-conf/local.php new file mode 100644 index 0000000..117c4d9 --- /dev/null +++ b/config/orgaTmpl/wiki-conf/local.php @@ -0,0 +1,26 @@ + +# Don't modify the lines above +# +# Userfile +# +# Auto-generated by install script +# Date: Sat, 13 Feb 2021 17:42:28 +0000 +# +# Format: +# login:passwordhash:Real Name:email:groups,comma,separated + +admin:$2y$10$GYvFgViXeEUmDViplHEs7eoYV8tmbfsS8wA1vfHQ.tWgW14o9aTjy:admin:contact@kaz.bzh:admin,user diff --git a/config/proxy/proxy_params b/config/proxy/proxy_params new file mode 100644 index 0000000..073a27e --- /dev/null +++ b/config/proxy/proxy_params @@ -0,0 +1,21 @@ + +#proxy_buffering off; +#proxy_set_header X-Forwarded-Host $host:$server_port; +#proxy_set_header X-Forwarded-Server $host; +#XXX pb proxy_set_header Connection $proxy_connection; + +proxy_buffers 256 16k; +proxy_buffer_size 16k; + +# mattermost +http2_push_preload on; # Enable HTTP/2 Server Push +add_header Strict-Transport-Security max-age=15768000; +proxy_set_header Host $http_host; +proxy_set_header X-Real-IP $remote_addr; +#proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +proxy_set_header X-Forwarded-Proto $scheme; + +#proxy_hide_header 'x-frame-options'; +#proxy_set_header x-frame-options allowall; +proxy_set_header X-Frame-Options SAMEORIGIN; + diff --git a/config/skip-file.txt b/config/skip-file.txt new file mode 100644 index 0000000..9e432ef --- /dev/null +++ b/config/skip-file.txt @@ -0,0 +1,89 @@ +# a verifier +/vigilo/ +dockers/grav +dockers/web/Dockerfile +dockers/proxy/todo-ssl +dockers/cloud/DEADJOE +# jamais +/.git/ +bin/createUser.log +bin/createUser.old +bin/createUser.txt +dockers/mattermost/modif_user.txt +state/activites_mailbox.csv +state/collecte.csv +config/createUser_cmds_to_run.sh +dockers/proxy/config/pb-vigilo.txt +*~ +*/*~ +*/*/*~ +*/*/*/*~ +.*~ +*/.*~ +*/*/.*~ +*/*/*/.*~ +*/*.old/ +*/*/*.old/ +*/*/*/*.old/ +# auto +config/dockers.env +dockers/proxy/config/nginx.conf +dockers/*-orga/ +dockers/proxy-* +# param +config/container-*.list +dockers/postfix/config/dovecot-quotas.cf +dockers/postfix/config/postfix-accounts.cf +dockers/postfix/config/postfix-accounts.cf +dockers/postfix/config/postfix-virtual.cf +dockers/postfix/config/ssl/demoCA/cacert.pem +dockers/postfix/config/ssl/demoCA/careq.pem +dockers/postfix/config/ssl/demoCA/index.txt +dockers/postfix/config/ssl/demoCA/index.txt.attr +dockers/postfix/config/ssl/demoCA/index.txt.old +dockers/postfix/config/ssl/demoCA/newcerts/*.pem +dockers/postfix/config/ssl/demoCA/private/cakey.pem +dockers/postfix/config/ssl/demoCA/serial +dockers/postfix/config/ssl/mail.dev.kaz.bzh-key.pem +dockers/postfix/config/ssl/mail.dev.kaz.bzh-req.pem +dockers/proxy/docker-compose.yml +dockers/test-all-but-mail.sh +dockers/web/html/status/allServices.html +dockers/web/html/status/allServices.tmpl.html +config/container-orga.list +config/container-withMail.list +config/container-withoutMail.list +config/password/env-mailServ +config/proxy/*_kaz_map.* +config/proxy/*_kaz_name.* +# parfois +dockers/proxy/docker-compose.tmpl.yml +# ajouté par rdiff.sh +config/proxy/port.kaz.local +config/proxy/allow_ip.kaz.local +config/proxy/redirect.kaz.local +config/proxy/foreign-certificate +config/proxy/wiki_kaz_map +config/proxy/wp_kaz_name +config/proxy/agora_kaz_map +config/proxy/wiki_kaz_name +config/proxy/agora_kaz_name +config/proxy/redirect +config/proxy/port +config/proxy/cloud_kaz_name +config/proxy/wp_kaz_map +config/proxy/allow_ip +config/proxy/cloud_kaz_map +dockers/postfix/filter/master.cf.update +dockers/postfix/filter/filter.sh +dockers/postfix/filter/jirafeauAPI +dockers/postfix/filter/eMailShrinker +dockers/postfix/filter/domainname +dockers/postfix/filter +dockers/jirafeau/config/config.local.php +config/skip-email.txt +dockers/sympa/filter/domainname +dockers/sympa/DEADJOE +bin/DEADJOE +dockers/sympa/reload.sh.non.utilisé +dockers/kaz-orga.sav diff --git a/dockers/apikaz/Readme.txt b/dockers/apikaz/Readme.txt new file mode 100644 index 0000000..dcbe9e2 --- /dev/null +++ b/dockers/apikaz/Readme.txt @@ -0,0 +1,22 @@ +yo, ceci est l'api de kaz ! + +https://apikaz.kazkouil.fr/ + +Je pars de ça: python api + docker-compose: https://dev.to/alissonzampietro/the-amazing-journey-of-docker-compose-17lj + +Pour la doc: +module Flask + flask-restful + flasgger +https://stackoverflow.com/questions/75840827/how-to-properly-generate-a-documentation-with-swagger-for-flask +https://medium.com/@DanKaranja/building-api-documentation-in-flask-with-swagger-a-step-by-step-guide-59a453509e2f +https://apidog.com/blog/what-is-flasgger/ + +autre piste: abandonnée pour l'instant. trop jeune ? +Documentation (OpenApi remplace swagger) +https://pypi.org/project/flask-openapi3/ + + +TODO: +* sécurisation de l'API : un token ? otp ? +* sécurité: comment différencier les rôles admin (tout faire) et kaznaute (changer uniquement ses coordonnées, relancer son site propre web, ...) +* pourquoi pas une seule classe pour plusieurs api ? (par exemple LDAP, pas mal de factorisation possible) +* pourquoi pas de donnée dans le body ? (au lieu des variables dans l'url). par ex, pour le message du mail. diff --git a/dockers/apikaz/docker-compose.yml b/dockers/apikaz/docker-compose.yml new file mode 100644 index 0000000..7b8437d --- /dev/null +++ b/dockers/apikaz/docker-compose.yml @@ -0,0 +1,56 @@ +version: '3.8' +services: + api-service: + build: ./source/ + container_name: ${apikazServName} +# restart: ${restartPolicy} + volumes: + - ./source/:/usr/src/app/ + #pour être dans la même time zone que le host + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + ports: + - 5000:5000 + env_file: + - ../../secret/env-${apikazServName} + environment: + PORT: 5000 + FLASK_DEBUG: 1 + FLASK_ENV: development # Activation du rechargement automatique + #important sinon mmctl va aller taper sur PROD1 + ENV MMCTL_SERVER: "https://${apikazHost}.${domain}" +# volumes: +# - apiKaz:/ + networks: + - apikazNet + - pahekoNet + - ldapNet + - cloudNet + - postfixNet + external_links: + - ${smtpServName}:${smtpHost}.${domain} + labels: + - "traefik.enable=true" + - "traefik.http.routers.${apikazServName}.rule=Host(`${apikazHost}.${domain}`)" + - "traefik.http.routers.${apikazServName}.middlewares=test-adminipwhitelist@file" + - "traefik.docker.network=apikazNet" + +#volumes: +# apiKaz: + +networks: + apikazNet: + external: true + name: apikazNet + pahekoNet: + external: true + name: pahekoNet + ldapNet: + external: true + name: ldapNet + cloudNet: + external: true + name: cloudNet + postfixNet: + external: true + name: postfixNet diff --git a/dockers/apikaz/source/Dockerfile b/dockers/apikaz/source/Dockerfile new file mode 100644 index 0000000..bafbf83 --- /dev/null +++ b/dockers/apikaz/source/Dockerfile @@ -0,0 +1,31 @@ +FROM python:3.11 + +#cette image permet d'avoir l'env python ainsi que le mode "reload on change", pratique quand on modifie les sources *.py + +# maj des packages dispo +RUN apt-get update + +#ldap pour le pip install python-ldap car apt-get python-ldap marche po +RUN apt-get install -y libsasl2-dev python3-dev libldap2-dev libssl-dev ldap-utils + +#pour l'api soap sympa', perl est déjà installé mais il faut les modules suivant +RUN cpan App::cpanminus +RUN cpanm SOAP::Lite XML::LibXML MIME::EncWords Text::LineFold Class::Singleton Locale::Messages + +#installer le truc de génération de mot de mdp +RUN apt-get -y install apg + +#installer mmctl pour mattermost +RUN mkdir -p /mm/ && cd /mm/ && \ + curl -vfsSL -O https://releases.mattermost.com/mmctl/v9.7.1/linux_amd64.tar && \ + tar -xf linux_amd64.tar + +#l'api Kaz +RUN mkdir /usr/src/app/ +COPY . /usr/src/app/ +WORKDIR /usr/src/app/ +EXPOSE 5000 + +#les modules python à installer lors du build +RUN pip install -r requirements.txt +CMD ["python", "app.py"] diff --git a/dockers/apikaz/source/Sympa/Constants.pm b/dockers/apikaz/source/Sympa/Constants.pm new file mode 100644 index 0000000..5348417 --- /dev/null +++ b/dockers/apikaz/source/Sympa/Constants.pm @@ -0,0 +1,75 @@ +# -*- indent-tabs-mode: nil; -*- +# vim:ft=perl:et:sw=4 +# $Id$ + +# Sympa - SYsteme de Multi-Postage Automatique +# +# Copyright (c) 1997, 1998, 1999 Institut Pasteur & Christophe Wolfhugel +# Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, +# 2006, 2007, 2008, 2009, 2010, 2011 Comite Reseau des Universites +# Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016, 2017 GIP RENATER +# Copyright 2018 The Sympa Community. See the AUTHORS.md file at the +# top-level directory of this distribution and at +# . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +package Sympa::Constants; + +use strict; + +use constant VERSION => '--VERSION--'; +use constant USER => '--USER--'; +use constant GROUP => '--GROUP--'; + +use constant CONFIG => '--CONFIG--'; +use constant WWSCONFIG => '--WWSCONFIG--'; +use constant SENDMAIL_ALIASES => '--SENDMAIL_ALIASES--'; + +use constant PIDDIR => '--piddir--'; +use constant EXPLDIR => '--expldir--'; +use constant SPOOLDIR => '--spooldir--'; +use constant SYSCONFDIR => '--sysconfdir--'; +use constant LOCALEDIR => '--localedir--'; +use constant LIBEXECDIR => '--libexecdir--'; +use constant SBINDIR => '--sbindir--'; +use constant SCRIPTDIR => '--scriptdir--'; +use constant MODULEDIR => '--modulesdir--'; +use constant DEFAULTDIR => '--defaultdir--'; +use constant ARCDIR => '--arcdir--'; +use constant BOUNCEDIR => '--bouncedir--'; +use constant EXECCGIDIR => '--execcgidir--'; +use constant STATICDIR => '--staticdir--'; +use constant CSSDIR => '--cssdir--'; +use constant PICTURESDIR => '--picturesdir--'; + +use constant EMAIL_LEN => 100; +use constant FAMILY_LEN => 50; +use constant LIST_LEN => 50; +use constant ROBOT_LEN => 80; + +1; +__END__ + +=encoding utf-8 + +=head1 NAME + +Sympa::Constants - Definition of constants + +=head1 DESCRIPTION + +This module keeps definition of constants used by Sympa software. + +=cut \ No newline at end of file diff --git a/dockers/apikaz/source/Sympa/Language.pm b/dockers/apikaz/source/Sympa/Language.pm new file mode 100644 index 0000000..87b0af8 --- /dev/null +++ b/dockers/apikaz/source/Sympa/Language.pm @@ -0,0 +1,1225 @@ +# -*- indent-tabs-mode: nil; -*- +# vim:ft=perl:et:sw=4 +# $Id$ + +# Sympa - SYsteme de Multi-Postage Automatique +# +# Copyright (c) 1997, 1998, 1999 Institut Pasteur & Christophe Wolfhugel +# Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, +# 2006, 2007, 2008, 2009, 2010, 2011 Comite Reseau des Universites +# Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016, 2017 GIP RENATER +# Copyright 2017, 2018 The Sympa Community. See the AUTHORS.md file at the +# top-level directory of this distribution and at +# . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +package Sympa::Language; + +use strict; +use warnings; +use base qw(Class::Singleton); + +use Encode qw(); +use Locale::Messages; +use POSIX qw(); + +use Sympa::Constants; + +BEGIN { + ## Using the Pure Perl implementation of gettext + ## This is required on Solaris : native implementation of gettext does not + ## map ll_RR with ll. + # libintl-perl 1.22 (virtually 1.23) or later is recommended to use + # 'gettext_dumb' package which is independent from POSIX locale. If older + # version is used. falls back to 'gettext_pp'. + my $package = Locale::Messages->select_package('gettext_dumb'); + Locale::Messages->select_package('gettext_pp') + unless $package and $package eq 'gettext_dumb'; + ## Workaround: Prevent from searching catalogs in /usr/share/locale. + undef $Locale::gettext_pp::__gettext_pp_default_dir; + + ## Define what catalogs are used + Locale::Messages::bindtextdomain(sympa => Sympa::Constants::LOCALEDIR); + Locale::Messages::bindtextdomain(web_help => Sympa::Constants::LOCALEDIR); + Locale::Messages::textdomain('sympa'); + ## Get translations by internal encoding. + Locale::Messages::bind_textdomain_codeset(sympa => 'utf-8'); + Locale::Messages::bind_textdomain_codeset(web_help => 'utf-8'); +} + +# Constructor for Class::Singleton. +sub _new_instance { + my $class = shift; + my $self = $class->SUPER::_new_instance(); + + ## Initialize lang/locale. + $self->set_lang('en'); + return $self; +} + +## The map to get language tag from older non-POSIX locale naming. +my %language_equiv = ( + 'cn' => 'zh-CN', + 'tw' => 'zh-TW', + 'cz' => 'cs', + 'us' => 'en-US', +); + +## The map to get appropriate POSIX locale name from language code. +## Why this is required is that on many systems locales often have canonic +## "ll_RR.ENCODING" names only. n.b. This format can not express all +## languages in proper way, e.g. Common Arabic ("ar"), Esperanto ("eo"). +## +## This map is also used to convert old-style Sympa "locales" to language +## tags ('en' is special case. cf. set_lang()). +my %lang2oldlocale = ( + 'af' => 'af_ZA', + 'ar' => 'ar_SY', + 'br' => 'br_FR', + 'bg' => 'bg_BG', + 'ca' => 'ca_ES', + 'cs' => 'cs_CZ', + 'de' => 'de_DE', + 'el' => 'el_GR', + 'es' => 'es_ES', + 'et' => 'et_EE', + 'eu' => 'eu_ES', + 'fi' => 'fi_FI', + 'fr' => 'fr_FR', + 'gl' => 'gl_ES', + 'hu' => 'hu_HU', + 'id' => 'id_ID', + 'it' => 'it_IT', + 'ja' => 'ja_JP', + 'ko' => 'ko_KR', + 'la' => 'la_VA', # from OpenOffice.org + 'ml' => 'ml_IN', + 'nb' => 'nb_NO', + 'nn' => 'nn_NO', + 'nl' => 'nl_NL', + 'oc' => 'oc_FR', + 'pl' => 'pl_PL', + 'pt' => 'pt_PT', + 'rm' => 'rm_CH', # CLDR + 'ro' => 'ro_RO', + 'ru' => 'ru_RU', + 'sv' => 'sv_SE', + 'tr' => 'tr_TR', + 'vi' => 'vi_VN', +); + +## Regexp for old style canonical locale used by Sympa-6.2a or earlier. +my $oldlocale_re = qr/^([a-z]{2})_([A-Z]{2})(?![A-Z])/i; + +## Regexp for IETF language tag described in RFC 5646 (BCP 47), modified. +my $language_tag_re = qr/^ + ([a-z]{2}(?:-[a-z]{3}){1,3} | [a-z]{2,3}) # language (and ext.) + (?:-([a-z]{4}))? # script + (?:-([a-z]{2}))? # region (no UN M.49) + (?:-( # variant + (?:[a-z0-9]{5,} | [0-9][a-z0-9]{3,}) + (?:-[a-z0-9]{5,} | -[0-9][a-z0-9]{3,})* + ))? +$/ix; + +## A tiny subset of script codes and gettext modifier names. +## Keys are ISO 15924 script codes (Titlecased, four characters). +## Values are property value aliases standardised by Unicode Consortium +## (lowercased). cf. . +my %script2modifier = ( + 'Arab' => 'arabic', + 'Cyrl' => 'cyrillic', + 'Deva' => 'devanagari', + 'Dsrt' => 'deseret', + 'Glag' => 'glagolitic', + 'Grek' => 'greek', + 'Guru' => 'gurmukhi', + 'Hebr' => 'hebrew', + 'Latn' => 'latin', + 'Mong' => 'mongolian', + 'Shaw' => 'shaw', # found in Debian "en@shaw" locale. + 'Tfng' => 'tifinagh', +); + +sub canonic_lang { + my $lang = shift; + return unless $lang; + + ## Compatibility: older non-POSIX locale names. + if ($language_equiv{$lang}) { + $lang = $language_equiv{$lang}; + } + ## Compatibility: names used as "lang" or "locale" by Sympa <= 6.2a. + elsif ($lang =~ $oldlocale_re) { + $lang = _oldlocale2lang(lc($1) . '_' . uc($2)); + } + + my @subtags; + + # unknown format. + return unless @subtags = ($lang =~ $language_tag_re); + + ## Canonicalize cases of subtags: ll-ext-Scri-RR-variant-... + $subtags[0] = lc $subtags[0]; + $subtags[1] =~ s/^(\w)(\w+)/uc($1) . lc($2)/e if $subtags[1]; + $subtags[2] = uc $subtags[2] if $subtags[2]; + $subtags[3] = lc $subtags[3] if $subtags[3]; + + ##XXX Maybe more canonicalizations here. + + ## Check subtags, + # won't support language extension subtags. + return unless $subtags[0] =~ /^[a-z]{2,3}$/; + + # won't allow multiple variant subtags. + $subtags[3] =~ s/-.+// if $subtags[3]; + + ##XXX Maybe more checks here. + + return @subtags if wantarray; + return join '-', grep {$_} @subtags; +} + +sub implicated_langs { + my @langs = @_; + die 'missing langs parameter' unless @langs; + + my @implicated_langs = (); + + foreach my $lang (@langs) { + my @subtags = canonic_lang($lang); + while (@subtags) { + my $l = join '-', grep {$_} @subtags; + @implicated_langs = ((grep { $_ ne $l } @implicated_langs), $l); + + ## Workaround: + ## - "zh-Hans-CN", "zh-Hant-TW", ... may occasionally be + ## identified with "zh-CN", "zh-TW" etc. Add them to + ## implication list. + if ($l =~ /^zh-(Hans|Hant)-[A-Z]{2}\b/) { + $l = join '-', grep {$_} @subtags[0, 2 .. $#subtags]; + @implicated_langs = + ((grep { $_ ne $l } @implicated_langs), $l); + } + + 1 until pop @subtags; + } + } + + return @implicated_langs; +} + +## Parses content of HTTP 1.1 Accept-Charset, Accept-Encoding or +## Accept-Language request header field. +## Returns an array of arrayrefs [ITEM, WEIGHT]. +## +## NOTE: This might be moved to utility package such as tools.pm. +sub parse_http_accept_string { + my $accept_string = shift || ''; + + $accept_string =~ s/^\s+//; + $accept_string =~ s/\s+$//; + $accept_string ||= '*'; + my @pairs = split /\s*,\s*/, $accept_string; + + my @ret = (); + foreach my $pair (@pairs) { + my ($item, $weight) = split /\s*;\s*/, $pair, 2; + if ( defined $weight + and $weight =~ s/^q\s*=\s*//i + and $weight =~ /^(\d+(\.\d*)?|\.\d+)$/) { + $weight += 0.0; + } else { + $weight = 1.0; + } + push @ret, [$item => $weight]; + } + return @ret; +} + +sub negotiate_lang { + my $accept_string = shift || '*'; + my @supported_languages = grep {$_} map { split /[\s,]+/, $_ } @_; + + ## parse Accept-Language: header field. + ## unknown languages are ignored. + my @accept_languages = + grep { $_->[0] eq '*' or $_->[0] = canonic_lang($_->[0]) } + parse_http_accept_string($accept_string); + return unless @accept_languages; + + ## try to find the best language. + my $best_lang = undef; + my $best_weight = 0.0; + foreach my $supported_lang (@supported_languages) { + my @supported_pfxs = implicated_langs($supported_lang); + foreach my $pair (@accept_languages) { + my ($accept_lang, $weight) = @$pair; + if ($accept_lang eq '*' + or grep { $accept_lang eq $_ } @supported_pfxs) { + unless ($best_lang and $weight <= $best_weight) { + $best_lang = $supported_pfxs[0]; # canonic form + $best_weight = $weight; + } + } + } + } + + return $best_lang; +} + +##sub GetSupportedLanguages { +##DEPRECATED: use Sympa::get_supported_languages(). +## Supported languages are defined by 'supported_lang' sympa.conf parameter. + +## Old name: PushLang() +sub push_lang { + my $self = shift; + my @langs = @_; + + push @{$self->{previous_lang}}, $self->get_lang; + $self->set_lang(@langs); + + return 1; +} + +## Old name: PopLang() +sub pop_lang { + my $self = shift; + + die 'calling pop_lang() without push_lang()' + unless @{$self->{previous_lang}}; + my $lang = pop @{$self->{previous_lang}}; + $self->set_lang($lang); + + return 1; +} + +## Old name: SetLang() +sub set_lang { + my $self = shift; + my @langs = @_; + my $locale; + + foreach my $lang (@langs) { + # Canonicalize lang. + # Note: 'en' is always allowed. Use 'en-US' and so on to provide NLS + # for English. + next unless $lang = canonic_lang($lang); + + # Try to set POSIX locale and gettext locale, and get lang actually + # set. + # Note: Macrolanguage 'zh', 'zh-Hans' or 'zh-Hant' may fallback to + # lang with available region. + if ($locale = _resolve_gettext_locale(lang2locale($lang))) { + ($lang) = + grep { lang2locale($_) eq $locale } implicated_langs($lang); + } elsif ($lang =~ /^zh\b/) { + my @rr; + if ($lang =~ /^zh-Hans\b/) { + @rr = qw(CN SG HK MO TW); # try simp. first + } elsif ($lang =~ /^zh-Hant\b/) { + @rr = qw(HK MO TW CN SG); # try trad. first + } else { + @rr = qw(CN HK MO SG TW); + } + foreach my $rr (@rr) { + $lang = "zh-$rr"; + last if $locale = _resolve_gettext_locale(lang2locale($lang)); + } + } + + next unless $locale and $lang; + + # The locale is the gettext catalog name; lang is the IETF language + # tag. Ex: locale = pt_BR ; lang = pt-BR + # locale_numeric and locale_time are POSIX locales for LC_NUMERIC and + # POSIX::LC_TIME catogories, respectively. As of 6.2b, they became + # optional: + # If setting each of them failed, 'C' locale will be set. + $self->{lang} = $lang; + $self->{locale} = $locale; + $self->{locale_numeric} = + _find_posix_locale(POSIX::LC_NUMERIC(), $locale) + || 'C'; + $self->{locale_time} = _find_posix_locale(POSIX::LC_TIME(), $locale) + || 'C'; + + return $lang; + } + + return; +} + +## Trys to set gettext locale and returns actually set locale. +## Mandatory parameter is gettext locale name. +sub _resolve_gettext_locale { + my $locale = shift or die 'missing locale parameter'; + + # 'en' is always allowed. + return $locale if $locale eq 'en'; + + # Workaround: + # - "nb" and "nn" are recommended not to have "_NO" region suffix: + # Both of them are official languages in Norway. However, current Sympa + # provides "nb_NO" NLS catalog. + $locale =~ s/^(nb|nn)\b/${1}_NO/; + + ## Check if catalog is loaded. + local %ENV; + $ENV{'LANGUAGE'} = $locale; + my $metadata = Locale::Messages::gettext(''); # get header + + unless ($metadata) { + ## If a sub-locale of 'en' (en-CA, en@shaw, ...) failed, fallback to + ## 'en'. Otherwise fails. + if ($locale =~ /^en(?![a-z])/) { + $locale = 'en'; + } else { + return; + } + } elsif ($metadata =~ /(?:\A|\n)Language:\s*([\@\w]+)/i) { + ## Get precise name of gettext locale if possible. + $locale = $1; + } + + ## Workaround for "nb" and "nn": See above. + $locale =~ s/^(nb|nn)_NO\b/$1/; + + return $locale; +} + +# Trys to set POSIX locale which affects to strftime, sprintf etc. +sub _find_posix_locale { + my $type = shift; + my $locale = shift; + + # Special case: 'en' is an alias of 'C' locale. Use 'en_US' and so on for + # real English. + return 'C' if $locale eq 'en'; + + my $orig_locale = POSIX::setlocale($type); + + ## From "ll@modifier", gets "ll", "ll_RR" and "@modifier". + my ($loc, $mod) = split /(?=\@)/, $locale, 2; + my $machloc = $loc; + $machloc =~ s/^([a-z]{2,3})(?!_)/$lang2oldlocale{$1} || $1/e; + $mod ||= ''; + + ## Set POSIX locale + my $posix_locale; + my @try; + + ## Add codeset. + ## UpperCase required for FreeBSD; dashless required on HP-UX; + ## null codeset is last resort. + foreach my $cs ('.utf-8', '.UTF-8', '.utf8', '') { + ## Truncate locale similarly in gettext: full locale, and omit + ## region then modifier. + push @try, + map { sprintf $_, $cs } + ("$machloc%s$mod", "$loc%s$mod", "$loc%s"); + } + foreach my $try (@try) { + if (POSIX::setlocale($type, $try)) { + $posix_locale = $try; + last; + } + } + + POSIX::setlocale($type, $orig_locale); + + return $posix_locale; +} + +## Old name: GetLangName() +## Note: Optional $lang argument was deprecated. +sub native_name { + my $self = shift; + die 'extra argument(s)' if @_; + my $name; + + unless ($self->{lang} and $self->{lang} ne 'en') { + $name = 'English'; + } else { + ## Workaround for nb/nn. + my $locale = $self->{locale}; + $locale =~ s/^(nb|nn)\b/${1}_NO/; + + local %ENV; + $ENV{'LANGUAGE'} = $locale; + my $metadata = Locale::Messages::gettext(''); # get header + + if ($metadata =~ /(?:\A|\n)Language-Team:\s*(.+)/i) { + $name = $1; + $name =~ s/\s*\<\S+\>//; + } + } + + return (defined $name and $name =~ /\S/) ? $name : ''; +} + +## Old name: GetLang() +sub get_lang { + my $self = shift; + return $self->{lang} || 'en'; # the last resort +} + +# DEPRECATED: use Conf::lang2charset(). +# sub GetCharset; + +## DEPRECATED: Use canonic_lang(). +## sub Locale2Lang; + +# Internal function. +# Convert language tag to gettext locale name. +sub lang2locale { + my $lang = shift; + my $locale; + my @subtags; + + ## unknown format. + return unless @subtags = canonic_lang($lang); + + ## convert from "ll-Scri-RR" to "ll_RR@scriptname", or + ## from "ll-RR-variant" to "ll_RR@variant". + $locale = $subtags[0]; + if ($subtags[2]) { + $locale .= '_' . $subtags[2]; + } + if ($subtags[1]) { + $locale .= '@' . ($script2modifier{$subtags[1]} || $subtags[1]); + } elsif ($subtags[3]) { + $locale .= '@' . $subtags[3]; + } + + return $locale; +} + +# Internal function. +# Get language tag from old-style "locale". +# Note: Old name is Locale2Lang(). +# Note: Use canonic_lang(). +sub _oldlocale2lang { + my $oldlocale = shift; + my @parts = split /[\W_]/, $oldlocale; + my $lang; + + if ($lang = {reverse %lang2oldlocale}->{$oldlocale}) { + return $lang; + } elsif (scalar @parts > 1 and length $parts[1]) { + return join '-', lc $parts[0], uc $parts[1]; + } else { + return lc $parts[0]; + } +} + +# Convert language tag to old style "locale". +# Note: This function in earlier releases was named Lang2Locale(). +sub lang2oldlocale { + my $lang = shift; + my $oldlocale; + my @subtags; + + ## unknown format. + return unless @subtags = canonic_lang($lang); + + ## 'zh-Hans' and 'zh-Hant' cannot map to useful POSIX locale. Map them to + ## 'zh_CN' and 'zh_TW'. + ## 'zh' cannot map. + if ($subtags[0] eq 'zh' and $subtags[1] and not $subtags[2]) { + if ($subtags[1] eq 'Hans') { + $subtags[2] = 'CN'; + } elsif ($subtags[1] eq 'Hant') { + $subtags[2] = 'TW'; + } + } + + unless ($subtags[2]) { + if ($lang2oldlocale{$subtags[0]}) { + return $lang2oldlocale{$subtags[0]}; + } + } else { + return join '_', $subtags[0], $subtags[2]; + } + ## unconvertible locale name + return; +} + +# Note: older name is sympa_dgettext(). +sub dgettext { + my $self = shift; + my $textdomain = shift; + my $msgid = shift; + + # Returns meta information on the catalog. + # Note: currently, charset is always 'utf-8'; encoding won't be used. + unless (defined $msgid) { + return; + } elsif ($msgid eq '') { # prevents meta information to be returned + return ''; + } elsif ($msgid eq '_language_') { + return $self->native_name; + } elsif ($msgid eq '_charset_') { + return 'UTF-8'; + } elsif ($msgid eq '_encoding_') { + return '8bit'; + } + + my $gettext_locale; + unless ($self->{lang} and $self->{lang} ne 'en') { + $gettext_locale = 'en_US'; + } else { + $gettext_locale = $self->{locale}; + + # Workaround for nb/nn. + $gettext_locale =~ s/^(nb|nn)\b/${1}_NO/; + } + + local %ENV; + $ENV{'LANGUAGE'} = $gettext_locale; + return Locale::Messages::dgettext($textdomain, $msgid); +} + +sub gettext { + my $self = shift; + my $msgid = shift; + + return $self->dgettext('', $msgid); +} + +sub gettext_sprintf { + my $self = shift; + my $format = shift; + my @args = @_; + + my $orig_locale = POSIX::setlocale(POSIX::LC_NUMERIC()); + + ## if lang has not been set or 'en' is set, fallback to native sprintf(). + unless ($self->{lang} and $self->{lang} ne 'en') { + POSIX::setlocale(POSIX::LC_NUMERIC(), 'C'); + } else { + $format = $self->gettext($format); + POSIX::setlocale(POSIX::LC_NUMERIC(), $self->{locale_numeric}); + } + my $ret = sprintf($format, @args); + + POSIX::setlocale(POSIX::LC_NUMERIC(), $orig_locale); + return $ret; +} + +my %date_part_names = ( + '%a' => { + 'index' => 6, + 'gettext_id' => 'Sun:Mon:Tue:Wed:Thu:Fri:Sat' + }, + '%A' => { + 'index' => 6, + 'gettext_id' => + 'Sunday:Monday:Tuesday:Wednesday:Thursday:Friday:Saturday' + }, + '%b' => { + 'index' => 4, + 'gettext_id' => 'Jan:Feb:Mar:Apr:May:Jun:Jul:Aug:Sep:Oct:Nov:Dec' + }, + '%B' => { + 'index' => 4, + 'gettext_id' => + 'January:February:March:April:May:June:July:August:September:October:November:December' + }, + '%p' => { + 'index' => 2, + 'gettext_id' => 'AM:PM' + }, +); + +sub gettext_strftime { + my $self = shift; + my $format = shift; + my @args = @_; + + my $orig_locale = POSIX::setlocale(POSIX::LC_TIME()); + + ## if lang has not been set or 'en' is set, fallback to native + ## POSIX::strftime(). + unless ($self->{lang} and $self->{lang} ne 'en') { + POSIX::setlocale(POSIX::LC_TIME(), 'C'); + } else { + $format = $self->gettext($format); + + ## If POSIX locale was not set, emulate format strings. + unless ($self->{locale_time} + and $self->{locale_time} ne 'C' + and $self->{locale_time} ne 'POSIX') { + my %names; + foreach my $k (keys %date_part_names) { + $names{$k} = [ + split /:/, + $self->gettext($date_part_names{$k}->{'gettext_id'}) + ]; + } + $format =~ s{(\%[EO]?.)}{ + my $index; + if ( $names{$1} + and defined( + $index = $args[$date_part_names{$1}->{'index'}] + ) + ) { + $index = ($index < 12) ? 0 : 1 + if $1 eq '%p'; + $names{$1}->[$index]; + } else { + $1; + } + }eg; + } + + POSIX::setlocale(POSIX::LC_TIME(), $self->{locale_time}); + } + my $ret = POSIX::strftime($format, @args); + Encode::_utf8_off($ret); + + POSIX::setlocale(POSIX::LC_TIME(), $orig_locale); + return $ret; +} + +sub maketext { + my $self = shift; + my $textdomain = shift; + my $template = shift; + my @args = @_; + + my $orig_locale = POSIX::setlocale(POSIX::LC_NUMERIC()); + + unless ($self->{lang} and $self->{lang} ne 'en') { + POSIX::setlocale(POSIX::LC_NUMERIC(), 'C'); + } else { + $template = $self->dgettext($textdomain, $template); + POSIX::setlocale(POSIX::LC_NUMERIC(), $self->{locale_numeric}); + } + my $ret = $template; + # replace parameters in string + $ret =~ s/[%]([%]|\d+)/($1 eq '%') ? '%' : $args[$1 - 1]/eg; + + POSIX::setlocale(POSIX::LC_NUMERIC(), $orig_locale); + return $ret; +} + +1; +__END__ + +=encoding utf-8 + +=head1 NAME + +Sympa::Language - Handling languages and locales + +=head1 SYNOPSIS + + use Sympa::Language; + my $language = Sympa::Language->instance; + $language->set_lang('zh-TW', 'zh', 'en'); + + print $language->gettext('Lorem ipsum dolor sit amet.'); + +=head1 DESCRIPTION + +This package provides interfaces for i18n (internationalization) of Sympa. + +The language tags are used to determine each language. +A language tag consists of one or more subtags: language, script, region and +variant. Below are some examples. + +=over 4 + +=item * + +C - Arabic language + +=item * + +C - Ainu language + +=item * + +C - Portuguese language in Brazil + +=item * + +C - Belarusian language in Latin script + +=item * + +C - Valencian variant of Catalan + +=back + +Other two sorts of identifiers are derived from language tags: +gettext locales and POSIX locales. + +The gettext locales determine each translation catalog. +It consists of one to three parts: language, territory and modifier. +For example, their equivalents of language tags above are C, C, +C, C and C, respectively. + +The POSIX locales determine each I. They have similar forms to +gettext locales and are used by this package internally. + +=head2 Functions + +=head3 Manipulating language tags + +=over 4 + +=item canonic_lang ( $lang ) + +I. +Canonicalizes language tag according to RFC 5646 (BCP 47) and returns it. + +Parameter: + +=over + +=item $lang + +Language tag or similar thing. +Old style "locale" by Sympa (see also L) will also be +accepted. + +=back + +Returns: + +Canonicalized language tag. +In array context, returns an array +C<(I, I + + + + + + + + + diff --git a/dockers/jirafeau/.env b/dockers/jirafeau/.env new file mode 120000 index 0000000..406acd1 --- /dev/null +++ b/dockers/jirafeau/.env @@ -0,0 +1 @@ +../../config/dockers.env \ No newline at end of file diff --git a/dockers/jirafeau/Dockerfile b/dockers/jirafeau/Dockerfile new file mode 100644 index 0000000..35b9080 --- /dev/null +++ b/dockers/jirafeau/Dockerfile @@ -0,0 +1,75 @@ +FROM php:7.4-apache + +######################################## +# APT local cache +# work around because COPY failed if no source file +COPY .dummy .apt-mirror-confi[g] .proxy-confi[g] / +RUN cp /.proxy-config /etc/profile.d/proxy.sh 2> /dev/null || true +RUN if [ -f /.apt-mirror-config ] ; then . /.apt-mirror-config && sed -i \ + -e "s%s\?://deb.debian.org%://${APT_MIRROR_DEBIAN}%g" \ + -e "s%s\?://security.debian.org%://${APT_MIRROR_DEBIAN_SECURITY}%g" \ + -e "s%s\?://archive.ubuntu.com%://${APT_MIRROR_UBUNTU}%g" \ + -e "s%s\?://security.ubuntu.com%://${APT_MIRROR_UBUNTU_SECURITY}%g" \ + /etc/apt/sources.list; fi + +######################################## +RUN apt-get update --quiet && apt-get install -y \ + libicu-dev libpq-dev zlib1g-dev libicu-dev \ + libzip-dev wget zip patch mailutils sendmail + +RUN apt-get install -y emacs php-elisp telnet + +RUN sed -i 's/127.0.0.1/smtp/' /etc/mail/submit.mc +RUN echo "define(\`SMART_HOST',\`smtp')" >> /etc/mail/sendmail.mc +RUN m4 /etc/mail/submit.mc > /etc/mail/submit.cf +RUN m4 /etc/mail/sendmail.mc > /etc/mail/sendmail.cf + +#install composer setup script +COPY dockers/jirafeau/composer-setup.sh /usr/local/bin/ +RUN chmod +x /usr/local/bin/composer-setup.sh + +#install internationalization libs +RUN docker-php-ext-configure intl +RUN docker-php-ext-install intl +RUN docker-php-ext-install zip + +######################################## +#install jirafeau +RUN mkdir /var/jirafeau/ /var/jirafeauData/ +WORKDIR /var/jirafeau +COPY --chown=www-data git/Jirafeau/ . +COPY --chown=www-data git/depollueur/src/Jirafeau/[aft].php ./ +COPY --chown=www-data dockers/jirafeau/media/kaz media/kaz +RUN sed -i -e '1i\

La limite des téléversements est actuellement de

' lib/template/footer.php +RUN sed -i -e '/
/i\
' lib/template/footer.php + +COPY dockers/jirafeau/config/composer.json . +RUN /usr/local/bin/composer-setup.sh +RUN php composer.phar install + +RUN echo '\n\ +upload_max_filesize = 1024M\n\ +post_max_size = 1024M\n\ +[mail function]\n\ +SMTP = smtp\n\ +smtp_port = 25\n\ +sendmail_path=/usr/sbin/sendmail -t -i\n\ +sendmail_from = no-reply@kaz.local\n\ +' > /usr/local/etc/php/php.ini + +RUN chown -R www-data.www-data . /var/jirafeauData/ +RUN chmod o=,ug=rwX -R . /var/jirafeauData/ +RUN rm -rf .git .gitignore .gitlab-ci.yml CONTRIBUTING.md README.md Dockerfile + +VOLUME ["/var/jirafeauData", "/etc/apache2/sites-available"] + +RUN echo "#!/bin/sh" >> /entrypoint.sh +RUN echo "chown -R www-data: /var/jirafeauData/" >> /entrypoint.sh +RUN echo "/usr/sbin/apache2ctl -D FOREGROUND" >> /entrypoint.sh +RUN chmod u=+x /entrypoint.sh + + +EXPOSE 80 + +ENTRYPOINT ["/entrypoint.sh"] + diff --git a/dockers/jirafeau/build.sh b/dockers/jirafeau/build.sh new file mode 100755 index 0000000..bb74cf7 --- /dev/null +++ b/dockers/jirafeau/build.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +SERV_DIR=$(cd $(dirname $0); pwd) +KAZ_ROOT=$(cd $(dirname $0)/../..; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +cd "${SERV_DIR}" +CONF_FILE="config/config.local.php" +touch "${CONF_FILE}" +chown 33:33 "${CONF_FILE}" + +SRC_JIR="https://gitlab.com/mojo42/Jirafeau.git" +JIR_VER="4.3.0" + +"${KAZ_BIN_DIR}/installDepollueur.sh" + +printKazMsg "\n *** Création du Dockerfile Jirafeau" + +printKazMsg "\n - GIT Jirafeau " +cd "${KAZ_GIT_DIR}" +if [ ! -d "Jirafeau" ]; then + git clone "${SRC_JIR}" --branch ${JIR_VER} +fi + +cd "${KAZ_GIT_DIR}/Jirafeau" && git reset --hard && git checkout ${JIR_VER} + +printKazMsg "\n - Dockefile" +cd "${KAZ_ROOT}" +# Pour permettre la copy de git il faut que le répertoire soit visible de la racine qui lance la construction +docker build -t filekaz . -f dockers/jirafeau/Dockerfile + diff --git a/dockers/jirafeau/composer-setup.sh b/dockers/jirafeau/composer-setup.sh new file mode 100755 index 0000000..1eb0ad7 --- /dev/null +++ b/dockers/jirafeau/composer-setup.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +EXPECTED_SIGNATURE=$(wget -q -O - https://composer.github.io/installer.sig) +php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" +ACTUAL_SIGNATURE=$(php -r "echo hash_file('SHA384', 'composer-setup.php');") + +if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ] +then + >&2 echo 'ERROR: Invalid installer signature' + rm composer-setup.php + exit 1 +fi + +php composer-setup.php --quiet +RESULT=$? +rm composer-setup.php +exit $RESULT + diff --git a/dockers/jirafeau/config/composer.json b/dockers/jirafeau/config/composer.json new file mode 100644 index 0000000..3c65b5c --- /dev/null +++ b/dockers/jirafeau/config/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "phpmailer/phpmailer": "^6.5" + } +} diff --git a/dockers/jirafeau/config/jirafeau.conf b/dockers/jirafeau/config/jirafeau.conf new file mode 100644 index 0000000..0821467 --- /dev/null +++ b/dockers/jirafeau/config/jirafeau.conf @@ -0,0 +1,24 @@ + + ServerName file.kaz.bzh + DocumentRoot /var/jirafeau/ + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + ServerSignature Off + + + AllowOverride All + Require all granted + Options FollowSymLinks MultiViews + + Dav off + + + + + deny from all + satisfy all + ErrorDocument 403 "Access denied." + + + + diff --git a/dockers/jirafeau/docker-compose.yml b/dockers/jirafeau/docker-compose.yml new file mode 100644 index 0000000..db5e7f7 --- /dev/null +++ b/dockers/jirafeau/docker-compose.yml @@ -0,0 +1,44 @@ +# jirafeauDir doit être déclaré dans .env qui pointe sur ../../config/docker.env +# car les variables déclarées dans env_file: ne sont pas encore connues dans volumes: + +version: '3' + +services: + jirafeau: + image: filekaz + build: . + container_name: ${jirafeauServName} + restart: always + networks: + - jirafeauNet + - postfixNet + external_links: + - ${smtpServName}:${smtpHost} + # ports: + # - 8081:80 + env_file: + - ../../secret/env-${jirafeauServName} + volumes: + - ./config/jirafeau.conf:/etc/apache2/sites-available/000-default.conf + - fileData:${jirafeauDir} + - ./config/config.local.php:/var/jirafeau/lib/config.local.php + - /etc/localtime:/etc/localtime:ro + - /etc/timezone:/etc/timezone:ro + labels: + - "traefik.enable=true" + - "traefik.http.routers.${jirafeauServName}-admin.rule=Host(`${fileHost}.${domain}`) && PathPrefix(`/admin.php`)" + - "traefik.http.routers.${jirafeauServName}-admin.middlewares=test-adminipwhitelist@file" + - "traefik.http.routers.${jirafeauServName}.rule=Host(`${fileHost}.${domain}`) && ! PathPrefix(`/admin.php`)" + - "traefik.docker.network=jirafeauNet" + +volumes: + fileData: + config: + +networks: + jirafeauNet: + external: true + name: jirafeauNet + postfixNet: + external: true + name: postfixNet diff --git a/dockers/jirafeau/download.sh b/dockers/jirafeau/download.sh new file mode 100755 index 0000000..5250e03 --- /dev/null +++ b/dockers/jirafeau/download.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +SERV_DIR=$(cd $(dirname $0); pwd) +KAZ_ROOT=$(cd $(dirname $0)/../..; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +SRC_JIR="https://gitlab.com/mojo42/Jirafeau.git" +JIR_VER="4.5.0" + +printKazMsg "\n *** Download Jirafeau" + +mkdir -p "${KAZ_GIT_DIR}" +cd "${KAZ_GIT_DIR}" +if [ ! -d "Jirafeau" ]; then + git clone "${SRC_JIR}" --branch ${JIR_VER} +fi + +cd "${KAZ_GIT_DIR}/Jirafeau" +if [ -z "$(git branch | grep "${JIR_VER}")" ]; then + printKazMsg " checkout branch ${JIR_VER}" + git fetch -a + git reset --hard + git checkout ${JIR_VER} +fi diff --git a/dockers/jirafeau/first.sh b/dockers/jirafeau/first.sh new file mode 100755 index 0000000..8da226b --- /dev/null +++ b/dockers/jirafeau/first.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +KAZ_ROOT=$(cd $(dirname $0)/../..; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +JIR_VER=4.5.0 + +cd $(dirname $0) +. "${DOCKERS_ENV}" +. "${KAZ_KEY_DIR}/env-${jirafeauServName}" + +CONF_FILE="${DOCK_VOL}/jirafeau_config/_data/config.local.php" + +if ! grep -q "'installation_done'\s*=>\s*true" "${CONF_FILE}" 2>/dev/null ; then + printKazMsg "\n *** Premier lancement de Jirafeau" + + checkDockerRunning "${jirafeauServName}" "Jirafeau" || exit + curl -X POST \ + -d "jirafeau=${JIR_VER}" \ + -d "step=1" \ + -d "admin_password=${HTTPD_PASSWORD}" \ + -d "next=1" \ + "${httpProto}://${fileHost}.${domain}/install.php" + curl -X POST -d "jirafeau=${JIR_VER}" \ + -d "step=2" \ + -d "web_root=${httpProto}://${fileHost}.${domain}/" \ + -d "var_root=${jirafeauDir}" \ + -d "next=1" \ + "${httpProto}://${fileHost}.${domain}/install.php" +fi + +updatePhpVar(){ + # $1 key + # $2 val + # $3 file + if grep -q "$1" "$3" ; then + sed -i \ + -e "s%\([\"']$1[\"']\s*=>\s*\)[^,]*,%\1$2,%" \ + "$3" + fi +} + +updatePhpVar "style" "'kaz'" "${CONF_FILE}" +updatePhpVar "organisation" "'KAZ'" "${CONF_FILE}" + +#updatePhpVar "web_root" "'${httpProto}://${fileHost}.${domain}/'" "${CONF_FILE}" +#updatePhpVar "admin_password" "'$(echo -n "${HTTPD_PASSWORD}" | sha256sum | awk '{print $1}')'" "${CONF_FILE}" +#updatePhpVar "var_root" "'${jirafeauDir}'" "${CONF_FILE}" +#updatePhpVar "installation_done" "true" "${CONF_FILE}" diff --git a/dockers/jirafeau/media/kaz/bandeau.png b/dockers/jirafeau/media/kaz/bandeau.png new file mode 100644 index 0000000..df4c4dc Binary files /dev/null and b/dockers/jirafeau/media/kaz/bandeau.png differ diff --git a/dockers/jirafeau/media/kaz/email.png b/dockers/jirafeau/media/kaz/email.png new file mode 100644 index 0000000..234a229 Binary files /dev/null and b/dockers/jirafeau/media/kaz/email.png differ diff --git a/dockers/jirafeau/media/kaz/error.png b/dockers/jirafeau/media/kaz/error.png new file mode 100644 index 0000000..4990130 Binary files /dev/null and b/dockers/jirafeau/media/kaz/error.png differ diff --git a/dockers/jirafeau/media/kaz/favicon.ico b/dockers/jirafeau/media/kaz/favicon.ico new file mode 100644 index 0000000..c42c696 Binary files /dev/null and b/dockers/jirafeau/media/kaz/favicon.ico differ diff --git a/dockers/jirafeau/media/kaz/jyraphe.png b/dockers/jirafeau/media/kaz/jyraphe.png new file mode 100644 index 0000000..066e9de Binary files /dev/null and b/dockers/jirafeau/media/kaz/jyraphe.png differ diff --git a/dockers/jirafeau/media/kaz/kaz.png b/dockers/jirafeau/media/kaz/kaz.png new file mode 100644 index 0000000..e27c743 Binary files /dev/null and b/dockers/jirafeau/media/kaz/kaz.png differ diff --git a/dockers/jirafeau/media/kaz/ok.png b/dockers/jirafeau/media/kaz/ok.png new file mode 100644 index 0000000..c1ce846 Binary files /dev/null and b/dockers/jirafeau/media/kaz/ok.png differ diff --git a/dockers/jirafeau/media/kaz/style.css.php b/dockers/jirafeau/media/kaz/style.css.php new file mode 100644 index 0000000..834d4c4 --- /dev/null +++ b/dockers/jirafeau/media/kaz/style.css.php @@ -0,0 +1,205 @@ + + * + * 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 . + */ + +/* + * This stylesheet is the default stylesheet for Jyraphe. + * The content is dynamically generated for easier handling. + */ + +$dark = '#8B4513'; + +header('Content-type: text/css'); + +?> + +body { + font-family: sans-serif; + text-align: center; + margin: 2ex auto; + background: white; + /* border: 5px solid; */ +} + +fieldset { + text-align: left; + width: 40em; + margin: auto; + background: #E2f5ff; + border: 2px solid #02233f; + -moz-border-radius: 10px; + -webkit-border-radius: 10px; + border-radius: 10px; +} + +fieldset legend { + color: white; + background: #02233f; + border: 2px solid #02233f; + padding: 1px 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; +} + +h1 { + width: 100%; + text-align: center; + background: url('bandeau.png') left top repeat-x; + height: 70px; + padding-top: 30px; +} + +h1 a { + text-decoration: none; + color: white; +} + +fieldset p { + margin-left: 25%; +} + +.jyraphe_info { + font-size: small; + margin-left: 30%; +} + +label { + float: left; + width: 12em; +} + +input[type=text], input[type=submit], select { + color: black; + width: 15em; + border: 1px #02233f solid; + background: white; +} + +input:hover { + color: white; + background: #02233f; +} + +#jyraphe { + background: url('jyraphe.png') right bottom no-repeat; + position: fixed; + bottom: 0; + right: 0; + height: 100px; + width: 100px; + clear:both; +} + +#kaz { + background: url('kaz.png') right bottom no-repeat; + position: fixed; + top: 0; + left: 10px; + height: 64px; + width: 64px; + clear:both; +} + +#copyright { + text-align: center; +} + +.error, .message { + width: 50em; + margin: 5ex auto; +} + +.error { + padding-bottom: 1ex; + border: red 2px solid; + background-color: #FBB; +} + +.error p:before { + content: url('error.png'); + padding-right: 1ex; +} + +.message { + padding: 1ex; + border: green 2px solid; + background-color: #BFB; +} + +.message p:before { + content: url('ok.png'); + padding-right: 1ex; +} + +.info { + text-align: left; + width: 40em; + margin: auto; + background: #E2f5ff; + border: 2px solid #02233f; + -moz-border-radius: 10px; + -webkit-border-radius: 10px; + border-radius: 10px; +} + +.info h2 { + text-align: center; +} + +.info h3 { + text-align: center; +} + +.info p { + margin-left: 5%; + margin-right: 5%; +} + +#upload {} + +#uploading { + text-align: center; + width: 30em; + background: #E2f5ff; + border: 2px solid #02233f; + margin: auto; +} + +#upload_finished { + text-align: center; + width: 60em; + background: #E2f5ff; + border: 2px solid #02233f; + margin: auto; +} + +#self_destruct { + font-weight: bold; + color: red; +} + +#upload_link_email { + margin-left: 10px; +} + +#upload_image_email { + padding-left: 20px; + padding-bottom: 15px; + background: url(email.png) no-repeat; +} diff --git a/dockers/ldap/.env b/dockers/ldap/.env new file mode 120000 index 0000000..406acd1 --- /dev/null +++ b/dockers/ldap/.env @@ -0,0 +1 @@ +../../config/dockers.env \ No newline at end of file diff --git a/dockers/ldap/UIHooks/post-hook.sh b/dockers/ldap/UIHooks/post-hook.sh new file mode 100755 index 0000000..26819bb --- /dev/null +++ b/dockers/ldap/UIHooks/post-hook.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +EMAIL=$1 +NEWPASSWORD=$(base64 -d <<< $2) +OLDPASSWORD=$(base64 -d <<< $3) + +URL_AGORA="https://${matterHost}.${domain}" +mattermost_token=${LDAPUI_MM_ADMIN_TOKEN} + +IDUSER=$(curl -s -H "Authorization: Bearer ${mattermost_token}" "${URL_AGORA}/api/v4/users/email/${EMAIL}" | awk -F "," '{print $1}' | sed -e 's/{"id"://g' -e 's/"//g') +if [ ${IDUSER} == 'app.user.missing_account.const' ] +then + exit 1 +else + curl -X PUT -i -H "Authorization: Bearer ${mattermost_token}" -d "{\"current_password\":\"${OLDPASSWORD}\",\"new_password\":\"${NEWPASSWORD}\"}" "${URL_AGORA}/api/v4/users/${IDUSER}/password" +fi diff --git a/dockers/ldap/base/acl.ldif.tmpl b/dockers/ldap/base/acl.ldif.tmpl new file mode 100644 index 0000000..c3a4198 --- /dev/null +++ b/dockers/ldap/base/acl.ldif.tmpl @@ -0,0 +1,21 @@ +dn: olcDatabase={2}mdb,cn=config +changeType: modify +replace: olcAccess +olcAccess: {0}to attrs=userPassword,shadowLastChange + by self write + by anonymous auth + by dn="cn=ldapui,ou=applications,$LDAPROOT" write + by dn="$BINDDN" write + by * none +olcAccess: {1}to dn.subtree="$LDAPROOT" + by self read + by dn="cn=ldapui,ou=applications,$LDAPROOT" read + by dn="cn=postfix,ou=applications,$LDAPROOT" read + by dn="cn=mattermost,ou=applications,$LDAPROOT" read + by dn="cn=cloud,ou=applications,$LDAPROOT" read + by dn="cn=mobilizon,ou=applications,$LDAPROOT" read + by dn="$BINDDN" write + by * none +olcAccess: {2}to * + by dn="$BINDDN" write + by * none diff --git a/dockers/ldap/base/kaz-schema.ldif.tmpl b/dockers/ldap/base/kaz-schema.ldif.tmpl new file mode 100644 index 0000000..b9ac132 --- /dev/null +++ b/dockers/ldap/base/kaz-schema.ldif.tmpl @@ -0,0 +1,35 @@ +dn: cn={$KAZNUMBER}kaz,cn=schema,cn=config +changeType: modify +replace: olcAttributeTypes +olcAttributeTypes: {0}( 1.3.6.1.4.1.5656.1.1.1 NAME 'mailDeSecours' + DESC 'Adresse mail de secours' + EQUALITY caseIgnoreIA5Match + SUBSTR caseIgnoreIA5SubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{320} ) +olcAttributeTypes: {1}( 1.3.6.1.4.1.5656.1.1.2 NAME 'quota' + DESC 'Quota en GO (integer)' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE) +olcAttributeTypes: {2}( 1.3.6.1.4.1.5656.1.1.3 NAME 'agoraEnabled' + DESC 'acces a agora' + EQUALITY caseIgnoreMatch + SUBSTR caseIgnoreSubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE) +olcAttributeTypes: {3}( 1.3.6.1.4.1.5656.1.1.4 NAME 'mobilizonEnabled' + DESC 'acces a mobilizon' + EQUALITY caseIgnoreMatch + SUBSTR caseIgnoreSubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE) +olcAttributeTypes: {4}( 1.3.6.1.4.1.5656.1.1.5 NAME 'identifiantKaz' + DESC 'Identifiant Kaz prenom.nom' + EQUALITY caseIgnoreMatch + SUBSTR caseIgnoreSubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{320} ) +- +replace: olcObjectClasses +olcObjectClasses: {0}( 1.3.6.1.4.1.5656.1.2.1 NAME 'kaznaute' + DESC 'Un kaznaute' + SUP top AUXILIARY + MUST ( cn $ quota $ mailDeSecours $ identifiantKaz ) + MAY ( agoraEnabled $ mobilizonEnabled ) + ) diff --git a/dockers/ldap/base/skeleton.ldif.tmpl b/dockers/ldap/base/skeleton.ldif.tmpl new file mode 100644 index 0000000..3504a35 --- /dev/null +++ b/dockers/ldap/base/skeleton.ldif.tmpl @@ -0,0 +1,36 @@ +dn: ou=users,$LDAPROOT +objectClass: organizationalUnit +ou: users + +dn: ou=applications,$LDAPROOT +objectClass: organizationalUnit +ou: system + +dn: ou=mailForwardings,$LDAPROOT +objectClass: organizationalUnit +ou: mailForwardings + +dn: cn=postfix,ou=applications,$LDAPROOT +objectClass: person +sn: postfix +userPassword: $POSTFIX_PASSWORD + +dn: cn=ldapui,ou=applications,$LDAPROOT +objectClass: person +sn: ldapui +userPassword: $LDAPUI_PASSWORD + +dn: cn=mattermost,ou=applications,$LDAPROOT +objectClass: person +sn: mattermost +userPassword: $MATTERMOST_PASSWORD + +dn: cn=cloud,ou=applications,$LDAPROOT +objectClass: person +sn: cloud +userPassword: $CLOUD_PASSWORD + +dn: cn=mobilizon,ou=applications,$LDAPROOT +objectClass: person +sn: mobilizon +userPassword: $MOBILIZON_PASSWORD diff --git a/dockers/ldap/br.inc.php b/dockers/ldap/br.inc.php new file mode 100644 index 0000000..abeac40 --- /dev/null +++ b/dockers/ldap/br.inc.php @@ -0,0 +1,156 @@ +amañ."; +$messages['answerrequired'] = "N'ho peus lakaet respont "; +$messages['questionrequired'] = "N'ho peus dibabet goulenn ebet"; +$messages['passwordrequired'] = "Ho ker-tremen a rankit lakaat"; +$messages['answermoderror'] = "N'eo ket bet enrollet ho respont"; +$messages['answerchanged'] = "Enrollet eo bet ho respont"; +$messages['answernomatch'] = "N'eo ket reizh ho respont"; +$messages['resetbyquestionshelp'] = "Dibabit ur goulenn ha respontit outi a-benn adderaouiñ ho ker-tremen. En a-raok e rankit bezañ enrollet ur respont."; +$messages['changehelp'] = "Lakait ho ker-tremen kozh ha dibabit unan nevez."; +$messages['changehelpreset'] = "Ankounac'haet ho ker-tremen ganeoc'h ?"; +$messages['changehelpquestions'] = "Adderaouit ho ker-tremen dre respont ouzh goulennoù"; +$messages['changehelptoken'] = "Adderaouit ho ker-tremen dre degemer un daeadenn dre bostel"; +$messages['changehelpsms'] = "Adderaouit ho ker-tremen dre SMS"; +$messages['resetmessage'] = "Kevarc'h {login},\n\nKlikit amañ evit adderaouiñ ho ker-tremen :\n{url}\n\nMa n'eo ket ganeoc'h-c'hwi eo bet goulennet, na rit ket a van."; +$messages['resetsubject'] = "Adderaouiñ ho ker-tremen"; +$messages['sendtokenhelp'] = "Lakait hoc'h anv-implijer hag ho chomlec'h postel evit adderaouiñ ho ker-tremen. Goude-se e rankoc'h klikañ war al liamm a vo bet kaset deoc'h dre bostel."; +$messages['sendtokenhelpnomail'] = "Lakait hoc'h anv-implijer evit adderaouiñ ho ker-tremen. Goude-se e rankoc'h klikañ war al liamm a vo bet kaset deoc'h dre bostel."; +$messages['mail'] = "Chomlec'h postel"; +$messages['mailrequired'] = "Ho chomlec'h-postel a rankit lakaat"; +$messages['mailnomatch'] = "Ne glot ket ar chomlec'h postel gant an anv-implijer merket"; +$messages['tokensent'] = "Kaset ez eus bet ur postel kadarnaat"; +$messages['tokennotsent'] = "En em gavet ez eus ur fazi pa 'z eo bet kaset ar postel kadarnaat"; +$messages['tokenrequired'] = "Ret eo kaout ar jedouer adderaouiñ"; +$messages['tokennotvalid'] = "N'eo ket reizh ar jedouer adderaouiñ"; +$messages['resetbytokenhelp'] = "Ar jedaouer kaset dre bostel a ro an tu deoc'h da adderaouiñ ho ker-tremen. Evit degemer ur jedaouer nevez, klikit amañ."; +$messages['resetbysmshelp'] = "Ar jedaouer kaset dre SMS a ro an tu deoc'h da adderaouiñ ho ker-tremen. Evit degemer ur jedaouer nevez, klikit amañ."; +$messages['changemessage'] = "Kevarc'h {login},\n\nCheñchet eo bet ho ker-tremen.\n\nMa n'eo ket ganeoc'h-c'hwi eo bet goulennet, kit diouzhtu e darempred gant merour ho rouedad."; +$messages['changesubject'] = "Cheñchet eo bet ho ker-tremen"; +$messages['badcaptcha'] = "N'eo ket bet skoet mat ar 'c'haptcha'. Klaskit adarre."; +$messages['captcharequired'] = "Ar 'c'haptcha' a rankit skeiñ."; +$messages['captcha'] = "Captcha"; +$messages['notcomplex'] = "Ne 'z eus ket a-walc'h a zoareoù arouezennoù disheñvel gant ho ker-stur."; +$messages['policycomplex'] = "Niver a zoareoù arouezennoù disheñvel d'an nebeutañ :"; +$messages['sms'] = "Niverenn SMS"; +$messages['smsresetmessage'] = "Setu ho jedouer :"; +$messages['sendsmshelp'] = "Skoit hoc'h anv-implijer evit degemer ho kod-kadarnaat. Goude-se skoit ar c'hod ho po bet dre SMS."; +$messages['smssent'] = "Kaset eo bet ar c'hod-kadarnaat dre SMS."; +$messages['smsnotsent'] = "Ur fazi a zo bet en ur kas an SMS"; +$messages['smsnonumber'] = "N'eo ket bet kavet an niverenn pellgomz hezoug."; +$messages['userfullname'] = "Anv klok"; +$messages['username'] = "Anv-implijer"; +$messages['smscrypttokensrequired'] = "Ret eo kaout an dibarzh crypt_tokens evit implijout ar fonktion SMS."; +$messages['smsuserfound'] = "Gwiriit eo reizh an titouroù amañ dindan ha klikit war Kas evit degemer ho kod-kadarnaat."; +$messages['smstoken'] = "Kod-kadarnaat"; +$messages['getuser'] = "Kavout an implijer"; +$messages['nophpmbstring'] = "Mat e vefe deoc'h staliañ PHP mbstring"; +$messages['menuquestions'] = "Question"; +$messages['menutoken'] = "Mail"; +$messages['menusms'] = "SMS"; +$messages['nophpxml'] = "Vous devriez installer PHP XML pour utiliser cet outil"; +$messages['tokenattempts'] = "Jedour didalvoud, klaskit adarre"; +$messages['emptychangeform'] = "Cheñchit ho ker-tremen"; +$messages['emptysendtokenform'] = "Degemerit ul liamm evit cheñch ho ker-tremen"; +$messages['emptyresetbyquestionsform'] = "Adderaouit ho ker-tremen"; +$messages['emptysetquestionsform'] = "Enrollit ho respont"; +$messages['emptysendsmsform'] = "Degemerit ur c'hod-adderaouiñ"; +$messages['sameaslogin'] = "Heñvel eo ho ker-tremen hag hoc'h anv-implijer"; +$messages['policydifflogin'] = "Disheñvel diouzh hoc'h anv-implijer e rank bezañ ho ker-tremen"; +$messages['changesshkeymessage'] = "Kevarc'h {login}, \n\nCheñchet eo bet hoc'h alc'hwez SSH. \n\nMa n'eo ket ganeoc'h-c'hwi eo bet goulennet, kit diouzhtu e darempred gant merour ho rouedad."; +$messages['menusshkey'] = "Alc'hwez SSH"; +$messages['changehelpsshkey'] = "Cheñchit hoc'h alc'hwez SSH"; +$messages['sshkeychanged'] = "Cheñchet eo bet hoc'h alc'hwez SSH"; +$messages['sshkeyrequired'] = "An alc'hwez SSH a rankit lakaat"; +$messages['invalidsshkey'] = "An alc'hwez SSH-mañ a seblant bezañ didalvoud"; +$messages['changesshkeysubject'] = "Cheñchet eo bet hoc'h alc'hwez SSH"; +$messages['sshkey'] = "Alc'hwez SSH"; +$messages['emptysshkeychangeform'] = "Cheñchit hoc'h alc'hwez SSH"; +$messages['changesshkeyhelp'] = "Lakait ho ker-tremen hag an alc'hwez SSH nevez."; +$messages['sshkeyerror'] = "Nac'het eo bet an alc'hwez gant ar c'havlec'h LDAP"; +$messages['pwned'] = "Siek eo ho ker-tremen nevez, mat e vefe deoc'h cheñch anezhañ e kement lec'h ec'h implijit anezhañ"; +$messages['policypwned'] = "Arabat d'ho ker-tremen nevez bezañ anavezet e-barzh ur bon foran a c'herioù-tremen siek"; +$messages['policydiffminchars'] = "Niver a arouezennoù o-unan d'an nebeutañ :"; +$messages['diffminchars'] = "Re heñvel ouzh ho ker-tremen kozh eo an hini nevez"; +$messages['specialatends'] = "Emañ arouezenn ispisial e-unan ho ker-tremen nevez e penn-kentañ pe er penn-diwezhañ"; +$messages['policyspecialatends'] = "Arabat d'an arouezenn ispisial e-unan bezañ e penn-kentañ pe e fin ho ker-tremen nevez."; +$messages['checkdatabeforesubmit'] = "Gwiriit an titouroù, mar plij, araok kadarnaat ar furmenn"; +$messages['forbiddenwords'] = "Gerioù difennet a zo en ho ker-stur"; +$messages['policyforbiddenwords'] = "Arabat d'ho ker-tremen bezañ ennañ :"; +$messages['forbiddenldapfields'] = "Ho ker-tremen a zo ennañ arroudoù eus ho antre LDAP"; +$messages['policyforbiddenldapfields'] = "Arabat d'ho ker-tremen bezañ ennañ perzhioù eus hoc'h antre :"; +$messages['ldap_cn'] = "anv klok"; +$messages['ldap_givenName'] = "anv-bihan"; +$messages['ldap_sn'] = "anv-familh"; +$messages['ldap_mail'] = "chomlec'h postel"; +$messages["questionspopulatehint"] = "Lakait hoc'h anv-implijer nemetken evit adkavout ar goulennoù ho peus enrollet."; +$messages['badquality'] = "N'eo ket pinvidik a-walc'h ho ker-tremen"; +$messages['tooyoung'] = "Re nevez-cheñchet eo ho ker-tremen"; +$messages['inhistory'] = "E-barzh roll ho kerioù-tremen kozh emañ ar ger-tremen-mañ"; +$messages['throttle'] = "Re a daolioù-arnod dindan re verr amzer. Klaskit diwezhatoc'h (ma 'z eus un den ac'hanoc'h)"; diff --git a/dockers/ldap/docker-compose.yml b/dockers/ldap/docker-compose.yml new file mode 100644 index 0000000..5fa9caa --- /dev/null +++ b/dockers/ldap/docker-compose.yml @@ -0,0 +1,84 @@ +# https://github.com/bitnami/bitnami-docker-openldap +# https://github.com/wheelybird/ldap-user-manager +# https://github.com/osixia/docker-openldap +# openssl x509 -outform der -in /etc/letsencrypt/local/_wildcard.kaz.local.pem -out /etc/letsencrypt/local/_wildcard.kaz.local.crt +# openssl pkey -in /etc/letsencrypt/local/_wildcard.kaz.local-key.pem -out /etc/letsencrypt/local/_wildcard.kaz.local.key +# apt install ldap-utils +# ldapsearch -x -H ldaps://kaz.local -D "cn=admin,dc=kaz,dc=local" -W + +version: '2' +services: + + web: + image: ltbproject/self-service-password + container_name: ${ldapUIName} + depends_on: + - ldap + networks: + - ldapNet + - postfixNet + links: + - ldap + external_links: + - ${smtpServName}:${smtpHost} + env_file: + - ../../secret/env-${ldapUIName} + # ports: + # - 389:389 + # - 636:636 + environment: + - domain=${domain} + - matterHost=${matterHost} + volumes: + - /etc/ssl:/etc/ssl:ro + - /usr/local/share/ca-certificates:/usr/local/share/ca-certificates:ro + - /etc/timezone:/etc/timezone:ro + - /root/mkcert:/root/mkcert:ro + - configSSP:/var/www/conf/ + - icons:/var/www/html/images/ + - lang:/var/www/lang/ + - ./UIHooks:/var/www/kaz/ + # labels: + # - "traefik.enable=true" + # - "traefik.http.routers.${ldapUIName}.rule=Host(`${ldapUIHost}.${domain}`)" + # - "traefik.docker.network=ldapNet" + + ldap: + image: docker.io/bitnami/openldap:2.6 + container_name: ${ldapServName} + restart: always + + env_file: + - ../../secret/env-${ldapServName} + # ports: + # - 389:389 + # - 636:636 + environment: + - LDAP_ROOT=${ldap_root} + - LDAP_PORT_NUMBER=389 + - LDAP_LDAPS_PORT_NUMBER=636 + - LDAP_CONFIG_ADMIN_ENABLED=yes + - LDAP_SKIP_DEFAULT_TREE=yes + - LDAP_ENABLE_TLS=no + volumes: + - openldapData:/bitnami/openldap + #- ./ldifs:/ldifs:ro + - /etc/letsencrypt:/etc/letsencrypt:ro + - /etc/localtime:/etc/localtime:ro + - /etc/timezone:/etc/timezone:ro + networks: + - ldapNet + +volumes: + openldapData: + configSSP: + icons: + lang: + +networks: + ldapNet: + external: true + name: ldapNet + postfixNet: + external: true + name: postfixNet diff --git a/dockers/ldap/first.sh b/dockers/ldap/first.sh new file mode 100755 index 0000000..fa618df --- /dev/null +++ b/dockers/ldap/first.sh @@ -0,0 +1,88 @@ +#!/bin/bash + +SERV_DIR=$(cd $(dirname $0); pwd) +KAZ_ROOT=$(cd $(dirname $0)/../..; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +cd $(dirname $0) +. "${DOCKERS_ENV}" +. "${KAZ_KEY_DIR}/env-${ldapServName}" +. "${KAZ_KEY_DIR}/env-${ldapUIName}" + + +checkDockerRunning "${ldapServName}" "LDAP" || exit + +printKazMsg "\n *** Premier lancement de LDAP : Mise en place" + +LDAP_IP=$(docker inspect -f '{{.NetworkSettings.Networks.ldapNet.IPAddress}}' ldapServ) +MAIL_IP=$(docker inspect -f '{{.NetworkSettings.Networks.ldapNet.IPAddress}}' mailServ) + +docker exec ${ldapUIName} bash -c "echo '${MAIL_IP} ${smtpHost}.${domain}' >> /etc/hosts" + +BINDDN=cn=${LDAP_ADMIN_USERNAME},${ldap_root} +DC=$(echo ${ldap_root} | cut -d',' -f1 | cut -d'=' -f2) + +for schema in `ls schemas/` +do + ldapadd -H ldap://$LDAP_IP -D "cn=${LDAP_CONFIG_ADMIN_USERNAME},cn=config" -w ${LDAP_CONFIG_ADMIN_PASSWORD} -f schemas/${schema} +done + +echo "dn: ${ldap_root} +objectClass: dcObject +objectClass: organization +dc: $DC +o: Kaz" | ldapadd -H ldap://$LDAP_IP -D "${BINDDN}" -w ${LDAP_ADMIN_PASSWORD} + +./update.sh + +CONFIG_IHM="${DOCK_VOL}/ldap_configSSP/_data/config.inc.php" + +updateVarInConf(){ + # $1 key + # $2 val + # $3 file + # $4 : vide => la valeur sera encadré par des guillement, sinon c'est du php + if grep -q "^\s*\$$1" "$3" ; then + echo " update ${CYAN}${BOLD}$1${NC} => $2" + # !!! les valeur ne doivent pas contenir le caractère '%' + if [ -z "$4" ]; then + sed -i -e "s%^\s*\(\$$1\s*=\).*$%\1 \"$2\";%" "$3" + else + sed -i -e "s%^\s*\(\$$1\s*=\).*$%\1 $2;%" "$3" + fi + else + echo " add ${CYAN}${BOLD}$1${NC} => $2" + if [ -z "$4" ]; then + echo "\$$1 = \"$2\";" >> "$3" + else + echo "\$$1 = $2;" >> "$3" + fi + fi +} + +updateVarInConf "ldap_url" "${LDAPUI_URI}" "${CONFIG_IHM}" +updateVarInConf "ldap_binddn" "${LDAPUI_ADMIN_BIND_DN}" "${CONFIG_IHM}" +updateVarInConf "ldap_bindpw" "${LDAPUI_ADMIN_BIND_PWD}" "${CONFIG_IHM}" +updateVarInConf "ldap_base" "${LDAPUI_BASE_DN}" "${CONFIG_IHM}" +updateVarInConf "ldap_login_attribute" "cn" "${CONFIG_IHM}" +updateVarInConf "hash" "CRYPT" "${CONFIG_IHM}" +updateVarInConf "use_questions" "false" "${CONFIG_IHM}" "php" +updateVarInConf "mail_from" "admin@${domain}" "${CONFIG_IHM}" +updateVarInConf "mail_from_name" "Récupération de mot de passe Kaz" "${CONFIG_IHM}" +updateVarInConf "mail_smtp_host" "${smtpHost}.${domain}" "${CONFIG_IHM}" +updateVarInConf "use_sms" "false" "${CONFIG_IHM}" "php" +updateVarInConf "keyphrase" "${LDAPUI_PASSWORD}" "${CONFIG_IHM}" +updateVarInConf "lang" "fr" "${CONFIG_IHM}" +updateVarInConf "allowed_lang" "array('fr', 'br');" "${CONFIG_IHM}" "php" +updateVarInConf "mail_smtp_secure" "tls" "${CONFIG_IHM}" +updateVarInConf "mail_address_use_ldap" "true" "${CONFIG_IHM}" +updateVarInConf "mail_attributes" "array(\"mailDeSecours\", \"mail\")" "${CONFIG_IHM}" "php" +updateVarInConf "pwd_min_length" "10" "${CONFIG_IHM}" +updateVarInConf "pwd_min_special" "2" "${CONFIG_IHM}" +updateVarInConf "pwd_show_policy" "always" "${CONFIG_IHM}" +updateVarInConf "posthook" "/var/www/kaz/post-hook.sh" "${CONFIG_IHM}" +updateVarInConf "posthook_password_encodebase64" "true" "${CONFIG_IHM}" + + +docker cp "${KAZ_BIN_DIR}/look/kaz/kaz-tete.png" "${ldapUIName}:/var/www/html/images/ltb-logo.png" diff --git a/dockers/ldap/schemas/kaz.ldif b/dockers/ldap/schemas/kaz.ldif new file mode 100644 index 0000000..2b0fb89 --- /dev/null +++ b/dockers/ldap/schemas/kaz.ldif @@ -0,0 +1,11 @@ +# On crée un schéma vide, qui sera peuplé ensuite par update.sh +# L'attribut mailDeSecours sert juste à bloquer la re-création si on relance + +dn: cn=kaz,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: kaz +olcAttributeTypes: {0}( 1.3.6.1.4.1.5656.1.1.1 NAME 'mailDeSecours' + DESC 'Adresse mail de secours' + EQUALITY caseIgnoreIA5Match + SUBSTR caseIgnoreIA5SubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{320} ) diff --git a/dockers/ldap/schemas/nextcloud.ldif b/dockers/ldap/schemas/nextcloud.ldif new file mode 100644 index 0000000..e40484c --- /dev/null +++ b/dockers/ldap/schemas/nextcloud.ldif @@ -0,0 +1,25 @@ +dn: cn=nextcloud,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: nextcloud +olcAttributeTypes: {0}( 1.3.6.1.4.1.49213.1.1.1 NAME 'nextcloudEnabled' + DESC 'whether user or group should be available in Nextcloud' + EQUALITY caseIgnoreMatch + SUBSTR caseIgnoreSubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE) +olcAttributeTypes: {1}( 1.3.6.1.4.1.49213.1.1.2 NAME 'nextcloudQuota' + DESC 'defines how much disk space is available for the user' + EQUALITY caseIgnoreMatch + SUBSTR caseIgnoreSubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE) +olcObjectClasses: {0}( 1.3.6.1.4.1.49213.1.2.1 NAME 'nextcloudAccount' + DESC 'A Nextcloud account' + SUP top AUXILIARY + MUST ( cn ) + MAY ( nextcloudEnabled $ nextcloudQuota ) + ) +olcObjectClasses: {1}( 1.3.6.1.4.1.49213.1.2.2 NAME 'nextcloudGroup' + DESC 'A Nextcloud group' + SUP top AUXILIARY + MUST ( cn ) + MAY ( nextcloudEnabled ) + ) diff --git a/dockers/ldap/schemas/postfixbook.ldif b/dockers/ldap/schemas/postfixbook.ldif new file mode 100644 index 0000000..7868ed4 --- /dev/null +++ b/dockers/ldap/schemas/postfixbook.ldif @@ -0,0 +1,41 @@ +dn: cn=postfixbook,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: postfixbook +olcAttributeTypes: {0}( 1.3.6.1.4.1.29426.1.10.1 NAME 'mailHomeDirectory' DE + SC 'The absolute path to the mail user home directory' EQUALITY caseExactIA + 5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) +olcAttributeTypes: {1}( 1.3.6.1.4.1.29426.1.10.2 NAME 'mailAlias' DESC 'RFC8 + 22 Mailbox - mail alias' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5Su + bstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} ) +olcAttributeTypes: {2}( 1.3.6.1.4.1.29426.1.10.3 NAME 'mailUidNumber' DESC ' + UID required to access the mailbox' EQUALITY integerMatch SYNTAX 1.3.6.1.4. + 1.1466.115.121.1.27 SINGLE-VALUE ) +olcAttributeTypes: {3}( 1.3.6.1.4.1.29426.1.10.4 NAME 'mailGidNumber' DESC ' + GID required to access the mailbox' EQUALITY integerMatch SYNTAX 1.3.6.1.4. + 1.1466.115.121.1.27 SINGLE-VALUE ) +olcAttributeTypes: {4}( 1.3.6.1.4.1.29426.1.10.5 NAME 'mailEnabled' DESC 'TR + UE to enable, FALSE to disable account' EQUALITY booleanMatch SYNTAX 1.3.6. + 1.4.1.1466.115.121.1.7 SINGLE-VALUE ) +olcAttributeTypes: {5}( 1.3.6.1.4.1.29426.1.10.6 NAME 'mailGroupMember' DESC + 'Name of a mail distribution list' EQUALITY caseExactIA5Match SYNTAX 1.3.6 + .1.4.1.1466.115.121.1.26 ) +olcAttributeTypes: {6}( 1.3.6.1.4.1.29426.1.10.7 NAME 'mailQuota' DESC 'Mail + quota limit in kilobytes' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.14 + 66.115.121.1.26 ) +olcAttributeTypes: {7}( 1.3.6.1.4.1.29426.1.10.8 NAME 'mailStorageDirectory' + DESC 'The absolute path to the mail users mailbox' EQUALITY caseExactIA5Ma + tch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) +olcAttributeTypes: {8}( 1.3.6.1.4.1.29426.1.10.9 NAME 'mailSieveRuleSource' + DESC 'Sun ONE Messaging Server defined attribute' SYNTAX 1.3.6.1.4.1.1466.1 + 15.121.1.26 X-ORIGIN 'Sun ONE Messaging Server' ) +olcAttributeTypes: {9}( 1.3.6.1.4.1.29426.1.10.10 NAME 'mailForwardingAddres + s' DESC 'Address(es) to forward all incoming messages to.' EQUALITY caseIgn + oreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{320} ) +olcObjectClasses: {0}( 1.3.6.1.4.1.29426.1.2.2.1 NAME 'PostfixBookMailAccoun + t' DESC 'Mail account used in Postfix Book' SUP top AUXILIARY MUST mail MAY + ( mailHomeDirectory $ mailAlias $ mailGroupMember $ mailUidNumber $ mailGi + dNumber $ mailEnabled $ mailQuota $ mailStorageDirectory $ mailSieveRuleSou + rce ) ) +olcObjectClasses: {1}( 1.3.6.1.4.1.29426.1.2.2.2 NAME 'PostfixBookMailForwar + d' DESC 'Mail forward used in Postfix Book' SUP top AUXILIARY MUST ( mail $ + mailAlias ) MAY mailForwardingAddress ) diff --git a/dockers/ldap/update.sh b/dockers/ldap/update.sh new file mode 100755 index 0000000..7b48637 --- /dev/null +++ b/dockers/ldap/update.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +SERV_DIR=$(cd $(dirname $0); pwd) +KAZ_ROOT=$(cd $(dirname $0)/../..; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +cd $(dirname $0) +. "${DOCKERS_ENV}" +. "${KAZ_KEY_DIR}/env-${ldapServName}" + +checkDockerRunning "${ldapServName}" "LDAP" || exit + +printKazMsg "\n *** Update du LDAP" + +LDAP_IP=$(docker inspect -f '{{.NetworkSettings.Networks.ldapNet.IPAddress}}' ldapServ) + +BINDDN=cn=${LDAP_ADMIN_USERNAME},${ldap_root} +DC=$(echo ${ldap_root} | cut -d',' -f1 | cut -d'=' -f2) + +cp base/acl.ldif.tmpl /tmp/acl.ldif +sed -i -e "s/\$BINDDN/${BINDDN}/g" /tmp/acl.ldif +sed -i -e "s/\$LDAPROOT/${ldap_root}/g" /tmp/acl.ldif + +cp base/skeleton.ldif.tmpl /tmp/skeleton.ldif +sed -i -e "s/\$LDAPROOT/${ldap_root}/g" /tmp/skeleton.ldif +sed -i -e "s%\$POSTFIX_PASSWORD%\{CRYPT\}`mkpasswd -m sha512crypt ${LDAP_POSTFIX_PASSWORD}`%g" /tmp/skeleton.ldif +sed -i -e "s%\$LDAPUI_PASSWORD%\{CRYPT\}`mkpasswd -m sha512crypt ${LDAP_LDAPUI_PASSWORD}`%g" /tmp/skeleton.ldif +sed -i -e "s%\$MATTERMOST_PASSWORD%\{CRYPT\}`mkpasswd -m sha512crypt ${LDAP_MATTERMOST_PASSWORD}`%g" /tmp/skeleton.ldif +sed -i -e "s%\$CLOUD_PASSWORD%\{CRYPT\}`mkpasswd -m sha512crypt ${LDAP_CLOUD_PASSWORD}`%g" /tmp/skeleton.ldif +sed -i -e "s%\$MOBILIZON_PASSWORD%\{CRYPT\}`mkpasswd -m sha512crypt ${LDAP_MOBILIZON_PASSWORD}`%g" /tmp/skeleton.ldif + +cp base/kaz-schema.ldif.tmpl /tmp/kaz-schema.ldif +KAZNUMBER=$(ldapsearch -H ldap://$LDAP_IP -D "cn=${LDAP_CONFIG_ADMIN_USERNAME},cn=config" -w ${LDAP_CONFIG_ADMIN_PASSWORD} -b cn=schema,cn=config | grep "kaz,cn=schema" | head -n1 | cut -d',' -f1 | cut -d'{' -f2 | cut -d'}' -f1) +sed -i -e "s/\$KAZNUMBER/${KAZNUMBER}/g" /tmp/kaz-schema.ldif + + +ldapmodify -H ldap://$LDAP_IP -D "cn=${LDAP_CONFIG_ADMIN_USERNAME},cn=config" -w ${LDAP_CONFIG_ADMIN_PASSWORD} -f /tmp/acl.ldif +ldapmodify -H ldap://$LDAP_IP -D "cn=${LDAP_CONFIG_ADMIN_USERNAME},cn=config" -w ${LDAP_CONFIG_ADMIN_PASSWORD} -f /tmp/kaz-schema.ldif +ldapadd -c -H ldap://$LDAP_IP -D "${BINDDN}" -w ${LDAP_ADMIN_PASSWORD} -f /tmp/skeleton.ldif diff --git a/dockers/mattermost/.env b/dockers/mattermost/.env new file mode 120000 index 0000000..406acd1 --- /dev/null +++ b/dockers/mattermost/.env @@ -0,0 +1 @@ +../../config/dockers.env \ No newline at end of file diff --git a/dockers/mattermost/docker-compose.yml b/dockers/mattermost/docker-compose.yml new file mode 100644 index 0000000..04236c1 --- /dev/null +++ b/dockers/mattermost/docker-compose.yml @@ -0,0 +1,94 @@ +version: "3" + +services: + + app: + image: mattermost/mattermost-team-edition:9.8.0 + container_name: ${mattermostServName} + restart: ${restartPolicy} + # memory: 1G + # disk_quota: 256M + volumes: + - matterConfig:/mattermost/config:rw +# - matterConfigLangSrv:/mattermost/i18n:rw +# - matterConfigLangClt:/mattermost/client/i18n:rw + - matterData:/mattermost/data:rw + - matterLogs:/mattermost/logs:rw + - matterPlugins:/mattermost/plugins:rw + - matterClientPlugins:/mattermost/client/plugins:rw +# - matterIcons:/mattermost/client/images/ +# - matterI18n:/mattermost/i18n:rw + - /etc/ssl:/etc/ssl:ro + - /etc/localtime:/etc/localtime:ro + - /etc/timezone:/etc/timezone:ro + - /etc/environment:/etc/environment:ro + env_file: + - ../../secret/env-${mattermostServName} + environment: + - VIRTUAL_HOST=mattermost.${domain} + - MM_PASSWORDSETTINGS_MINIMUMLENGTH=10 + - MM_PASSWORDSETTINGS_LOWERCASE=false + - MM_PASSWORDSETTINGS_UPPERCASE=false + - MM_PASSWORDSETTINGS_NUMBER=false + - MM_PASSWORDSETTINGS_SYMBOL=true + # in case your config is not in default location + #- MM_CONFIG=/mattermost/config/config.json + + depends_on: + - db + links: + - db + expose: + - ${matterPort} + # ports: + # - 8089:80 + networks: + - mattermostNet + - postfixNet + external_links: + - ${smtpServName}:${smtpHost}.${domain} + labels: + - "traefik.enable=true" + - "traefik.http.routers.${mattermostServName}.rule=Host(`${matterHost}.${domain}`)" + - "traefik.http.services.${mattermostServName}.loadbalancer.server.port=${matterPort}" + - "traefik.docker.network=mattermostNet" + healthcheck: + test: ["CMD", "curl", "-f", "http://app:${matterPort}"] + interval: 20s + retries: 10 + start_period: 20s + timeout: 10s + + db: + image: mariadb:10.5 + container_name: ${mattermostDBName} + restart: ${restartPolicy} + networks: + - mattermostNet + env_file: + - ../../secret/env-${mattermostDBName} + volumes: + - matterDB:/var/lib/mysql + - /home/sauve/:/svg/ + - /etc/localtime:/etc/localtime:ro + - /etc/timezone:/etc/timezone:ro + +volumes: + matterDB: + matterConfig: + matterData: + matterLogs: + matterPlugins: + matterClientPlugins: + matterConfigLangSrv: + matterConfigLangClt: +# matterI18n: +# matterIcons: + +networks: + mattermostNet: + external: true + name: mattermostNet + postfixNet: + external: true + name: postfixNet diff --git a/dockers/mattermost/first.sh b/dockers/mattermost/first.sh new file mode 100755 index 0000000..46b81be --- /dev/null +++ b/dockers/mattermost/first.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +KAZ_ROOT=$(cd $(dirname $0)/../..; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +cd $(dirname $0) +. "${DOCKERS_ENV}" +. "${KAZ_KEY_DIR}/SetAllPass.sh" + +"${KAZ_BIN_DIR}/gestContainers.sh" --install -M -agora + + diff --git a/dockers/mobilizon/.env b/dockers/mobilizon/.env new file mode 120000 index 0000000..406acd1 --- /dev/null +++ b/dockers/mobilizon/.env @@ -0,0 +1 @@ +../../config/dockers.env \ No newline at end of file diff --git a/dockers/mobilizon/config.exs b/dockers/mobilizon/config.exs new file mode 100644 index 0000000..caece0b --- /dev/null +++ b/dockers/mobilizon/config.exs @@ -0,0 +1,98 @@ +# Mobilizon instance configuration + +import Config + +listen_ip = System.get_env("MOBILIZON_INSTANCE_LISTEN_IP", "0.0.0.0") + +listen_ip = + case listen_ip |> to_charlist() |> :inet.parse_address() do + {:ok, listen_ip} -> listen_ip + _ -> raise "MOBILIZON_INSTANCE_LISTEN_IP does not match the expected IP format." + end + +config :mobilizon, Mobilizon.Web.Endpoint, + server: true, + url: [host: System.get_env("MOBILIZON_INSTANCE_HOST", "mobilizon.lan")], + http: [ + port: String.to_integer(System.get_env("MOBILIZON_INSTANCE_PORT", "4000")), + ip: listen_ip + ], + secret_key_base: System.get_env("MOBILIZON_INSTANCE_SECRET_KEY_BASE", "changethis") + +config :mobilizon, Mobilizon.Web.Auth.Guardian, + secret_key: System.get_env("MOBILIZON_INSTANCE_SECRET_KEY", "changethis") + +config :mobilizon, :instance, + name: System.get_env("MOBILIZON_INSTANCE_NAME", "Mobilizon"), + description: "Change this to a proper description of your instance", + hostname: System.get_env("MOBILIZON_INSTANCE_HOST", "mobilizon.lan"), + registrations_open: System.get_env("MOBILIZON_INSTANCE_REGISTRATIONS_OPEN", "false") == "true", + demo: false, + allow_relay: true, + federating: true, + email_from: System.get_env("MOBILIZON_INSTANCE_EMAIL", "noreply@mobilizon.lan"), + email_reply_to: System.get_env("MOBILIZON_REPLY_EMAIL", "noreply@mobilizon.lan") + +config :mobilizon, Mobilizon.Storage.Repo, + adapter: Ecto.Adapters.Postgres, + username: System.get_env("MOBILIZON_DATABASE_USERNAME", "username"), + password: System.get_env("MOBILIZON_DATABASE_PASSWORD", "password"), + database: System.get_env("MOBILIZON_DATABASE_DBNAME", "mobilizon"), + hostname: System.get_env("MOBILIZON_DATABASE_HOST", "postgres"), + port: System.get_env("MOBILIZON_DATABASE_PORT", "5432"), + pool_size: 10 + +config :mobilizon, Mobilizon.Web.Email.Mailer, + adapter: Swoosh.Adapters.SMTP, + relay: System.get_env("MOBILIZON_SMTP_SERVER", "localhost"), + port: System.get_env("MOBILIZON_SMTP_PORT", "25"), + username: System.get_env("MOBILIZON_SMTP_USERNAME", nil), + password: System.get_env("MOBILIZON_SMTP_PASSWORD", nil), + tls: :if_available, + allowed_tls_versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"], + ssl: System.get_env("MOBILIZON_SMTP_SSL", "false"), + retries: 1, + no_mx_lookups: false, + auth: :if_available + +config :geolix, + databases: [ + %{ + id: :city, + adapter: Geolix.Adapter.MMDB2, + source: "/var/lib/mobilizon/geo_db/GeoLite2-City.mmdb" + } + ] + +config :mobilizon, Mobilizon.Web.Upload.Uploader.Local, + uploads: System.get_env("MOBILIZON_UPLOADS", "/var/lib/mobilizon/uploads") + +config :mobilizon, :exports, + path: System.get_env("MOBILIZON_UPLOADS_EXPORTS", "/var/lib/mobilizon/uploads/exports"), + formats: [ + Mobilizon.Service.Export.Participants.CSV, + Mobilizon.Service.Export.Participants.PDF, + Mobilizon.Service.Export.Participants.ODS + ] + +config :tz_world, + data_dir: System.get_env("MOBILIZON_TIMEZONES_DIR", "/var/lib/mobilizon/timezones") + +config :mobilizon, + Mobilizon.Service.Auth.Authenticator, + Mobilizon.Service.Auth.LDAPAuthenticator + +config :mobilizon, :ldap, + enabled: true, + host: System.get_env("MOBILIZON_LDAP_HOST", "ldap"), + port: 389, + ssl: false, + sslopts: [], + tls: false, + tlsopts: [], + base: System.get_env("MOBILIZON_LDAP_BASE", ""), + uid: System.get_env("MOBILIZON_LDAP_UID", "cn"), + require_bind_for_search: true, + group: false, + bind_uid: {:full, System.get_env("MOBILIZON_LDAP_BINDUID", "nobody")}, + bind_password: System.get_env("MOBILIZON_LDAP_BINDPASSWORD", "nobody") diff --git a/dockers/mobilizon/docker-compose.yml b/dockers/mobilizon/docker-compose.yml new file mode 100644 index 0000000..d4fb128 --- /dev/null +++ b/dockers/mobilizon/docker-compose.yml @@ -0,0 +1,59 @@ +version: "3.9" + +services: + mobilizon: + image: framasoft/mobilizon:latest + container_name: ${mobilizonServName} + restart: ${restartPolicy} + depends_on: + - db + env_file: + - ../../secret/env-${mobilizonServName} + environment: + - MOBILIZON_INSTANCE_PORT=80 + - MOBILIZON_DATABASE_HOST=db + - MOBILIZON_LDAP_HOST=ldap + - MOBILIZON_LDAP_BASE=ou=users,${ldap_root} + - MOBILIZON_LDAP_UID=cn + expose: + - 80 + volumes: + - mobilizonUploads:/var/lib/mobilizon/uploads + - ${PWD}/config.exs:/etc/mobilizon/config.exs:ro + # - ${PWD}/GeoLite2-City.mmdb:/var/lib/mobilizon/geo_db/GeoLite2-City.mmdb + networks: + - mobilizonNet + - postfixNet + - ldapNet + labels: + - "traefik.enable=true" + - "traefik.http.routers.${mobilizonServName}.rule=Host(`${mobilizonHost}.${domain}`)" + - "traefik.http.services.${mobilizonServName}.loadbalancer.server.port=80" + - "traefik.docker.network=mobilizonNet" + + db: + image: postgis/postgis:13-3.1 + container_name: ${mobilizonDBName} + restart: ${restartPolicy} + env_file: + - ../../secret/env-${mobilizonDBName} + volumes: + - mobilizonDB:/var/lib/postgresql/data + networks: + - mobilizonNet + + +volumes: + mobilizonDB: + mobilizonUploads: + +networks: + mobilizonNet: + external: true + name: mobilizonNet + postfixNet: + external: true + name: postfixNet + ldapNet: + external: true + name: ldapNet diff --git a/dockers/paheko/.env b/dockers/paheko/.env new file mode 120000 index 0000000..406acd1 --- /dev/null +++ b/dockers/paheko/.env @@ -0,0 +1 @@ +../../config/dockers.env \ No newline at end of file diff --git a/dockers/paheko/Dockerfile b/dockers/paheko/Dockerfile new file mode 100644 index 0000000..0e7ffd0 --- /dev/null +++ b/dockers/paheko/Dockerfile @@ -0,0 +1,94 @@ +FROM php:8.2-apache + +# pour upgrader paheko, il suffit de modifier le numéro de version ici +ENV PAHEKO_VERSION 1.3.9 + +ENV PAHEKO_DOWNLOAD_DIR download/paheko +ENV PAHEKO_DIR /usr/share/paheko + +######################################## +# APT local cache +# work around because COPY failed if no source file +#COPY .dummy .apt-mirror-confi[g] .proxy-confi[g] / +#RUN cp /.proxy-config /etc/profile.d/proxy.sh 2> /dev/null || true +#RUN if [ -f /.apt-mirror-config ] ; then . /.apt-mirror-config && sed -i \ +# -e "s/deb.debian.org/${APT_MIRROR_DEBIAN}/g" \ +# -e "s/security.debian.org/${APT_MIRROR_DEBIAN_SECURITY}/g" \ +# -e "s/archive.ubuntu.com/${APT_MIRROR_UBUNTU}/g" \ +# -e "s/security.ubuntu.com/${APT_MIRROR_UBUNTU_SECURITY}/g" \ +# /etc/apt/sources.list; fi + +######################################## +RUN apt-get update --quiet && apt-get install -y \ + libicu-dev libpq-dev zlib1g-dev libicu-dev \ + libzip-dev git wget zip patch imagemagick libmagickwand-dev; \ + pecl install imagick; \ + docker-php-ext-install zip; \ + docker-php-ext-enable imagick; + +#install composer setup script +COPY dockers/paheko/composer-setup.sh /usr/local/bin/ +RUN chmod +x /usr/local/bin/composer-setup.sh + +#install internationalization libs +RUN docker-php-ext-configure intl +RUN docker-php-ext-install intl +RUN docker-php-ext-install -j$(nproc) pdo ctype +RUN /usr/local/bin/composer-setup.sh + +ADD "${PAHEKO_DOWNLOAD_DIR}/paheko-${PAHEKO_VERSION}.tar.gz" /usr/share/ +RUN mv /usr/share/paheko-${PAHEKO_VERSION} ${PAHEKO_DIR} + + +RUN echo '\n\ + Options -MultiViews\n\ +\n\ +\n\ +php_value upload_max_filesize 100M\n\ +php_value post_max_size 100M\n\ +php_value memory_limit 256M\n\ +FallbackResource /_route.php\n\ +'> ${PAHEKO_DIR}/www/.htaccess + +#install prince pour génération pdf +RUN curl https://www.princexml.com/download/prince_20230609-1_debian12_amd64.deb --output prince.deb +RUN apt-get install libdeflate0 libjbig0 libjpeg62-turbo -y + +#on récupère 2 paquets de sécu sur bullseyes (me demande pas pourquoi) +RUN curl http://security.debian.org/debian-security/pool/updates/main/libw/libwebp/libwebp6_0.6.1-2.1+deb11u2_amd64.deb --output libwebp6_0.6.1.deb +RUN curl http://security.debian.org/debian-security/pool/updates/main/o/openssl/libssl1.1_1.1.1n-0+deb11u5_amd64.deb --output libssl1.1_1.1.deb +RUN dpkg -i libwebp6_0.6.1.deb +RUN dpkg -i libssl1.1_1.1.deb +RUN apt-get install libfontconfig1 libgif7 libjpeg62-turbo liblcms2-2 libtiff5-dev -y +RUN apt-get install libaom3 libavif15 -y +RUN dpkg -i prince.deb + +COPY dockers/paheko/config/factory_cron.sh ${PAHEKO_DIR}/ +COPY dockers/paheko/config/factory_cron_emails.sh ${PAHEKO_DIR}/ +COPY dockers/paheko/config/setupWebRights.sh ${PAHEKO_DIR}/ +RUN mkdir ${PAHEKO_DIR}/users + +#Plugin facturation (le seul qui ne fasse pas parti de la distribution de base +COPY "dockers/paheko/config/facturation.tar.gz" ${PAHEKO_DIR}/data/plugins/ +RUN mkdir ${PAHEKO_DIR}/data/plugins/facturation && tar zxvf ${PAHEKO_DIR}/data/plugins/facturation.tar.gz -C ${PAHEKO_DIR}/data/plugins/facturation && rm ${PAHEKO_DIR}/data/plugins/facturation.tar.gz + +#fab: pour eviter l'anomalie: $msg mustn't contain any NULL byte +#RUN sed -i "394i\$msg = str_replace(chr(0), '', \$msg);" ${PAHEKO_DIR}/include/lib/KD2/ErrorManager.php + +#install cron pour factory_cron.sh +RUN apt-get install cron joe rsyslog -y +RUN sed -i '/imklog/s/^/#/' /etc/rsyslog.conf +RUN echo "0 1 * * * cd ${PAHEKO_DIR} && ${PAHEKO_DIR}/factory_cron.sh" >> /var/spool/cron/crontabs/root +RUN echo "* * * * * cd ${PAHEKO_DIR} && ${PAHEKO_DIR}/factory_cron_emails.sh" >> /var/spool/cron/crontabs/root +RUN chmod 600 /var/spool/cron/crontabs/root +RUN sed -i "/exec/i\service cron start\n" /usr/local/bin/docker-php-entrypoint +RUN ln -sf /usr/local/bin/php /usr/bin/ + +# Set owner web server +RUN chown -R www-data: ${PAHEKO_DIR} + +#fab le 03/02/2023: pour avoir un php.ini de prod avec zend.assertions = -1 +RUN cp "/usr/local/etc/php/php.ini-production" "/usr/local/etc/php/php.ini" + +EXPOSE 80 +VOLUME [ "/usr/share/paheko/users" ] diff --git a/dockers/paheko/build.sh b/dockers/paheko/build.sh new file mode 100755 index 0000000..e768b07 --- /dev/null +++ b/dockers/paheko/build.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +KAZ_ROOT=$(cd "$(dirname $0)/../.."; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +###########################################################################" +#on récupère la dernière version de paheko et tous les plugin + +PAHEKO_VER=$(grep "^ENV PAHEKO_VERSION" Dockerfile | awk '{print $3}') +#FCT_VER="0.8.5" +echo "VERSION PAHEKO : $PAHEKO_VER" +#echo "VERSION FACTURATION : $FCT_VER" + +downloadFile () { + # $1 URL to download + if [ ! -f "$(basename "$1")" ]; then + printKazMsg " - load $1" + curl -L -O "$1" + fi +} + +mkdir -p "${KAZ_DNLD_DIR}/paheko" +cd "${KAZ_DNLD_DIR}/paheko" + +#pour être certain de récupérer les dernières versions de plugin +rm * -f + +printKazMsg "\n *** Download paheko on ${KAZ_DNLD_DIR}" + +downloadFile "https://fossil.kd2.org/paheko/uv/paheko-${PAHEKO_VER}.tar.gz" + +########################################################################### +printKazMsg "\n *** Création du Dockerfile paheko" + +cd "${KAZ_ROOT}" + +docker build -t pahekokaz . -f dockers/paheko/Dockerfile diff --git a/dockers/paheko/composer-setup.sh b/dockers/paheko/composer-setup.sh new file mode 100755 index 0000000..1eb0ad7 --- /dev/null +++ b/dockers/paheko/composer-setup.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +EXPECTED_SIGNATURE=$(wget -q -O - https://composer.github.io/installer.sig) +php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" +ACTUAL_SIGNATURE=$(php -r "echo hash_file('SHA384', 'composer-setup.php');") + +if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ] +then + >&2 echo 'ERROR: Invalid installer signature' + rm composer-setup.php + exit 1 +fi + +php composer-setup.php --quiet +RESULT=$? +rm composer-setup.php +exit $RESULT + diff --git a/dockers/paheko/config/config.local.tmpl.php b/dockers/paheko/config/config.local.tmpl.php new file mode 100644 index 0000000..b2113da --- /dev/null +++ b/dockers/paheko/config/config.local.tmpl.php @@ -0,0 +1,129 @@ +Page non trouv??e'); +} + +$user_data_dir = rtrim(FACTORY_USER_DIRECTORY, '/') . '/' . $login; + + +if (!is_dir($user_data_dir)) { + mkdir($user_data_dir, 0700, true); + //http_response_code(404); + //die("

Cette association n'existe pas.

"); +} + +// D??finir le dossier o?? sont stock??s les donn??es +define('Paheko\DATA_ROOT', $user_data_dir); + +const PREFER_HTTPS = false; +const SMTP_HOST = "__SMTP_HOST__.__DOMAIN__"; +const SMTP_USER = null; +const API_USER = "__PAHEKO_API_USER__"; +const API_PASSWORD = "__PAHEKO_API_PASSWORD__"; +const SMTP_PASSWORD = null; +const SMTP_PORT = 25; +const SMTP_SECURITY = 'NONE'; +const ROOT = __DIR__; + +const DB_FILE = DATA_ROOT . '/association.sqlite'; +//const PLUGINS_ROOT = DATA_ROOT . '/plugins'; +const PLUGINS_ROOT = __DIR__ . '/data/plugins'; + +// D??finir l'URL +//original +//define('Garradin\WWW_URL', 'https://' . $login . FACTORY_USER_DIRECTORY . '/'); +//maj kaz +define('Paheko\WWW_URL', "__HTTP_PROTO__://".$login."-paheko.__DOMAIN__/"); + +define('Paheko\WWW_URI', '/'); + +// Désactiver le log des erreurs visible dans l'interface (sécurité) +define('Paheko\ERRORS_ENABLE_LOG_VIEW', true); + +// Ne pas afficher les erreurs de code +define('Paheko\SHOW_ERRORS', true); + + +#add by fab le 21/04/2022 +//const PDF_COMMAND = 'prince'; +const PDF_COMMAND = 'auto'; diff --git a/dockers/paheko/config/factory_cron.sh b/dockers/paheko/config/factory_cron.sh new file mode 100755 index 0000000..05bb4ec --- /dev/null +++ b/dockers/paheko/config/factory_cron.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +# Répertoire où sont stockées les données des utilisateurs +# veiller à ce que ce soit le même que dans config.local.php +FACTORY_USER_DIRECTORY="users" + +# Chemin vers le script cron.php de Paheko +PAHEKO_CRON_SCRIPT="scripts/cron.php" + +cd /usr/share/paheko + +for user in $(cd ${FACTORY_USER_DIRECTORY} && ls -1d */) +do + PAHEKO_FACTORY_USER=$(basename "$user") /usr/bin/php $PAHEKO_CRON_SCRIPT + echo $PAHEKO_FACTORY_USER +done + diff --git a/dockers/paheko/config/factory_cron_emails.sh b/dockers/paheko/config/factory_cron_emails.sh new file mode 100755 index 0000000..eeed1b6 --- /dev/null +++ b/dockers/paheko/config/factory_cron_emails.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +# Répertoire où sont stockées les données des utilisateurs +# veiller à ce que ce soit le même que dans config.local.php +FACTORY_USER_DIRECTORY="users" + +# Chemin vers le script emails.php de Paheko +PAHEKO_CRON_SCRIPT="scripts/emails.php" + +for user in $(cd ${FACTORY_USER_DIRECTORY} && ls -1d */) +do + PAHEKO_FACTORY_USER=$(basename "$user") /usr/bin/php $PAHEKO_CRON_SCRIPT + echo $PAHEKO_FACTORY_USER +done diff --git a/dockers/paheko/config/facturation.tar.gz b/dockers/paheko/config/facturation.tar.gz new file mode 100644 index 0000000..52deac4 Binary files /dev/null and b/dockers/paheko/config/facturation.tar.gz differ diff --git a/dockers/paheko/config/paheko.conf b/dockers/paheko/config/paheko.conf new file mode 100644 index 0000000..c61c2ff --- /dev/null +++ b/dockers/paheko/config/paheko.conf @@ -0,0 +1,26 @@ + + #ServerName paheko.kaz.bzh + DocumentRoot /usr/share/paheko/www + ErrorLog /var/log/apache2/error.log + CustomLog /var/log/apache2/access.log combined + ServerSignature Off + + + AllowOverride All + Require all granted + Options FollowSymLinks MultiViews + + Dav off + + + + + deny from all + satisfy all + ErrorDocument 403 "Access denied." + + +FallbackResource /_route.php + + + diff --git a/dockers/paheko/config/setupWebRights.sh b/dockers/paheko/config/setupWebRights.sh new file mode 100755 index 0000000..098d611 --- /dev/null +++ b/dockers/paheko/config/setupWebRights.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +chown -R www-data: /usr/share/paheko diff --git a/dockers/paheko/docker-compose-gen.sh b/dockers/paheko/docker-compose-gen.sh new file mode 100755 index 0000000..e1c51d2 --- /dev/null +++ b/dockers/paheko/docker-compose-gen.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +#koi : recréer le docker-compose.yml de paheko en insérant les routes traefik pour toutes les orga qui ont un paheko +#kan : 04/12/2023 +#ki : fab + +KAZ_ROOT=$(cd "$(dirname $0)/../.."; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +file="docker-compose.yml" + +#je construis les labels +TMPFILE=$(mktemp /tmp/paheko-XXXXX) +echo " - \"traefik.enable=true\"" > $TMPFILE +echo " - \"traefik.frontend.rule=HostRegexp:{host:[a-zA-Z0-9_-]+-${pahekoHost}}.${domain}\"" >> $TMPFILE +for orga in $(getPahekoOrgaList); do + echo " - \"traefik.http.routers."$orga".rule=Host(\`"$orga"-paheko.kaz.bzh\`)\"" >> $TMPFILE +done + +#je remet TMPFILE entre labels: et volumes: +awk '/labels:/,/volumes:/ {if (/labels:/) {p=1; print; next} else if (p && /volumes:/) {p=0; system("cat '$TMPFILE'"); print; next} else if (p) {next} } 1' "$file" > "$file.tmp" && mv "$file.tmp" "$file" + +rm $TMPFILE diff --git a/dockers/paheko/docker-compose.yml b/dockers/paheko/docker-compose.yml new file mode 100644 index 0000000..227376e --- /dev/null +++ b/dockers/paheko/docker-compose.yml @@ -0,0 +1,38 @@ +version: '3.5' + +services: + + paheko: + image: pahekokaz + build: . + container_name: ${pahekoServName} + volumes: + - ./config/paheko.conf:/etc/apache2/sites-available/000-default.conf + - ./config/config.local.php:/usr/share/paheko/config.local.php + - ./config/factory_cron.sh:/usr/share/paheko/factory_cron.sh +# - ./config/cron.php:/usr/share/paheko/scripts/cron.php + - assoUsers:/usr/share/paheko/users + - /etc/localtime:/etc/localtime:ro + - /etc/timezone:/etc/timezone:ro + # ports: + # - 8086:80 + networks: + - pahekoNet + - postfixNet + external_links: + - ${smtpServName}:${smtpHost}.${domain} + labels: + - "traefik.enable=true" + - "traefik.frontend.rule=HostRegexp:{host:[a-zA-Z0-9_-]+-${pahekoHost}}.${domain}" + - "traefik.http.routers.${pahekoServName}.rule=HostRegexp(`{host:[a-zA-Z0-9_-]+-${pahekoHost}}.${domain}`)" + +volumes: + assoUsers: + +networks: + pahekoNet: + external: true + name: pahekoNet + postfixNet: + external: true + name: postfixNet diff --git a/dockers/paheko/first.sh b/dockers/paheko/first.sh new file mode 100755 index 0000000..046938d --- /dev/null +++ b/dockers/paheko/first.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +SERV_DIR=$(cd $(dirname $0); pwd) +KAZ_ROOT=$(cd $(dirname $0)/../..; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +cd $(dirname $0) +. "${DOCKERS_ENV}" + + +if ! [[ "$(docker ps -f name=${pahekoServName} | grep -w ${pahekoServName})" ]]; then + echo -e "Paheko not running... abort" + exit +fi + +printKazMsg "\n *** Premier lancement de Paheko" + +docker exec "${pahekoServName}" /usr/share/paheko/setupWebRights.sh diff --git a/dockers/paheko/paheko-gen.sh b/dockers/paheko/paheko-gen.sh new file mode 100755 index 0000000..a0214bd --- /dev/null +++ b/dockers/paheko/paheko-gen.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +KAZ_ROOT=$(cd "$(dirname $0)/../.."; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +TMPL=config/config.local.tmpl.php +CONF=config/config.local.php + +cd $(dirname $0) + +"${APPLY_TMPL}" "${TMPL}" "${CONF}" diff --git a/dockers/paheko/reload.sh b/dockers/paheko/reload.sh new file mode 100755 index 0000000..c75dc02 --- /dev/null +++ b/dockers/paheko/reload.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +echo -e "\n *** Reload Paheko" +docker exec -ti pahekoServ apachectl -k restart diff --git a/dockers/postfix/.env b/dockers/postfix/.env new file mode 120000 index 0000000..406acd1 --- /dev/null +++ b/dockers/postfix/.env @@ -0,0 +1 @@ +../../config/dockers.env \ No newline at end of file diff --git a/dockers/postfix/Dockerfile b/dockers/postfix/Dockerfile new file mode 100644 index 0000000..afce43e --- /dev/null +++ b/dockers/postfix/Dockerfile @@ -0,0 +1,65 @@ +FROM docker.io/mailserver/docker-mailserver:13.3.1 + +######################################## +# APT local cache +# work around because COPY failed if no source file +COPY .dummy .apt-mirror-confi[g] .proxy-confi[g] / +RUN cp /.proxy-config /etc/profile.d/proxy.sh 2> /dev/null || true +RUN if [ -f /.apt-mirror-config ] ; then . /.apt-mirror-config && sed -i \ + -e "s/deb.debian.org/${APT_MIRROR_DEBIAN}/g" \ + -e "s/security.debian.org/${APT_MIRROR_DEBIAN_SECURITY}/g" \ + -e "s/archive.ubuntu.com/${APT_MIRROR_UBUNTU}/g" \ + -e "s/security.ubuntu.com/${APT_MIRROR_UBUNTU_SECURITY}/g" \ + /etc/apt/sources.list; fi + +######################################## +RUN apt-get update +RUN apt-get -y autoremove +RUN apt-get install -y locales locales-all +RUN sed -i '/fr_FR.UTF-8/s/^# //g' /etc/locale.gen && locale-gen +ENV LC_ALL fr_FR.UTF-8 +ENV LANG fr_FR.UTF-8 +ENV LANGUAGE fr_FR:fr +RUN update-locale LANG=fr_FR.UTF-8 + +RUN apt-get -y install rsyslog apt-utils apg gawk altermime +RUN apt-get -y install libboost-program-options-dev libboost-system-dev libboost-filesystem-dev libcurl4-gnutls-dev +#RUN apt-get -y install emacs elpa-php-mode +RUN apt-get -y install joe vim nano mailutils bsd-mailx procps dos2unix + +# creation du user filter,son repertoire home, copie des fichiers +RUN mkdir /home/filter ; useradd -d /home/filter filter ; chown filter /home/filter + +RUN apt-get install -y --fix-missing doxygen dos2unix git build-essential make g++ libboost-program-options-dev libboost-system-dev libboost-filesystem-dev libcurl4-gnutls-dev libssl-dev +WORKDIR /home/ +RUN git clone https://git.kaz.bzh/KAZ/depollueur.git +WORKDIR /home/depollueur/ +RUN make + +RUN cp build/out/* /home/filter/ +RUN cp src/bash/* /home/filter/ + +RUN chown filter /home/filter/*; chmod 755 /home/filter/* + +# creation du repertoire filter et application des bons droits pour le filtre +RUN mkdir -p /var/log/mail; touch /var/log/mail/filter.log ; chown filter /var/log/mail/filter.log ; chmod 777 /var/log/mail/filter.log +RUN mkdir -p /var/spool/filter ; chmod 775 /var/spool/filter ; chown filter /var/spool/filter +RUN mkdir -p /var/log/mail/pb ; chmod a+rwx /var/log/mail/pb +RUN sed -i '5i/var/log/mail/filter.log' /etc/logrotate.d/rsyslog + +# modif des fichiers de postfix +RUN cat /home/filter/master.cf.update >> /etc/postfix/master.cf +RUN sed -i -e 's/reject_rbl_client bl.spamcop.net$//g' /etc/postfix/main.cf + +# pour le confort : modif du .bashrc de root +RUN sed -i 's/# alias/alias/g' /root/.bashrc +RUN /etc/init.d/postfix restart + +RUN echo "#!/bin/bash" > /entrypoint.sh +RUN echo "/usr/bin/supervisord -c /etc/supervisor/supervisord.conf" >> /entrypoint.sh +RUN chmod u+x /entrypoint.sh + +# HOTFIX DMARC +RUN chmod 777 /var/run/opendmarc/ + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/dockers/postfix/Readme.txt b/dockers/postfix/Readme.txt new file mode 100644 index 0000000..9fc9764 --- /dev/null +++ b/dockers/postfix/Readme.txt @@ -0,0 +1,14 @@ +url du docker iintial avec toutes la doc: https://github.com/tomav/docker-mailserver + +le script pour géréer la conf de postfix et les BAL : ./setup.sh help +comme l'image a changé de "tvial/docker-mailserver:latest" en "postfixkaz:latest", il faudra penser à utiliser l'option -i IMAGE_NAME avec setup.sh + +Fabrication du docker avec le fichier Dockerfile +Cela utilise + - Dockerfile : le fichier de description + +# docker build -t postfixkaz . + + +le postfix-main.cf : +virtual_alias_maps = ldap:/etc/postfix/ldap-aliases.cf, ldap:/etc/postfix/ldap-groups.cf, texthash:/etc/postfix/virtual diff --git a/dockers/postfix/build.sh b/dockers/postfix/build.sh new file mode 100755 index 0000000..0946f9a --- /dev/null +++ b/dockers/postfix/build.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +KAZ_ROOT=$(cd $(dirname $0)/../..; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +printKazMsg "\n *** Création du Dockerfile Postfix" + +cd "${KAZ_ROOT}" +docker build --no-cache -t postfixkaz . -f dockers/postfix/Dockerfile + diff --git a/dockers/postfix/config b/dockers/postfix/config new file mode 120000 index 0000000..e50d3a7 --- /dev/null +++ b/dockers/postfix/config @@ -0,0 +1 @@ +/var/lib/docker/volumes/postfix_mailConfig/_data/ \ No newline at end of file diff --git a/dockers/postfix/docker-compose.yml b/dockers/postfix/docker-compose.yml new file mode 100644 index 0000000..ecbca49 --- /dev/null +++ b/dockers/postfix/docker-compose.yml @@ -0,0 +1,64 @@ +version: '3.3' + +services: + mail: + image: postfixkaz + hostname: ${smtpHost} + domainname: ${domain} + container_name: ${smtpServName} + networks: + - postfixNet + - jirafeauNet + - ldapNet + external_links: + - ${jirafeauServName}:${fileHost} + ports: + - ${MAIN_IP}:25:25 + - ${MAIN_IP}:143:143 + - ${MAIN_IP}:587:587 + - ${MAIN_IP}:465:465 + - ${MAIN_IP}:993:993 + - ${MAIN_IP}:4190:4190 + volumes: + - mailData:/var/mail + - mailState:/var/mail-state + - mailLog:/var/log/mail + - mailConfig:/tmp/docker-mailserver/ + - filterConfig:/home/filter/config/ + - /etc/localtime:/etc/localtime:ro + - /etc/timezone:/etc/timezone:ro + - /etc/letsencrypt:/etc/letsencrypt:ro +# - /etc/ssl:/etc/ssl:ro +# - /usr/local/share/ca-certificates:/usr/local/share/ca-certificates:ro + environment: + - HOSTNAME=${smtpHost} + - DOMAINNAME=${domain} + - CONTAINER_NAME=${smtpServName} + - LDAP_SERVER_HOST=ldap://${ldapServName} # your ldap container/IP/ServerName + - LDAP_SEARCH_BASE=${ldap_root} + env_file: + - env-config + - ../../secret/env-${smtpServName} + cap_add: + - NET_ADMIN + - SYS_PTRACE + restart: always + +volumes: + mailData: + mailState: + mailLog: + mailConfig: + filterConfig: + external: true + +networks: + postfixNet: + external: true + name: postfixNet + jirafeauNet: + external: true + name: jirafeauNet + ldapNet: + external: true + name: ldapNet diff --git a/dockers/postfix/env-config b/dockers/postfix/env-config new file mode 100644 index 0000000..ae1b1fc --- /dev/null +++ b/dockers/postfix/env-config @@ -0,0 +1,430 @@ + +# ----------------------------------------------------------------------------------------------------------------------------- +# --------------------- General Settings -------------------------------------------------------------------------------------- +# ----------------------------------------------------------------------------------------------------------------------------- + +ACCOUNT_PROVISIONER=LDAP + +# empty => uses the `hostname` command to get the mail server's canonical hostname +# => Specify a fully-qualified domainname to serve mail for. This is used for many of the config features so if you can't set your hostname (e.g. you're in a container platform that doesn't let you) specify it in this environment variable. +OVERRIDE_HOSTNAME= + +# 0 => Debug disabled +# 1 => Enables debug on startup +DMS_DEBUG=0 + +# 0 => mail state in default directories +# 1 => consolidate all states into a single directory (`/var/mail-state`) to allow persistence using docker volumes +# # KAZ : modif pour permettre le cron sa spamassassin ref : https://docker-mailserver.github.io/docker-mailserver/latest/faq/#what-about-docker-datadmsmail-state-folder-varmail-state-internally +ONE_DIR=1 + +# empty => postmaster@domain.com +# => Specify the postmaster address +POSTMASTER_ADDRESS= + +# Set different options for mynetworks option (can be overwrite in postfix-main.cf) +# **WARNING**: Adding the docker network's gateway to the list of trusted hosts, e.g. using the `network` or +# `connected-networks` option, can create an open relay +# https://github.com/tomav/docker-mailserver/issues/1405#issuecomment-590106498 +# empty => localhost only +# host => Add docker host (ipv4 only) +# network => Add all docker containers (ipv4 only) +# connected-networks => Add all connected docker networks (ipv4 only) +PERMIT_DOCKER=connected-networks + +# In case you network interface differs from 'eth0', e.g. when you are using HostNetworking in Kubernetes, +# you can set NETWORK_INTERFACE to whatever interface you want. This interface will then be used. +# - **empty** => eth0 +NETWORK_INTERFACE=eth0 + +# empty => modern +# modern => Enables TLSv1.2 and modern ciphers only. (default) +# intermediate => Enables TLSv1, TLSv1.1 and TLSv1.2 and broad compatibility ciphers. +# old => NOT implemented. If you really need it, then customize the TLS ciphers overriding postfix and dovecot settings +# (https://github.com/tomav/docker-mailserver/wiki/) +TLS_LEVEL= + +# Configures the handling of creating mails with forged sender addresses. +# +# empty => (not recommended, but default for backwards compatibility reasons) +# Mail address spoofing allowed. Any logged in user may create email messages with a forged sender address. +# See also https://en.wikipedia.org/wiki/Email_spoofing +# 1 => (recommended) Mail spoofing denied. Each user may only send with his own or his alias addresses. +# Addresses with extension delimiters(http://www.postfix.org/postconf.5.html#recipient_delimiter) are not able to send messages. +SPOOF_PROTECTION=1 + +# Enables the Sender Rewriting Scheme. SRS is needed if your mail server acts as forwarder. See [postsrsd](https://github.com/roehling/postsrsd/blob/master/README.md#sender-rewriting-scheme-crash-course) for further explanation. +# - **0** => Disabled +# - 1 => Enabled +ENABLE_SRS=0 + +# 1 => Enables POP3 service +# empty => disables POP3 +ENABLE_POP3= +ENABLE_CLAMAV=0 + +# If you enable Fail2Ban, don't forget to add the following lines to your `docker-compose.yml`: +# cap_add: +# - NET_ADMIN +# Otherwise, `iptables` won't be able to ban IPs. +ENABLE_FAIL2BAN=1 + +# Fail2Ban blocktype +# drop => drop packet (send NO reply) +# reject => reject packet (send ICMP unreachable) +FAIL2BAN_BLOCKTYPE=drop + +# 1 => Enables Managesieve on port 4190 +# empty => disables Managesieve +ENABLE_MANAGESIEVE=1 + +# **enforce** => Allow other tests to complete. Reject attempts to deliver mail with a 550 SMTP reply, and log the helo/sender/recipient information. Repeat this test the next time the client connects. +# drop => Drop the connection immediately with a 521 SMTP reply. Repeat this test the next time the client connects. +# ignore => Ignore the failure of this test. Allow other tests to complete. Repeat this test the next time the client connects. This option is useful for testing and collecting statistics without blocking mail. +POSTSCREEN_ACTION=enforce + +# empty => all daemons start +# 1 => only launch postfix smtp +SMTP_ONLY= + +# Please read [the SSL page in the wiki](https://github.com/tomav/docker-mailserver/wiki/Configure-SSL) for more information. +# +# empty => SSL disabled +# letsencrypt => Enables Let's Encrypt certificates +# custom => Enables custom certificates +# manual => Let's you manually specify locations of your SSL certificates for non-standard cases +# self-signed => Enables self-signed certificates +#SSL_TYPE=self-signed +SSL_TYPE=letsencrypt +#SSL_CERT_PATH= +#SSL_KEY_PATH= + +# Set how many days a virusmail will stay on the server before being deleted +# empty => 7 days +VIRUSMAILS_DELETE_DELAY= + +# This Option is activating the Usage of POSTFIX_DAGENT to specify a lmtp client different from default dovecot socket. +# empty => disabled +# 1 => enabled +ENABLE_POSTFIX_VIRTUAL_TRANSPORT= + +# Enabled by ENABLE_POSTFIX_VIRTUAL_TRANSPORT. Specify the final delivery of postfix +# +# empty => fail +# `lmtp:unix:private/dovecot-lmtp` (use socket) +# `lmtps:inet::` (secure lmtp with starttls, take a look at https://sys4.de/en/blog/2014/11/17/sicheres-lmtp-mit-starttls-in-dovecot/) +# `lmtp::2003` (use kopano as mailstore) +# etc. +POSTFIX_DAGENT= + +# Set the mailbox size limit for all users. If set to zero, the size will be unlimited (default). +# +# empty => 0 +POSTFIX_MAILBOX_SIZE_LIMIT=0 + +# Set the message size limit for all users. If set to zero, the size will be unlimited (not recommended!) +# +# empty => 10240000 (~10 MB) +#POSTFIX_MESSAGE_SIZE_LIMIT=1024000 +POSTFIX_MESSAGE_SIZE_LIMIT=0 + +# Enables regular pflogsumm mail reports. +# This is a new option. The old REPORT options are still supported for backwards compatibility. If this is not set and reports are enabled with the old options, logrotate will be used. +# +# not set => No report +# daily_cron => Daily report for the previous day +# logrotate => Full report based on the mail log when it is rotated +PFLOGSUMM_TRIGGER= + +# Recipient address for pflogsumm reports. +# +# not set => Use REPORT_RECIPIENT or POSTMASTER_ADDRESS +# => Specify the recipient address(es) +PFLOGSUMM_RECIPIENT= + +# From address for pflogsumm reports. +# +# not set => Use REPORT_SENDER or POSTMASTER_ADDRESS +# => Specify the sender address +PFLOGSUMM_SENDER= + +# Interval for logwatch report. +# +# none => No report is generated +# daily => Send a daily report +# weekly => Send a report every week +LOGWATCH_INTERVAL= + +# Recipient address for logwatch reports if they are enabled. +# +# not set => Use REPORT_RECIPIENT or POSTMASTER_ADDRESS +# => Specify the recipient address(es) +LOGWATCH_RECIPIENT= + +# Enables a report being sent (created by pflogsumm) on a regular basis. (deprecated) +# **0** => Report emails are disabled +# 1 => Using POSTMASTER_ADDRESS as the recipient +# => Specify the recipient address +REPORT_RECIPIENT=0 + +# Change the sending address for mail report (deprecated) +# **empty** => mailserver-report@hostname +# => Specify the report sender (From) address +REPORT_SENDER= + +# Changes the interval in which a report is being sent. (deprecated) +# **daily** => Send a daily report +# weekly => Send a report every week +# monthly => Send a report every month +# +# Note: This Variable actually controls logrotate inside the container and rotates the log depending on this setting. The main log output is still available in its entirety via `docker logs mail` (Or your respective container name). If you want to control logrotation for the docker generated logfile see: [Docker Logging Drivers](https://docs.docker.com/config/containers/logging/configure/) +REPORT_INTERVAL=daily + +# Choose TCP/IP protocols to use +# **all** => All possible protocols. +# ipv4 => Use only IPv4 traffic. Most likely you want this behind Docker. +# ipv6 => Use only IPv6 traffic. +# +# Note: More details in http://www.postfix.org/postconf.5.html#inet_protocols +POSTFIX_INET_PROTOCOLS=all + +# ----------------------------------------------------------------------------------------------------------------------------- +# --------------------- Spamassassin section ---------------------------------------------------------------------------------- +# ----------------------------------------------------------------------------------------------------------------------------- + +ENABLE_SPAMASSASSIN=1 + +# deliver spam messages in the inbox (eventually tagged using SA_SPAM_SUBJECT) +SPAMASSASSIN_SPAM_TO_INBOX=1 + +# spam messages will be moved in the Junk folder (SPAMASSASSIN_SPAM_TO_INBOX=1 required) +MOVE_SPAM_TO_JUNK=1 + +# add spam info headers if at, or above that level: +SA_TAG=2.0 + +# add 'spam detected' headers at that level +SA_TAG2=6.31 + +# triggers spam evasive actions +SA_KILL=6.31 + +# add tag to subject if spam detected +SA_SPAM_SUBJECT=***SPAM***** + +# KAM is a 3rd party SpamAssassin ruleset, provided by the McGrail Foundation. If SpamAssassin is enabled, KAM can be used in addition to the default ruleset. + +ENABLE_SPAMASSASSIN_KAM=1 + +# ----------------------------------------------------------------------------------------------------------------------------- +# --------------------- Fetchmail section ------------------------------------------------------------------------------------- +# ----------------------------------------------------------------------------------------------------------------------------- + +ENABLE_FETCHMAIL=0 + +# The interval to fetch mail in seconds +FETCHMAIL_POLL=300 + +# ----------------------------------------------------------------------------------------------------------------------------- +# --------------------- LDAP section ------------------------------------------------------------------------------------------ +# ----------------------------------------------------------------------------------------------------------------------------- + +# A second container for the ldap service is necessary (i.e. https://github.com/osixia/docker-openldap) +# For preparing the ldap server to use in combination with this container this article may be helpful: http://acidx.net/wordpress/2014/06/installing-a-mailserver-with-postfix-dovecot-sasl-ldap-roundcube/ + +# empty => LDAP authentification is disabled +# 1 => LDAP authentification is enabled +ENABLE_LDAP= + +# empty => no +# yes => LDAP over TLS enabled for Postfix +LDAP_START_TLS=no + +# If you going to use the mailserver in combination with docker-compose you can set the service name here +# empty => mail.domain.com +# Specify the dns-name/ip-address where the ldap-server +LDAP_SERVER_HOST= + +# empty => ou=people,dc=domain,dc=com +# => e.g. LDAP_SEARCH_BASE=dc=mydomain,dc=local +LDAP_SEARCH_BASE= + +# empty => cn=admin,dc=domain,dc=com +# => take a look at examples of SASL_LDAP_BIND_DN +LDAP_BIND_DN= + +# empty** => admin +# => Specify the password to bind against ldap +LDAP_BIND_PW= + +# e.g. `"(&(mail=%s)(mailEnabled=TRUE))"` +# => Specify how ldap should be asked for users +LDAP_QUERY_FILTER_USER=(&(mail=%s)(mailEnabled=TRUE)) + +# e.g. `"(&(mailGroupMember=%s)(mailEnabled=TRUE))"` +# => Specify how ldap should be asked for groups +LDAP_QUERY_FILTER_GROUP=(&(mailGroupMember=%s)(mailEnabled=TRUE)) + +# e.g. `"(&(mailAlias=%s)(mailEnabled=TRUE))"` +# => Specify how ldap should be asked for aliases +LDAP_QUERY_FILTER_ALIAS=(|(&(mailAlias=%s)(objectClass=PostfixBookMailForward))(&(mailAlias=%s)(objectClass=PostfixBookMailAccount)(mailEnabled=TRUE))) + +# e.g. `"(&(|(mail=*@%s)(mailalias=*@%s)(mailGroupMember=*@%s))(mailEnabled=TRUE))"` +# => Specify how ldap should be asked for domains +LDAP_QUERY_FILTER_DOMAIN=(|(&(mail=*@%s)(objectClass=PostfixBookMailAccount)(mailEnabled=TRUE))(&(mailGroupMember=*@%s)(objectClass=PostfixBookMailAccount)(mailEnabled=TRUE))(&(mailalias=*@%s)(objectClass=PostfixBookMailForward))) + +# ----------------------------------------------------------------------------------------------------------------------------- +# ---------------- Dovecot section -------------------------------------------------------------------------------------------- +# ----------------------------------------------------------------------------------------------------------------------------- + +# empty => no +# yes => LDAP over TLS enabled for Dovecot +DOVECOT_TLS=no + +# e.g. `"(&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n))"` +DOVECOT_USER_FILTER=(&(objectClass=PostfixBookMailAccount)(mail=%u)) + +# e.g. `"(&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n))"` +DOVECOT_PASS_FILTER=(&(objectClass=PostfixBookMailAccount)(mail=%u)) + +# Define the mailbox format to be used +# default is maildir, supported values are: sdbox, mdbox, maildir +DOVECOT_MAILBOX_FORMAT=maildir + +# empty => no +# yes => Allow bind authentication for LDAP +# https://wiki.dovecot.org/AuthDatabase/LDAP/AuthBinds +DOVECOT_AUTH_BIND=yes + +DOVECOT_PASS_ATTRS=cn=user,userPassword=password + +# DOVECOT_USER_ATTRS=mailHomeDirectory=home,mailUidNumber=uid,mailGidNumber=gid,mailStorageDirectory=mail,mailQuota=quota_rule=*:bytes=%$ +DOVECOT_USER_ATTRS=mailHomeDirectory=home,mailUidNumber=uid,mailGidNumber=gid,mailStorageDirectory=mail,mailQuota=quota_rule=*:bytes=20G + +ENABLE_QUOTAS=1 + +# ----------------------------------------------------------------------------------------------------------------------------- +# ---------------- Postgrey section ------------------------------------------------------------------------------------------- +# ----------------------------------------------------------------------------------------------------------------------------- + +ENABLE_POSTGREY=0 +# greylist for N seconds +POSTGREY_DELAY=300 +# delete entries older than N days since the last time that they have been seen +POSTGREY_MAX_AGE=35 +# response when a mail is greylisted +POSTGREY_TEXT=Delayed by postgrey +# whitelist host after N successful deliveries (N=0 to disable whitelisting) +POSTGREY_AUTO_WHITELIST_CLIENTS=5 + +# ----------------------------------------------------------------------------------------------------------------------------- +# ---------------- SASL section ----------------------------------------------------------------------------------------------- +# ----------------------------------------------------------------------------------------------------------------------------- + +ENABLE_SASLAUTHD=0 + +# empty => pam +# `ldap` => authenticate against ldap server +# `shadow` => authenticate against local user db +# `mysql` => authenticate against mysql db +# `rimap` => authenticate against imap server +# NOTE: can be a list of mechanisms like pam ldap shadow +SASLAUTHD_MECHANISMS= + +# empty => None +# e.g. with SASLAUTHD_MECHANISMS rimap you need to specify the ip-address/servername of the imap server ==> xxx.xxx.xxx.xxx +SASLAUTHD_MECH_OPTIONS= + +# empty => localhost +SASLAUTHD_LDAP_SERVER= + +# empty or 0 => `ldap://` will be used +# 1 => `ldaps://` will be used +SASLAUTHD_LDAP_SSL= + +# empty => anonymous bind +# specify an object with priviliges to search the directory tree +# e.g. active directory: SASLAUTHD_LDAP_BIND_DN=cn=Administrator,cn=Users,dc=mydomain,dc=net +# e.g. openldap: SASLAUTHD_LDAP_BIND_DN=cn=admin,dc=mydomain,dc=net +SASLAUTHD_LDAP_BIND_DN= + +# empty => anonymous bind +SASLAUTHD_LDAP_PASSWORD= + +# empty => Reverting to SASLAUTHD_MECHANISMS pam +# specify the search base +SASLAUTHD_LDAP_SEARCH_BASE= + +# empty => default filter `(&(uniqueIdentifier=%u)(mailEnabled=TRUE))` +# e.g. for active directory: `(&(sAMAccountName=%U)(objectClass=person))` +# e.g. for openldap: `(&(uid=%U)(objectClass=person))` +SASLAUTHD_LDAP_FILTER= + +# empty => no +# yes => LDAP over TLS enabled for SASL +# Must not be used together with SASLAUTHD_LDAP_SSL=1_ +SASLAUTHD_LDAP_START_TLS= + +# empty => no +# yes => Require and verify server certificate +SASLAUTHD_LDAP_TLS_CHECK_PEER= + +# empty => No sasl_passwd will be created +# string => `/etc/postfix/sasl_passwd` will be created with the string as password +SASL_PASSWD= + +# ----------------------------------------------------------------------------------------------------------------------------- +# ---------------- SRS section -------------------------------------------------------------------------------------------- +# ----------------------------------------------------------------------------------------------------------------------------- + +# envelope_sender => Rewrite only envelope sender address (default) +# header_sender => Rewrite only header sender (not recommended) +# envelope_sender,header_sender => Rewrite both senders +# An email has an "envelope" sender (indicating the sending server) and a +# "header" sender (indicating who sent it). More strict SPF policies may require +# you to replace both instead of just the envelope sender. +SRS_SENDER_CLASSES=envelope_sender + +# empty => Envelope sender will be rewritten for all domains +# provide comma separated list of domains to exclude from rewriting +SRS_EXCLUDE_DOMAINS= + +# empty => generated when the image is built +# provide a secret to use in base64 +# you may specify multiple keys, comma separated. the first one is used for +# signing and the remaining will be used for verification. this is how you +# rotate and expire keys +SRS_SECRET= + +# ----------------------------------------------------------------------------------------------------------------------------- +# ---------------- Default relay host section --------------------------------------------------------------------------------- +# ----------------------------------------------------------------------------------------------------------------------------- + +# Setup relaying all mail through a default relay host +# +# empty => don't configure default relay host +# default host and optional port to relay all mail through +DEFAULT_RELAY_HOST= + +# ----------------------------------------------------------------------------------------------------------------------------- +# ---------------- Multi-domain relay section --------------------------------------------------------------------------------- +# ----------------------------------------------------------------------------------------------------------------------------- + +# Setup relaying for multiple domains based on the domain name of the sender +# optionally uses usernames and passwords in postfix-sasl-password.cf and relay host mappings in postfix-relaymap.cf +# +# empty => don't configure relay host +# default host to relay mail through +RELAY_HOST= + +# empty => 25 +# default port to relay mail +RELAY_PORT=25 + +# empty => no default +# default relay username (if no specific entry exists in postfix-sasl-password.cf) +RELAY_USER= + +# empty => no default +# password for default relay user +RELAY_PASSWORD= diff --git a/dockers/postfix/first.sh b/dockers/postfix/first.sh new file mode 100755 index 0000000..591f62b --- /dev/null +++ b/dockers/postfix/first.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +SERV_DIR=$(cd $(dirname $0); pwd) +KAZ_ROOT=$(cd $(dirname $0)/../..; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +cd $(dirname $0) +. "${DOCKERS_ENV}" + +checkDockerRunning "${smtpServName}" "Postfix" || exit + +SETUP_MAIL="docker exec -ti mailServ setup" + +printKazMsg "\n *** Premier lancement de Postfix" + +if [ ! -s /var/lib/docker/volumes/postfix_mailConfig/_data/user-patches.sh ]; then + cat > /var/lib/docker/volumes/postfix_mailConfig/_data/user-patches.sh << EOF +#!/bin/bash +awk -i inplace 'BEGIN {cp=0} /127.0.0.1:10025/ {cp=1} /content_filter=/ {if (cp) gsub("content_filter=.*", "content_filter=filter:dummy"); print \$0; cp=0; next} {print}' /etc/postfix/master.cf +echo ${domain} > /home/filter/config/domainedepot +touch /home/filter/config/file_domaines_non_depollues.txt +sed -i "s/\$mail_plugins/\$mail_plugins quota/g" /etc/dovecot/conf.d/10-mail.conf +sed -i "s/\$mail_plugins/\$mail_plugins imap_quota/g" /etc/dovecot/conf.d/20-imap.conf +mv /etc/dovecot/conf.d/90-quota.conf.disab /etc/dovecot/conf.d/90-quota.conf +EOF + +chmod +x /var/lib/docker/volumes/postfix_mailConfig/_data/user-patches.sh +fi + +if [ "${mode}" == "local" ] || exit + +# echo "virtual_alias_maps = ldap:/etc/postfix/ldap-aliases.cf, ldap:/etc/postfix/ldap-groups.cf, texthash:/etc/postfix/virtual" >> config/postfix-main.cf diff --git a/dockers/postfix2/.env b/dockers/postfix2/.env new file mode 120000 index 0000000..406acd1 --- /dev/null +++ b/dockers/postfix2/.env @@ -0,0 +1 @@ +../../config/dockers.env \ No newline at end of file diff --git a/dockers/postfix2/docker-compose.yml b/dockers/postfix2/docker-compose.yml new file mode 100644 index 0000000..6e7cf0a --- /dev/null +++ b/dockers/postfix2/docker-compose.yml @@ -0,0 +1,42 @@ +version: '3.3' + +services: + mail: + image: docker.io/mailserver/docker-mailserver:latest + hostname: ${smtpHost} + domainname: ${domain} + container_name: ${smtpServName} + networks: + - postfixNet + #ports: + #- ${MAIN_IP}:25:25 + volumes: + - mailData:/var/mail + - mailState:/var/mail-state + - mailLog:/var/log/mail + - mailConfig:/tmp/docker-mailserver/ + - /etc/localtime:/etc/localtime:ro + - /etc/timezone:/etc/timezone:ro + - /etc/letsencrypt:/etc/letsencrypt:ro + environment: + - HOSTNAME=${smtpHost} + - DOMAINNAME=${domain} + - CONTAINER_NAME=${smtpServName} + env_file: + - env-config + - ../../secret/env-${smtpServName} + cap_add: + - NET_ADMIN + - SYS_PTRACE + restart: always + +volumes: + mailData: + mailState: + mailLog: + mailConfig: + +networks: + postfixNet: + external: true + name: postfixNet diff --git a/dockers/postfix2/env-config b/dockers/postfix2/env-config new file mode 100644 index 0000000..6d28dd6 --- /dev/null +++ b/dockers/postfix2/env-config @@ -0,0 +1,416 @@ + +# ----------------------------------------------------------------------------------------------------------------------------- +# --------------------- General Settings -------------------------------------------------------------------------------------- +# ----------------------------------------------------------------------------------------------------------------------------- + +ACCOUNT_PROVISIONER=FILE + +# empty => uses the `hostname` command to get the mail server's canonical hostname +# => Specify a fully-qualified domainname to serve mail for. This is used for many of the config features so if you can't set your hostname (e.g. you're in a container platform that doesn't let you) specify it in this environment variable. +OVERRIDE_HOSTNAME= + +# 0 => Debug disabled +# 1 => Enables debug on startup +DMS_DEBUG=0 + +# 0 => mail state in default directories +# 1 => consolidate all states into a single directory (`/var/mail-state`) to allow persistence using docker volumes +ONE_DIR=0 + +# empty => postmaster@domain.com +# => Specify the postmaster address +POSTMASTER_ADDRESS= + +# Set different options for mynetworks option (can be overwrite in postfix-main.cf) +# **WARNING**: Adding the docker network's gateway to the list of trusted hosts, e.g. using the `network` or +# `connected-networks` option, can create an open relay +# https://github.com/tomav/docker-mailserver/issues/1405#issuecomment-590106498 +# empty => localhost only +# host => Add docker host (ipv4 only) +# network => Add all docker containers (ipv4 only) +# connected-networks => Add all connected docker networks (ipv4 only) +PERMIT_DOCKER=connected-networks + +# In case you network interface differs from 'eth0', e.g. when you are using HostNetworking in Kubernetes, +# you can set NETWORK_INTERFACE to whatever interface you want. This interface will then be used. +# - **empty** => eth0 +NETWORK_INTERFACE=eth0 + +# empty => modern +# modern => Enables TLSv1.2 and modern ciphers only. (default) +# intermediate => Enables TLSv1, TLSv1.1 and TLSv1.2 and broad compatibility ciphers. +# old => NOT implemented. If you really need it, then customize the TLS ciphers overriding postfix and dovecot settings +# (https://github.com/tomav/docker-mailserver/wiki/) +TLS_LEVEL= + +# Configures the handling of creating mails with forged sender addresses. +# +# empty => (not recommended, but default for backwards compatibility reasons) +# Mail address spoofing allowed. Any logged in user may create email messages with a forged sender address. +# See also https://en.wikipedia.org/wiki/Email_spoofing +# 1 => (recommended) Mail spoofing denied. Each user may only send with his own or his alias addresses. +# Addresses with extension delimiters(http://www.postfix.org/postconf.5.html#recipient_delimiter) are not able to send messages. +SPOOF_PROTECTION=1 + +# Enables the Sender Rewriting Scheme. SRS is needed if your mail server acts as forwarder. See [postsrsd](https://github.com/roehling/postsrsd/blob/master/README.md#sender-rewriting-scheme-crash-course) for further explanation. +# - **0** => Disabled +# - 1 => Enabled +ENABLE_SRS=0 + +# 1 => Enables POP3 service +# empty => disables POP3 +ENABLE_POP3= +ENABLE_CLAMAV=0 + +# If you enable Fail2Ban, don't forget to add the following lines to your `docker-compose.yml`: +# cap_add: +# - NET_ADMIN +# Otherwise, `iptables` won't be able to ban IPs. +ENABLE_FAIL2BAN=0 + +# 1 => Enables Managesieve on port 4190 +# empty => disables Managesieve +ENABLE_MANAGESIEVE=0 + +# **enforce** => Allow other tests to complete. Reject attempts to deliver mail with a 550 SMTP reply, and log the helo/sender/recipient information. Repeat this test the next time the client connects. +# drop => Drop the connection immediately with a 521 SMTP reply. Repeat this test the next time the client connects. +# ignore => Ignore the failure of this test. Allow other tests to complete. Repeat this test the next time the client connects. This option is useful for testing and collecting statistics without blocking mail. +POSTSCREEN_ACTION=enforce + +# empty => all daemons start +# 1 => only launch postfix smtp +SMTP_ONLY= + +# Please read [the SSL page in the wiki](https://github.com/tomav/docker-mailserver/wiki/Configure-SSL) for more information. +# +# empty => SSL disabled +# letsencrypt => Enables Let's Encrypt certificates +# custom => Enables custom certificates +# manual => Let's you manually specify locations of your SSL certificates for non-standard cases +# self-signed => Enables self-signed certificates +SSL_TYPE=manual +#SSL_TYPE=self-signed +#SSL_TYPE=letsencrypt +SSL_CERT_PATH=/etc/letsencrypt/live/kaz.bzh/fullchain.pem +SSL_KEY_PATH=/etc/letsencrypt/live/kaz.bzh/privkey.pem + +# Set how many days a virusmail will stay on the server before being deleted +# empty => 7 days +VIRUSMAILS_DELETE_DELAY= + +# This Option is activating the Usage of POSTFIX_DAGENT to specify a lmtp client different from default dovecot socket. +# empty => disabled +# 1 => enabled +ENABLE_POSTFIX_VIRTUAL_TRANSPORT= + +# Enabled by ENABLE_POSTFIX_VIRTUAL_TRANSPORT. Specify the final delivery of postfix +# +# empty => fail +# `lmtp:unix:private/dovecot-lmtp` (use socket) +# `lmtps:inet::` (secure lmtp with starttls, take a look at https://sys4.de/en/blog/2014/11/17/sicheres-lmtp-mit-starttls-in-dovecot/) +# `lmtp::2003` (use kopano as mailstore) +# etc. +POSTFIX_DAGENT= + +# Set the mailbox size limit for all users. If set to zero, the size will be unlimited (default). +# +# empty => 0 +POSTFIX_MAILBOX_SIZE_LIMIT=0 + +# Set the message size limit for all users. If set to zero, the size will be unlimited (not recommended!) +# +# empty => 10240000 (~10 MB) +#POSTFIX_MESSAGE_SIZE_LIMIT=1024000 +POSTFIX_MESSAGE_SIZE_LIMIT=0 + +# Enables regular pflogsumm mail reports. +# This is a new option. The old REPORT options are still supported for backwards compatibility. If this is not set and reports are enabled with the old options, logrotate will be used. +# +# not set => No report +# daily_cron => Daily report for the previous day +# logrotate => Full report based on the mail log when it is rotated +PFLOGSUMM_TRIGGER= + +# Recipient address for pflogsumm reports. +# +# not set => Use REPORT_RECIPIENT or POSTMASTER_ADDRESS +# => Specify the recipient address(es) +PFLOGSUMM_RECIPIENT= + +# From address for pflogsumm reports. +# +# not set => Use REPORT_SENDER or POSTMASTER_ADDRESS +# => Specify the sender address +PFLOGSUMM_SENDER= + +# Interval for logwatch report. +# +# none => No report is generated +# daily => Send a daily report +# weekly => Send a report every week +LOGWATCH_INTERVAL= + +# Recipient address for logwatch reports if they are enabled. +# +# not set => Use REPORT_RECIPIENT or POSTMASTER_ADDRESS +# => Specify the recipient address(es) +LOGWATCH_RECIPIENT= + +# Enables a report being sent (created by pflogsumm) on a regular basis. (deprecated) +# **0** => Report emails are disabled +# 1 => Using POSTMASTER_ADDRESS as the recipient +# => Specify the recipient address +REPORT_RECIPIENT=0 + +# Change the sending address for mail report (deprecated) +# **empty** => mailserver-report@hostname +# => Specify the report sender (From) address +REPORT_SENDER= + +# Changes the interval in which a report is being sent. (deprecated) +# **daily** => Send a daily report +# weekly => Send a report every week +# monthly => Send a report every month +# +# Note: This Variable actually controls logrotate inside the container and rotates the log depending on this setting. The main log output is still available in its entirety via `docker logs mail` (Or your respective container name). If you want to control logrotation for the docker generated logfile see: [Docker Logging Drivers](https://docs.docker.com/config/containers/logging/configure/) +REPORT_INTERVAL=daily + +# Choose TCP/IP protocols to use +# **all** => All possible protocols. +# ipv4 => Use only IPv4 traffic. Most likely you want this behind Docker. +# ipv6 => Use only IPv6 traffic. +# +# Note: More details in http://www.postfix.org/postconf.5.html#inet_protocols +POSTFIX_INET_PROTOCOLS=all + +# ----------------------------------------------------------------------------------------------------------------------------- +# --------------------- Spamassassin section ---------------------------------------------------------------------------------- +# ----------------------------------------------------------------------------------------------------------------------------- + +ENABLE_SPAMASSASSIN=0 + +# deliver spam messages in the inbox (eventually tagged using SA_SPAM_SUBJECT) +SPAMASSASSIN_SPAM_TO_INBOX=1 + +# spam messages will be moved in the Junk folder (SPAMASSASSIN_SPAM_TO_INBOX=1 required) +MOVE_SPAM_TO_JUNK=1 + +# add spam info headers if at, or above that level: +SA_TAG=2.0 + +# add 'spam detected' headers at that level +SA_TAG2=6.31 + +# triggers spam evasive actions +SA_KILL=6.31 + +# add tag to subject if spam detected +SA_SPAM_SUBJECT=***SPAM***** + +# ----------------------------------------------------------------------------------------------------------------------------- +# --------------------- Fetchmail section ------------------------------------------------------------------------------------- +# ----------------------------------------------------------------------------------------------------------------------------- + +ENABLE_FETCHMAIL=0 + +# The interval to fetch mail in seconds +FETCHMAIL_POLL=300 + +# ----------------------------------------------------------------------------------------------------------------------------- +# --------------------- LDAP section ------------------------------------------------------------------------------------------ +# ----------------------------------------------------------------------------------------------------------------------------- + +# A second container for the ldap service is necessary (i.e. https://github.com/osixia/docker-openldap) +# For preparing the ldap server to use in combination with this container this article may be helpful: http://acidx.net/wordpress/2014/06/installing-a-mailserver-with-postfix-dovecot-sasl-ldap-roundcube/ + +# empty => LDAP authentification is disabled +# 1 => LDAP authentification is enabled +ENABLE_LDAP= + +# empty => no +# yes => LDAP over TLS enabled for Postfix +LDAP_START_TLS=no + +# If you going to use the mailserver in combination with docker-compose you can set the service name here +# empty => mail.domain.com +# Specify the dns-name/ip-address where the ldap-server +LDAP_SERVER_HOST= + +# empty => ou=people,dc=domain,dc=com +# => e.g. LDAP_SEARCH_BASE=dc=mydomain,dc=local +LDAP_SEARCH_BASE= + +# empty => cn=admin,dc=domain,dc=com +# => take a look at examples of SASL_LDAP_BIND_DN +LDAP_BIND_DN= + +# empty** => admin +# => Specify the password to bind against ldap +LDAP_BIND_PW= + +# e.g. `"(&(mail=%s)(mailEnabled=TRUE))"` +# => Specify how ldap should be asked for users +LDAP_QUERY_FILTER_USER=(&(mail=%s)(mailEnabled=TRUE)) + +# e.g. `"(&(mailGroupMember=%s)(mailEnabled=TRUE))"` +# => Specify how ldap should be asked for groups +LDAP_QUERY_FILTER_GROUP=(&(mailGroupMember=%s)(mailEnabled=TRUE)) + +# e.g. `"(&(mailAlias=%s)(mailEnabled=TRUE))"` +# => Specify how ldap should be asked for aliases +LDAP_QUERY_FILTER_ALIAS=(|(&(mailAlias=%s)(objectClass=PostfixBookMailForward))(&(mailAlias=%s)(objectClass=PostfixBookMailAccount)(mailEnabled=TRUE))) + +# e.g. `"(&(|(mail=*@%s)(mailalias=*@%s)(mailGroupMember=*@%s))(mailEnabled=TRUE))"` +# => Specify how ldap should be asked for domains +LDAP_QUERY_FILTER_DOMAIN=(|(&(mail=*@%s)(objectClass=PostfixBookMailAccount)(mailEnabled=TRUE))(&(mailGroupMember=*@%s)(objectClass=PostfixBookMailAccount)(mailEnabled=TRUE))(&(mailalias=*@%s)(objectClass=PostfixBookMailForward))) + +# ----------------------------------------------------------------------------------------------------------------------------- +# ---------------- Dovecot section -------------------------------------------------------------------------------------------- +# ----------------------------------------------------------------------------------------------------------------------------- + +# empty => no +# yes => LDAP over TLS enabled for Dovecot +DOVECOT_TLS=no + +# e.g. `"(&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n))"` +DOVECOT_USER_FILTER=(&(objectClass=PostfixBookMailAccount)(mail=%u)) + +# e.g. `"(&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n))"` +DOVECOT_PASS_FILTER=(&(objectClass=PostfixBookMailAccount)(mail=%u)) + +# Define the mailbox format to be used +# default is maildir, supported values are: sdbox, mdbox, maildir +DOVECOT_MAILBOX_FORMAT=maildir + +# empty => no +# yes => Allow bind authentication for LDAP +# https://wiki.dovecot.org/AuthDatabase/LDAP/AuthBinds +DOVECOT_AUTH_BIND=yes + +DOVECOT_PASS_ATTRS=cn=user,userPassword=password + +# ----------------------------------------------------------------------------------------------------------------------------- +# ---------------- Postgrey section ------------------------------------------------------------------------------------------- +# ----------------------------------------------------------------------------------------------------------------------------- + +ENABLE_POSTGREY=0 +# greylist for N seconds +POSTGREY_DELAY=300 +# delete entries older than N days since the last time that they have been seen +POSTGREY_MAX_AGE=35 +# response when a mail is greylisted +POSTGREY_TEXT=Delayed by postgrey +# whitelist host after N successful deliveries (N=0 to disable whitelisting) +POSTGREY_AUTO_WHITELIST_CLIENTS=5 + +# ----------------------------------------------------------------------------------------------------------------------------- +# ---------------- SASL section ----------------------------------------------------------------------------------------------- +# ----------------------------------------------------------------------------------------------------------------------------- + +ENABLE_SASLAUTHD=0 + +# empty => pam +# `ldap` => authenticate against ldap server +# `shadow` => authenticate against local user db +# `mysql` => authenticate against mysql db +# `rimap` => authenticate against imap server +# NOTE: can be a list of mechanisms like pam ldap shadow +SASLAUTHD_MECHANISMS= + +# empty => None +# e.g. with SASLAUTHD_MECHANISMS rimap you need to specify the ip-address/servername of the imap server ==> xxx.xxx.xxx.xxx +SASLAUTHD_MECH_OPTIONS= + +# empty => localhost +SASLAUTHD_LDAP_SERVER= + +# empty or 0 => `ldap://` will be used +# 1 => `ldaps://` will be used +SASLAUTHD_LDAP_SSL= + +# empty => anonymous bind +# specify an object with priviliges to search the directory tree +# e.g. active directory: SASLAUTHD_LDAP_BIND_DN=cn=Administrator,cn=Users,dc=mydomain,dc=net +# e.g. openldap: SASLAUTHD_LDAP_BIND_DN=cn=admin,dc=mydomain,dc=net +SASLAUTHD_LDAP_BIND_DN= + +# empty => anonymous bind +SASLAUTHD_LDAP_PASSWORD= + +# empty => Reverting to SASLAUTHD_MECHANISMS pam +# specify the search base +SASLAUTHD_LDAP_SEARCH_BASE= + +# empty => default filter `(&(uniqueIdentifier=%u)(mailEnabled=TRUE))` +# e.g. for active directory: `(&(sAMAccountName=%U)(objectClass=person))` +# e.g. for openldap: `(&(uid=%U)(objectClass=person))` +SASLAUTHD_LDAP_FILTER= + +# empty => no +# yes => LDAP over TLS enabled for SASL +# Must not be used together with SASLAUTHD_LDAP_SSL=1_ +SASLAUTHD_LDAP_START_TLS= + +# empty => no +# yes => Require and verify server certificate +SASLAUTHD_LDAP_TLS_CHECK_PEER= + +# empty => No sasl_passwd will be created +# string => `/etc/postfix/sasl_passwd` will be created with the string as password +SASL_PASSWD= + +# ----------------------------------------------------------------------------------------------------------------------------- +# ---------------- SRS section -------------------------------------------------------------------------------------------- +# ----------------------------------------------------------------------------------------------------------------------------- + +# envelope_sender => Rewrite only envelope sender address (default) +# header_sender => Rewrite only header sender (not recommended) +# envelope_sender,header_sender => Rewrite both senders +# An email has an "envelope" sender (indicating the sending server) and a +# "header" sender (indicating who sent it). More strict SPF policies may require +# you to replace both instead of just the envelope sender. +SRS_SENDER_CLASSES=envelope_sender + +# empty => Envelope sender will be rewritten for all domains +# provide comma separated list of domains to exclude from rewriting +SRS_EXCLUDE_DOMAINS= + +# empty => generated when the image is built +# provide a secret to use in base64 +# you may specify multiple keys, comma separated. the first one is used for +# signing and the remaining will be used for verification. this is how you +# rotate and expire keys +SRS_SECRET= + +# ----------------------------------------------------------------------------------------------------------------------------- +# ---------------- Default relay host section --------------------------------------------------------------------------------- +# ----------------------------------------------------------------------------------------------------------------------------- + +# Setup relaying all mail through a default relay host +# +# empty => don't configure default relay host +# default host and optional port to relay all mail through +DEFAULT_RELAY_HOST= + +# ----------------------------------------------------------------------------------------------------------------------------- +# ---------------- Multi-domain relay section --------------------------------------------------------------------------------- +# ----------------------------------------------------------------------------------------------------------------------------- + +# Setup relaying for multiple domains based on the domain name of the sender +# optionally uses usernames and passwords in postfix-sasl-password.cf and relay host mappings in postfix-relaymap.cf +# +# empty => don't configure relay host +# default host to relay mail through +RELAY_HOST= + +# empty => 25 +# default port to relay mail +RELAY_PORT= + +# empty => no default +# default relay username (if no specific entry exists in postfix-sasl-password.cf) +RELAY_USER= + +# empty => no default +# password for default relay user +RELAY_PASSWORD= diff --git a/dockers/proxy/.env b/dockers/proxy/.env new file mode 120000 index 0000000..406acd1 --- /dev/null +++ b/dockers/proxy/.env @@ -0,0 +1 @@ +../../config/dockers.env \ No newline at end of file diff --git a/dockers/proxy/Dockerfile b/dockers/proxy/Dockerfile new file mode 100644 index 0000000..9b41398 --- /dev/null +++ b/dockers/proxy/Dockerfile @@ -0,0 +1,22 @@ +FROM nginx + +######################################## +# APT local cache +# work around because COPY failed if no source file +COPY .dummy .apt-mirror-confi[g] .proxy-confi[g] / +RUN cp /.proxy-config /etc/profile.d/proxy.sh 2> /dev/null || true +RUN if [ -f /.apt-mirror-config ] ; then . /.apt-mirror-config && sed -i \ + -e "s/deb.debian.org/${APT_MIRROR_DEBIAN}/g" \ + -e "s/security.debian.org/${APT_MIRROR_DEBIAN_SECURITY}/g" \ + -e "s/archive.ubuntu.com/${APT_MIRROR_UBUNTU}/g" \ + -e "s/security.ubuntu.com/${APT_MIRROR_UBUNTU_SECURITY}/g" \ + /etc/apt/sources.list; fi + +######################################## +RUN apt-get update --quiet && apt-get upgrade -y + +RUN apt install -y python3 python3-venv libaugeas0 +RUN python3 -m venv /opt/certbot/ +RUN /opt/certbot/bin/python -m pip install --upgrade pip +RUN /opt/certbot/bin/python -m pip install certbot certbot-nginx +RUN ln -s /opt/certbot/bin/certbot /usr/bin/certbot diff --git a/dockers/proxy/Readme.txt b/dockers/proxy/Readme.txt new file mode 100644 index 0000000..4e392ea --- /dev/null +++ b/dockers/proxy/Readme.txt @@ -0,0 +1,43 @@ +Pour l'installation d'un mandataire pour aiguiller les demandes web + +Contenu du répertoire : +. +├── conf Paramettrage du mandataire +│   ├── allow_admin_ip Les adresses IP des administrateur pour les URI protégés +│   ├── nginx.conf La config du mandataire produite automatiquement +│   ├── nginx.conf.tmpl Modèle de config du mandataire +│   ├── proxy_params Le paramétrage de transmetre des requêtes +│   └── proxy-gen.sh Le script de production à partir du modèle +├── docker-compose.yml Scénario de lancement +└── Readme.txt Ce fichier + +# cd /dockers/proxy + +1) Lancement du mandataire +Dans docker-compose.yml + - il y a nommage du container + +# docker-compose up -d + +2) Verification +Il y a un container reverse-proxy + +# docker ps | grep reverse + +3) Modification de config +Il faut éditer + +# cd conf +# emacs .env nginx.conf.tmpl +# ./proxy-gen.sh + +4) Arrêt du mandataire + +# docker-compose down + + +A faire: +Impose le https dans le cache du navigateur +7776000 (= 90jours) +31536000 (= 365 jours) +add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; diff --git a/dockers/proxy/build.sh b/dockers/proxy/build.sh new file mode 100755 index 0000000..35f2d0a --- /dev/null +++ b/dockers/proxy/build.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +KAZ_ROOT=$(cd "$(dirname $0)/../.."; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +printKazMsg "\n *** Création du Dockerfile proxy" + +cd "${KAZ_ROOT}" + +docker build -t proxykaz . -f dockers/proxy/Dockerfile diff --git a/dockers/proxy/config/nginx.tmpl.conf b/dockers/proxy/config/nginx.tmpl.conf new file mode 100644 index 0000000..eed5199 --- /dev/null +++ b/dockers/proxy/config/nginx.tmpl.conf @@ -0,0 +1,734 @@ +# pour l'utilisation de certificats dynamique +user root; + +events { + worker_connections 1000000; +} +http { + + resolver 127.0.0.11 ipv6=off; + server_tokens off; + + ######################################## + #### autoriser des uploads de 50Mo max + #### pour tous les sites + ### sinon placer la variable dans chaque server{} + client_max_body_size 1024M; + add_header Set-Cookie lang="fr"; + + ######################################## + #### redirection http vers https + include includes/redirect; + + map $ssl_early_data $tls1_3_early_data { + "~." $ssl_early_data; + default ""; + } + + map $http_upgrade $connection_upgrade { + default upgrade; + '' close; + } + map $ssl_server_name $ssl_local_cert { + volatile; + hostnames; + ~^(?.*\.)__DOMAIN__$ __DOMAIN__; + default $ssl_server_name; + } + + ######################################## + #### Default +{{web + +# ######################################## +# #### Autoconfig pour thunderbird +server { + server_name autoconfig.__DOMAIN__; + include includes/port; + ssl_certificate /etc/letsencrypt/live/__DOMAIN__/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/__DOMAIN__/privkey.pem; + include includes/proxy_params; + location /mail/config-v1.1.xml { + proxy_pass http://__DOMAIN__/mail/config-v1.1.xml; + } + } + +# merci de ne pas effacer +server { + server_name autoconfig.bodamcity.fr; + include includes/port; + ssl_certificate /etc/letsencrypt/live/autoconfig.bodamcity.fr/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/autoconfig.bodamcity.fr/privkey.pem; + include includes/proxy_params; + include includes/allow_ip; + location / { + proxy_pass http://kaz.bzh; + } +} + +# merci de ne pas effacer +server { + server_name autoconfig.legrandmechantlude.org; + include includes/port; + ssl_certificate /etc/letsencrypt/live/autoconfig.legrandmechantlude.org/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/autoconfig.legrandmechantlude.org/privkey.pem; + include includes/proxy_params; + include includes/allow_ip; + location / { + proxy_pass http://kaz.bzh; + } +} + +# merci de ne pas effacer +server { + server_name autoconfig.lbrondel-psychotherapie.fr; + include includes/port; + ssl_certificate /etc/letsencrypt/live/autoconfig.lbrondel-psychotherapie.fr/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/autoconfig.lbrondel-psychotherapie.fr/privkey.pem; + include includes/proxy_params; + include includes/allow_ip; + location / { + proxy_pass http://kaz.bzh; + } +} + + + server { + server_name __DOMAIN__ www.__DOMAIN__; + include includes/port; + ssl_certificate /etc/letsencrypt/live/www.__DOMAIN__/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/www.__DOMAIN__/privkey.pem; + include includes/proxy_params; + + include includes/allow_ip; + # XXX >>> + # A concerver jusqu'en juin 2021 + location /email.css { + proxy_pass http://__DOMAIN__/m/email.css; + } + location /kaz-50.png { + proxy_pass http://__DOMAIN__/m/logo.png; + } + location /kaz-du-libre-23.png { + proxy_pass http://__DOMAIN__/m/coche.png; + } + # <<< + location / { + proxy_pass http://__DOMAIN__; + } + } +}} + + ######################################## + #### Jirafeau (filesender) +{{jirafeau + server { + server_name __FILE_HOST__.__DOMAIN__; + include includes/port; + ssl_certificate /etc/letsencrypt/live/__DOMAIN__/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/__DOMAIN__/privkey.pem; + include includes/proxy_params; + + location /admin.php { + include allow_admin_ip; + proxy_pass http://__FILE_HOST__.__DOMAIN__; + } + location / { + include includes/allow_ip; + proxy_pass http://__FILE_HOST__.__DOMAIN__; + } + } +}} + + ######################################## + #### CALC +{{ethercalc + server { + server_name __CALC_HOST__.__DOMAIN__; + include includes/port; + ssl_certificate /etc/letsencrypt/live/__DOMAIN__/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/__DOMAIN__/privkey.pem; + include includes/proxy_params; + + location / { + include includes/allow_ip; + proxy_pass http://__CALC_HOST__.__DOMAIN__:8000; + } + } +}} + + ######################################## + #### YAKFORMS +{{yakforms + server { + server_name __YAKFORMS_HOST__.__DOMAIN__; + include includes/port; + ssl_certificate /etc/letsencrypt/live/__DOMAIN__/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/__DOMAIN__/privkey.pem; + include includes/proxy_params; + + location / { + include includes/allow_ip; + proxy_pass http://__YAKFORMS_HOST__.__DOMAIN__; + } + } +}} + + ######################################## + #### PAD +{{etherpad + server { + server_name __PAD_HOST__.__DOMAIN__; + include includes/port; + ssl_certificate /etc/letsencrypt/live/__DOMAIN__/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/__DOMAIN__/privkey.pem; + include includes/proxy_params; + + location /admin/ { + include allow_admin_ip; + proxy_pass http://__PAD_HOST__.__DOMAIN__:9001; + } + location / { + include includes/allow_ip; + proxy_pass http://__PAD_HOST__.__DOMAIN__:9001; + } + } +}} + + ######################################## + #### roundcube +{{roundcube + server { + server_name __WEBMAIL_HOST__.__DOMAIN__; + include includes/port; + ssl_certificate /etc/letsencrypt/live/__DOMAIN__/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/__DOMAIN__/privkey.pem; + include includes/proxy_params; + + location / { + include includes/allow_ip; + proxy_pass http://__WEBMAIL_HOST__.__DOMAIN__; + } + } +}} + + ######################################## + #### Framadate +{{framadate + server { + server_name __DATE_HOST__.__DOMAIN__; + include includes/port; + ssl_certificate /etc/letsencrypt/live/__DOMAIN__/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/__DOMAIN__/privkey.pem; + include includes/proxy_params; + + location /admin/ { + include allow_admin_ip; + proxy_pass http://__DATE_HOST__.__DOMAIN__; + } + location / { + include includes/allow_ip; + proxy_pass http://__DATE_HOST__.__DOMAIN__; + } + } +}} + + ######################################## + #### LDAP +{{ldap + server { + server_name __LDAPUI_HOST__.__DOMAIN__; + include includes/port; + ssl_certificate /etc/letsencrypt/live/__DOMAIN__/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/__DOMAIN__/privkey.pem; + include includes/proxy_params; + + location / { + include includes/allow_ip; + proxy_pass http://__LDAPUI_HOST__.__DOMAIN__; + } + } +}} + +######################################## +#### Mobilizon +{{mobilizon +server { + server_name __MOBILIZON_HOST__.__DOMAIN__; + include includes/port; + ssl_certificate /etc/letsencrypt/live/__DOMAIN__/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/__DOMAIN__/privkey.pem; + include includes/proxy_params; + + location / { + include includes/allow_ip; + proxy_pass http://__MOBILIZON_HOST__.__DOMAIN__; + } +} +}} + + + ######################################## + #### paheko kaz +{{paheko + # map $http_host $paheko_kaz_map { + # hostnames; + # # déclaration des domaines extérieurs vers un paheko local + # include includes/paheko_kaz_map; + # } + + server { + # XXX dans __DOMAIN__ il faudrait remplacer le . par \. + # mais c'est pas grave pour nous. Il n'y a pas de domaine kazXbzh à la racine du NIC + server_name ~^(?.+)-__PAHEKO_HOST__\.__DOMAIN__$; + + include includes/port; + ssl_certificate /etc/letsencrypt/live/$ssl_local_cert/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/$ssl_local_cert/privkey.pem; + include includes/proxy_params; + + location / { + include includes/allow_ip; + proxy_pass http://__PAHEKO_HOST__.__DOMAIN__; + } + } +}} + + ############################################# + # dokuwiki kaz +{{dokuwiki + server { + server_name __DOKUWIKI_HOST__.__DOMAIN__; + include includes/port; + ssl_certificate /etc/letsencrypt/live/__DOMAIN__/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/__DOMAIN__/privkey.pem; + include includes/proxy_params; + + location / { + include includes/allow_ip; + proxy_pass http://__DOKUWIKI_HOST__.__DOMAIN__; + } + } +}} + + ############################################# + # gitea kaz +{{gitea + server { + server_name __GIT_HOST__.__DOMAIN__; + include includes/port; + ssl_certificate /etc/letsencrypt/live/__DOMAIN__/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/__DOMAIN__/privkey.pem; + include includes/proxy_params; + + location / { + include includes/allow_ip; + proxy_pass http://__GIT_HOST__.__DOMAIN__:3000; + } + } +}} + + ############################################# + # vaultwarden + {{vaultwarden + server { + server_name __VAULTWARDEN_HOST__.__DOMAIN__; + include includes/port; + ssl_certificate /etc/letsencrypt/live/__DOMAIN__/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/__DOMAIN__/privkey.pem; + include includes/proxy_params; + + location / { + include includes/allow_ip; + proxy_pass http://__VAULTWARDEN_HOST__.__DOMAIN__:80; + } + } + }} + + ############################################# + # imapsync + {{imapsync + server { + server_name __IMAPSYNC_HOST__.__DOMAIN__; + include includes/port; + ssl_certificate /etc/letsencrypt/live/__DOMAIN__/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/__DOMAIN__/privkey.pem; + include includes/proxy_params; + + location / { + include includes/allow_ip; + proxy_pass http://__IMAPSYNC_HOST__.__DOMAIN__:8080; + } + } + }} + + ############################################# + # castopod + {{castopod + server { + server_name __CASTOPOD_HOST__.__DOMAIN__; + include includes/port; + ssl_certificate /etc/letsencrypt/live/__DOMAIN__/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/__DOMAIN__/privkey.pem; + include includes/proxy_params; + + location / { + include includes/allow_ip; + proxy_pass http://__CASTOPOD_HOST__.__DOMAIN__:8000; + } + } + }} + + + ######################################## + #### mattermost +{{mattermost + + server { + server_name __MATTER_HOST__.__DOMAIN__; + include includes/port; + ssl_certificate /etc/letsencrypt/live/__DOMAIN__/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/__DOMAIN__/privkey.pem; + include includes/proxy_params; + + include includes/allow_ip; + ssl_ecdh_curve prime256v1:secp384r1:secp521r1; + +# test add_header X-Early-Data $tls1_3_early_data; + location ~ /api/v[0-9]+/(users/)?websocket$ { + proxy_pass http://__MATTER_HOST__.__DOMAIN__:8000; +# test proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; +#test proxy_set_header Connection $connection_upgrade; + client_body_timeout 60; + send_timeout 300; + lingering_timeout 5; + proxy_connect_timeout 90; + proxy_send_timeout 300; + proxy_read_timeout 90s; +# test proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +# test proxy_set_header Proxy ""; + rewrite ^/(.+)$ /$1 break; + } + + location / { + proxy_pass http://__MATTER_HOST__.__DOMAIN__:8000; + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_read_timeout 600s; + # proxy_cache mattermost_cache; # test + # proxy_cache_lock on; # test + # proxy_cache_min_uses 2; # test + # proxy_cache_revalidate on; # test + # proxy_cache_use_stale timeout; # test + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + } +}} + + ######################################## + #### nextcloud / collabora +{{cloud + server { + server_name __CLOUD_HOST__.__DOMAIN__; + include includes/port; + ssl_certificate /etc/letsencrypt/live/__DOMAIN__/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/__DOMAIN__/privkey.pem; + include includes/proxy_params; + + location / { + include includes/allow_ip; + proxy_pass http://__CLOUD_HOST__.__DOMAIN__; + } + } +}} +{{collabora + server { + server_name __OFFICE_HOST__.__DOMAIN__; + include includes/port; + ssl_certificate /etc/letsencrypt/live/__DOMAIN__/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/__DOMAIN__/privkey.pem; + include includes/proxy_params; + + proxy_set_header Host $http_host; + + # static files + location ^~ /loleaflet { + include includes/allow_ip; + proxy_pass http://__OFFICE_HOST__.__DOMAIN__:9980; + } + location ^~ /browser { + include includes/allow_ip; + proxy_pass http://__OFFICE_HOST__.__DOMAIN__:9980; + } + # WOPI discovery URL + location ^~ /hosting/discovery { + include includes/allow_ip; + proxy_pass http://__OFFICE_HOST__.__DOMAIN__:9980; + } + # Capabilities + location ^~ /hosting/capabilities { + include includes/allow_ip; + proxy_pass http://__OFFICE_HOST__.__DOMAIN__:9980; + } + # main websocket + location ~ ^/(.|l)ool/(.*)/ws$ { + include includes/allow_ip; + proxy_pass http://__OFFICE_HOST__.__DOMAIN__:9980; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_read_timeout 36000s; + } + # download, presentation and image upload + location ~ ^/(c|l)ool { + include includes/allow_ip; + proxy_pass http://__OFFICE_HOST__.__DOMAIN__:9980; + } + # Admin Console websocket + location ^~ /(c|l)ool/adminws { + include allow_admin_ip; + proxy_pass http://__OFFICE_HOST__.__DOMAIN__:9980; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_read_timeout 36000s; + } + location / { + include includes/allow_ip; + proxy_pass http://__OFFICE_HOST__.__DOMAIN__:9980; + } + } +}} + + ######################################## + #### association +{{orga + map $http_host $cloud_kaz_map { + hostnames; + include includes/cloud_kaz_map; + } + map $http_host $agora_kaz_map { + hostnames; + include includes/agora_kaz_map; + } + map $http_host $wiki_kaz_map { + hostnames; + include includes/wiki_kaz_map; + } + map $http_host $wp_kaz_map { + hostnames; + include includes/wp_kaz_map; + } + map $http_host $pod_kaz_map { + hostnames; + include includes/pod_kaz_map; + } + + server { + server_name ~^(?.+)-__CASTOPOD_HOST__\.__DOMAIN__$; + include includes/pod_kaz_name; + if ($asso = '') { + set $asso $pod_kaz_map; + } + include includes/port; + ssl_certificate /etc/letsencrypt/live/$ssl_local_cert/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/$ssl_local_cert/privkey.pem; + include includes/proxy_params; + + location / { + include includes/allow_ip; + proxy_pass http://$asso-__CASTOPOD_HOST__.__DOMAIN__:8000; + } + } + + server { + server_name ~^(?.+)-__CLOUD_HOST__\.__DOMAIN__$; + include includes/cloud_kaz_name; + if ($asso = '') { + set $asso $cloud_kaz_map; + } + include includes/port; + ssl_certificate /etc/letsencrypt/live/$ssl_local_cert/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/$ssl_local_cert/privkey.pem; + include includes/proxy_params; + + location / { + include includes/allow_ip; + proxy_pass http://$asso-__CLOUD_HOST__.__DOMAIN__; + } + } + + server { + server_name ~^(?.+)-__OFFICE_HOST__\.__DOMAIN__$; + include includes/port; + ssl_certificate /etc/letsencrypt/live/$ssl_local_cert/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/$ssl_local_cert/privkey.pem; + include includes/proxy_params; + proxy_set_header Host $http_host; + + # static files + location ^~ /loleaflet { + include includes/allow_ip; + proxy_pass http://$asso-__OFFICE_HOST__.__DOMAIN__:9980; + } + location ^~ /browser { + include includes/allow_ip; + proxy_pass http://$asso-__OFFICE_HOST__.__DOMAIN__:9980; + } + # WOPI discovery URL + location ^~ /hosting/discovery { + include includes/allow_ip; + proxy_pass http://$asso-__OFFICE_HOST__.__DOMAIN__:9980; + } + # Capabilities + location ^~ /hosting/capabilities { + include includes/allow_ip; + proxy_pass http://$asso-__OFFICE_HOST__.__DOMAIN__:9980; + } + # main websocket + location ~ ^/(c|l)ool/(.*)/ws$ { + include includes/allow_ip; + proxy_pass http://$asso-__OFFICE_HOST__.__DOMAIN__:9980; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_read_timeout 36000s; + } + # download, presentation and image upload + location ~ ^/(c|l)ool { + include includes/allow_ip; + proxy_pass http://$asso-__OFFICE_HOST__.__DOMAIN__:9980; + } + # Admin Console websocket + location ^~ /(c|l)ool/adminws { + include allow_admin_ip; + proxy_pass http://$asso-__OFFICE_HOST__.__DOMAIN__:9980; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_read_timeout 36000s; + } + location / { + include includes/allow_ip; + proxy_pass http://$asso-__OFFICE_HOST__.__DOMAIN__:9980; + } + } + + server { + server_name ~^(?.+)-__MATTER_HOST__\.__DOMAIN__$; + include includes/agora_kaz_name; + if ($asso = '') { + set $asso $agora_kaz_map; + } + + include includes/port; + ssl_certificate /etc/letsencrypt/live/$ssl_local_cert/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/$ssl_local_cert/privkey.pem; + include includes/proxy_params; + + include includes/allow_ip; + ssl_ecdh_curve prime256v1:secp384r1:secp521r1; + + add_header X-Early-Data $tls1_3_early_data; + location ~ /api/v[0-9]+/(users/)?websocket$ { + proxy_pass http://$asso-__MATTER_HOST__.__DOMAIN__:8000; + proxy_set_header Connection "upgrade"; # test +# test proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; +# test proxy_set_header Connection $connection_upgrade; + client_body_timeout 60; + send_timeout 300; + lingering_timeout 5; + proxy_connect_timeout 90; + proxy_send_timeout 300; + proxy_read_timeout 90s; +# test proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +# test proxy_set_header Proxy ""; + rewrite ^/(.+)$ /$1 break; + } + + location / { + proxy_pass http://$asso-__MATTER_HOST__.__DOMAIN__:8000; + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_read_timeout 600s; + # proxy_cache mattermost_cache; # test + # proxy_cache_lock on; # test + # proxy_cache_min_uses 2; # test + # proxy_cache_revalidate on; # test + # proxy_cache_use_stale timeout; # test + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + } + + server { + server_name ~^(?.+)-__DOKUWIKI_HOST__\.__DOMAIN__$; + include includes/wiki_kaz_name; + if ($asso = '') { + set $asso $wiki_kaz_map; + } + + include includes/port; + ssl_certificate /etc/letsencrypt/live/$ssl_local_cert/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/$ssl_local_cert/privkey.pem; + include includes/proxy_params; + + location / { + include includes/allow_ip; + proxy_pass http://$asso-__DOKUWIKI_HOST__.__DOMAIN__; + } + } + + server { + server_name ~^(?.+)-__WORDPRESS_HOST__\.__DOMAIN__$; + include includes/wp_kaz_name; + if ($asso = '') { + set $asso $wp_kaz_map; + } + + include includes/port; + ssl_certificate /etc/letsencrypt/live/$ssl_local_cert/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/$ssl_local_cert/privkey.pem; + include includes/proxy_params; + + location / { + include includes/allow_ip; + proxy_pass http://$asso-__WORDPRESS_HOST__.__DOMAIN__; + } + } +}} + + ######################################## + #### vigilo kaz +{{vigilo + server { + server_name __VIGILO_HOST__.__DOMAIN__; + include includes/port; + ssl_certificate /etc/letsencrypt/live/__DOMAIN__/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/__DOMAIN__/privkey.pem; + include includes/proxy_params; + + proxy_set_header X-Real-IP $remote_addr; + + location / { + include includes/allow_ip; + proxy_pass http://__VIGILO_HOST__.__DOMAIN__; + + proxy_hide_header 'x-frame-options'; + #proxy_set_header x-frame-options allowall; + #add_header X-Frame-Options "ALLOW-FROM *"; + add_header X-Frame-Options "ALLOWALL"; + + if ($request_method = OPTIONS) { + add_header "Access-Control-Allow-Methods" "GET, POST, OPTIONS, HEAD, DELETE"; + add_header "Access-Control-Allow-Headers" "Authorization, Origin, X-Requested-With, Content-Type, Accept"; + add_header 'Content-Type' 'text/plain charset=UTF-8'; + add_header 'Content-Length' 0; + return 204; + } + + } + } +}} + ######################################## +} diff --git a/dockers/proxy/docker-compose.tmpl.yml.dist b/dockers/proxy/docker-compose.tmpl.yml.dist new file mode 100644 index 0000000..5f3c25c --- /dev/null +++ b/dockers/proxy/docker-compose.tmpl.yml.dist @@ -0,0 +1,242 @@ +# if faut définir les variables d'environnement avant +# ln -s ../dockers.env .env + +version: '3.3' + +services: + reverse-proxy: + image: proxykaz + container_name: ${proxyServName} + restart: ${restartPolicy} + ports: + - ${MAIN_IP}:80:80 + - ${MAIN_IP}:443:443 + # - 80:80 + # - 443:443 + external_links: + - ${proxyServName}:proxy.${domain} +{{web + - ${webServName}:${domain} +}} +{{jirafeau + - ${jirafeauServName}:${fileHost}.${domain} +}} +{{ethercalc + - ${ethercalcServName}:${calcHost}.${domain} +}} +{{etherpad + - ${etherpadServName}:${padHost}.${domain} +}} +{{framadate + - ${framadateServName}:${dateHost}.${domain} +}} +{{ldap + - ${ldapUIName}:${ldapUIHost}.${domain} +}} +{{mobilizon + - ${mobilizonServName}:${mobilizonHost}.${domain} +}} +{{cloud + - ${nextcloudServName}:${cloudHost}.${domain} +}} +{{collabora + - ${officeServName}:${site}-${officeHost}.${domain} +}} +{{paheko + - ${pahekoServName}:${pahekoHost}.${domain} +}} +{{mattermost + - ${mattermostServName}:${matterHost}.${domain} +}} +{{roundcube + - ${roundcubeServName}:${webmailHost}.${domain} +}} +{{gitea + - ${gitServName}:${gitHost}.${domain} +}} +{{dokuwiki + - ${dokuwikiServName}:${dokuwikiHost}.${domain} +}} +{{vigilo + - ${vigiloServName}:${vigiloHost}.${domain} +}} +{{postfix + - ${smtpServName}:${smtpHost}.${domain} +}} +{{vaultwarden + - ${vaultwardenServName}:${vaultwardenHost}.${domain} +}} +{{imapsync + - ${imapsyncServName}:${imapsyncHost}.${domain} +}} +{{castopod + - ${castopodServName}:${castopodHost}.${domain} +}} + +#### BEGIN ORGA HOST +#### END ORGA HOST + networks: + - proxyNet +{{web + - webNet +}} +{{jirafeau + - jirafeauNet +}} +{{ethercalc + - ethercalcNet +}} +{{etherpad + - etherpadNet +}} +{{framadate + - framadateNet +}} +{{ldap + - ldapNet +}} +{{mobilizon + - mobilizonNet +}} +{{cloud + - cloudNet +}} +{{collabora + - collaboraNet +}} +{{paheko + - pahekoNet +}} +{{mattermost + - mattermostNet +}} +{{roundcube + - roundcubeNet +}} +{{gitea + - giteaNet +}} +{{dokuwiki + - dokuwikiNet +}} +{{postfix + - postfixNet +}} +{{vaultwarden + - vaultwardenNet +}} +{{imapsync + - imapsyncNet +}} +{{castopod + - castopodNet +}} + +#### BEGIN ORGA USE_NET +#### END ORGA USE_NET + volumes: + - ../../config/proxy/:/etc/nginx/includes/:rw + - ../../secret/allow_admin_ip:/etc/nginx/allow_admin_ip:ro + - ./config/nginx.conf:/etc/nginx/nginx.conf:rw + - /etc/ssl:/etc/ssl:ro + - /etc/letsencrypt:/etc/letsencrypt:rw + - /etc/localtime:/etc/localtime:ro + - /etc/timezone:/etc/timezone:ro + - /root/mkcert:/root/mkcert:ro + +networks: + proxyNet: + external: true + name: proxyNet +{{web + webNet: + external: true + name: webNet +}} +{{jirafeau + jirafeauNet: + external: true + name: jirafeauNet +}} +{{ethercalc + ethercalcNet: + external: true + name: ethercalcNet +}} +{{etherpad + etherpadNet: + external: true + name: etherpadNet +}} +{{framadate + framadateNet: + external: true + name: framadateNet +}} +{{ldap + ldapNet: + external: true + name: ldapNet +}} +{{mobilizon + mobilizonNet: + external: true + name: mobilizonNet +}} +{{cloud + cloudNet: + external: true + name: cloudNet +}} +{{collabora + collaboraNet: + external: true + name: collaboraNet +}} +{{paheko + pahekoNet: + external: true + name: pahekoNet +}} +{{mattermost + mattermostNet: + external: true + name: mattermostNet +}} +{{roundcube + roundcubeNet: + external: true + name: roundcubeNet +}} +{{gitea + giteaNet: + external: true + name: giteaNet +}} +{{dokuwiki + dokuwikiNet: + external: true + name: dokuwikiNet +}} +{{postfix + postfixNet: + external: true + name: postfixNet +}} +{{vaultwarden + vaultwardenNet: + external: true + name: vaultwardenNet +}} +{{imapsync + imapsyncNet: + external: true + name: imapsyncNet +}} +{{castopod + castopodNet: + external: true + name: castopodNet +}} +#### BEGIN ORGA DEF_NET +#### END ORGA DEF_NET diff --git a/dockers/proxy/proxy-gen.sh b/dockers/proxy/proxy-gen.sh new file mode 100755 index 0000000..a264d26 --- /dev/null +++ b/dockers/proxy/proxy-gen.sh @@ -0,0 +1,127 @@ +#!/bin/bash + +KAZ_ROOT=$(cd "$(dirname $0)/../.."; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars +. "${DOCKERS_ENV}" + +printKazMsg "\n *** Proxy update config" + +NGINX_TMPL=config/nginx.tmpl.conf +NGINX_CONF=config/nginx.conf +DOCKER_DIST=docker-compose.tmpl.yml.dist +DOCKER_TMPL=docker-compose.tmpl.yml +DOCKER_CONF=docker-compose.yml + +for service in agora cloud paheko wiki wp pod; do + touch "${KAZ_CONF_PROXY_DIR}/${service}_kaz_map" + touch "${KAZ_CONF_PROXY_DIR}/${service}_kaz_name" +done + +# update port +PROXY_ALLOW_CFG="${KAZ_CONF_PROXY_DIR}/allow_ip" +if [ ! -f "${PROXY_ALLOW_CFG}" ]; then + cat > "${PROXY_ALLOW_CFG}" < "${PROXY_PORT_CFG}" < "${PROXY_REDIRECT}" < /dev/null || true +RUN if [ -f /.apt-mirror-config ] ; then . /.apt-mirror-config && sed -i \ + -e "s%s\?://deb.debian.org%://${APT_MIRROR_DEBIAN}%g" \ + -e "s%s\?://security.debian.org%://${APT_MIRROR_DEBIAN_SECURITY}%g" \ + -e "s%s\?://archive.ubuntu.com%://${APT_MIRROR_UBUNTU}%g" \ + -e "s%s\?://security.ubuntu.com%://${APT_MIRROR_UBUNTU_SECURITY}%g" \ + /etc/apt/sources.list; fi + +######################################## +WORKDIR /quota +COPY dockers/quotas/html/ . +EXPOSE 3000 +WORKDIR /quota/express_webapp +RUN npm install --no-audit +CMD ["npm","start"] diff --git a/dockers/quotas/build.sh b/dockers/quotas/build.sh new file mode 100755 index 0000000..880955c --- /dev/null +++ b/dockers/quotas/build.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +KAZ_ROOT=$(cd "$(dirname $0)/../.."; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +########################################################################### +printKazMsg "\n *** Création du Dockerfile quotas" + +cd "${KAZ_ROOT}" + +docker build -t quotaskaz . -f dockers/quotas/Dockerfile diff --git a/dockers/quotas/docker-compose.yml b/dockers/quotas/docker-compose.yml new file mode 100644 index 0000000..3b8dcea --- /dev/null +++ b/dockers/quotas/docker-compose.yml @@ -0,0 +1,45 @@ +version: "3.5" + +services: + + quotas: + # ports: + # - 8084:3000 + image: quotaskaz + container_name: ${quotasServName} + # restart: ${restartPolicy} + depends_on: + - db + networks: + - quotasNet + links: + - db + env_file: + - ../../secret/env-${quotasDBName} + # labels: + # - "traefik.enable=true" + # - "traefik.http.routers.${quotasServName}.rule=Host(`${quotaHost}.${domain}`)" + + + db: + image: mariadb:10.5 + container_name: ${quotasDBName} + restart: ${restartPolicy} + networks: + - quotasNet + env_file: + - ../../secret/env-${quotasDBName} + volumes: + - ./initdb.d:/docker-entrypoint-initdb.d:ro + - quotasDB:/var/lib/mysql + - /home/sauve/:/svg/ + - /etc/localtime:/etc/localtime:ro + - /etc/timezone:/etc/timezone:ro + +volumes: + quotasDB: + +networks: + quotasNet: + external: true + name: quotasNet diff --git a/dockers/quotas/html/.gitignore b/dockers/quotas/html/.gitignore new file mode 100644 index 0000000..69ff398 --- /dev/null +++ b/dockers/quotas/html/.gitignore @@ -0,0 +1,2 @@ +express_webapp/node_modules +kaz.quota/node_modules diff --git a/dockers/quotas/html/LancementAffichageQuota.txt b/dockers/quotas/html/LancementAffichageQuota.txt new file mode 100644 index 0000000..6295311 --- /dev/null +++ b/dockers/quotas/html/LancementAffichageQuota.txt @@ -0,0 +1,51 @@ +------------------------------------------------ + + + + + KAZ : AFFICHAGE DES QUOTAS + + + + + +------------------------------------------------ +Auteur du document : GUIQUERRO Nathaniel +Création de l'affichage de Quota : GAULTIER Lauryne, GUIQUERRO Nathaniel +Date : 7 / 01 / 2022 +------------------------------------------------ + +Installation : + +afin tout lancement 'npm' doit déjà être installé préalablement +ensuite il faut récuperer le code contenue dans kaz.quota et express_webapp du git + +Vérifiez bien que express_webapp contient un répertoire bin avec un fichier www dedans. + +ensuite ouvez un terminale et placez vous dans le répertoire express_webapp grâce à la +commande cd ./express_webapp et éxécutez al commande 'npm install' et après 'npm start' + +Si aucun message d'erreur s'affiche rendez vous sur votre navigateur préféré pour vous connecter +sur l'url : http://localhost:3000/ + + +Bonne Lecture ! + +Cordialement l'équipe Nelph + + +------------------------------------------------ + +Installation avec Docker : + +Lancement WEB seul : +docker build -t quota . +docker run -it -p 127.0.0.1:3000:3000 quotas + +Le page est accessible avec firefox à l'adresse : localhost:3000 + +Lancement WEB et DB : +docker-compose up + +La DB peut être accédé avec la commande : +mysql -h 127.0.0.1 -prougail-saucisse diff --git a/dockers/quotas/html/README.md b/dockers/quotas/html/README.md new file mode 100644 index 0000000..f011185 --- /dev/null +++ b/dockers/quotas/html/README.md @@ -0,0 +1,50 @@ +# KAZ : AFFICHAGE DES QUOTAS + +Auteur du document : GUIQUERRO Nathaniel, GAULTIER Lauryne +Création de l'affichage de Quota : GAULTIER Lauryne, GUIQUERRO Nathaniel +Date : 7 / 01 / 2022 + +# Installation : +Avant tout lancement 'npm' doit déjà être installé. +Ensuite il faut récuperer le code contenu dans kaz.quota et express_webapp du git. +Vérifiez bien que express_webapp contient un répertoire bin avec un fichier www dedans. + +Ensuite ouvez un terminal et placez vous dans le répertoire express_webapp grâce à la +commande cd ./express_webapp et exécutez les commande 'npm install' et 'npm start'. + +Si aucun message d'erreur ne s'affiche, rendez vous sur votre navigateur préféré pour vous connecter sur l'url : + +```bash +http://localhost:3000/ +``` + +Bonne lecture ! + +Cordialement l'équipe Nelph + +# Installation avec Docker : + +Lancement WEB et DB : + +```bash +cd /kaz/src +``` + +```bash +./lancementDocker.sh +``` +Initialiser la base de données: +```bash +./initDB.sh tartelette +``` + +Vous pouvez accéder à la DB avec la commande : +```bash +mysql -h 127.0.0.1 -tartelette +``` +Le page est accessible par navigateur à l'adresse : + +```bash +localhost:3000 +``` + diff --git a/dockers/quotas/html/express_webapp/app.js b/dockers/quotas/html/express_webapp/app.js new file mode 100644 index 0000000..5af1867 --- /dev/null +++ b/dockers/quotas/html/express_webapp/app.js @@ -0,0 +1,52 @@ +var createError = require('http-errors'); +var express = require('express'); +var path = require('path'); +var cookieParser = require('cookie-parser'); +var logger = require('morgan'); + +var indexRouter = require('./routes/index'); +var usersRouter = require('./routes/users'); +var indexAdminRouter = require('./routes/indexAdmin'); +var usersAdminRouter = require('./routes/usersAdmin'); +var groupesRouter = require('./routes/groupes'); +var ServiceGRouter = require('./routes/serviceg'); + +var app = express(); + +// view engine setup +app.set('views', path.join(__dirname, 'views')); +app.set('view engine', 'jade'); + +app.use(logger('dev')); +app.use(express.json()); +app.use(express.urlencoded({ extended: false })); +app.use(cookieParser()); +app.use(express.static(path.join(__dirname, 'public'))); + +app.use('/', indexRouter); +app.use('/users', usersRouter); + + +app.use('/admin/', indexAdminRouter); +app.use('/admin/users', usersAdminRouter); +app.use('/admin/groupes',groupesRouter); +app.use('/admin/serviceg',ServiceGRouter); + + +// catch 404 and forward to error handler +app.use(function(req, res, next) { + next(createError(404)); +}); + +// error handler +app.use(function(err, req, res, next) { + // set locals, only providing error in development + res.locals.message = err.message; + res.locals.error = req.app.get('env') === 'development' ? err : {}; + + // render the error page + res.status(err.status || 500); + res.render('error'); +}); + +module.exports = app; diff --git a/dockers/quotas/html/express_webapp/bin/www b/dockers/quotas/html/express_webapp/bin/www new file mode 100755 index 0000000..08f0b16 --- /dev/null +++ b/dockers/quotas/html/express_webapp/bin/www @@ -0,0 +1,90 @@ +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var app = require('../app'); +var debug = require('debug')('express-webapp:server'); +var http = require('http'); + +/** + * Get port from environment and store in Express. + */ + +var port = normalizePort(process.env.PORT || '3000'); +app.set('port', port); + +/** + * Create HTTP server. + */ + +var server = http.createServer(app); + +/** + * Listen on provided port, on all network interfaces. + */ + +server.listen(port); +server.on('error', onError); +server.on('listening', onListening); + +/** + * Normalize a port into a number, string, or false. + */ + +function normalizePort(val) { + var port = parseInt(val, 10); + + if (isNaN(port)) { + // named pipe + return val; + } + + if (port >= 0) { + // port number + return port; + } + + return false; +} + +/** + * Event listener for HTTP server "error" event. + */ + +function onError(error) { + if (error.syscall !== 'listen') { + throw error; + } + + var bind = typeof port === 'string' + ? 'Pipe ' + port + : 'Port ' + port; + + // handle specific listen errors with friendly messages + switch (error.code) { + case 'EACCES': + console.error(bind + ' requires elevated privileges'); + process.exit(1); + break; + case 'EADDRINUSE': + console.error(bind + ' is already in use'); + process.exit(1); + break; + default: + throw error; + } +} + +/** + * Event listener for HTTP server "listening" event. + */ + +function onListening() { + var addr = server.address(); + var bind = typeof addr === 'string' + ? 'pipe ' + addr + : 'port ' + addr.port; + debug('Listening on ' + bind); +} diff --git a/dockers/quotas/html/express_webapp/data/db.js b/dockers/quotas/html/express_webapp/data/db.js new file mode 100644 index 0000000..e69de29 diff --git a/dockers/quotas/html/express_webapp/database.js b/dockers/quotas/html/express_webapp/database.js new file mode 100644 index 0000000..809a84b --- /dev/null +++ b/dockers/quotas/html/express_webapp/database.js @@ -0,0 +1,18 @@ + +// Creating new Object of Sequelize +const sequelize = new Sequelize( + process.env.MYSQL_DATABASE, + process.env.MYSQL_USER, + process.env.MYSQL_PASSWORD, { + // Explicitly specifying + // mysql database + dialect: 'mariadb', + // By default host is 'localhost' + host: 'db' + } +); + +// Exporting the sequelize object. +// We can use it in another file +// for creating models +module.exports = sequelize diff --git a/dockers/quotas/html/express_webapp/package-lock.json b/dockers/quotas/html/express_webapp/package-lock.json new file mode 100644 index 0000000..ef74c48 --- /dev/null +++ b/dockers/quotas/html/express_webapp/package-lock.json @@ -0,0 +1,3149 @@ +{ + "name": "express-webapp", + "version": "0.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "express-webapp", + "version": "0.0.0", + "dependencies": { + "alert": "^5.0.10", + "billboard.js": "^3.3.3", + "chart.js": "^3.7.1", + "cookie-parser": "~1.4.4", + "debug": "~2.6.9", + "express": "~4.16.1", + "http-errors": "~1.6.3", + "jade": "~1.11.0", + "mariadb": "^2.5.5", + "morgan": "~1.9.1", + "mysql": "^2.18.1", + "node-localstorage": "^2.2.1", + "sequelize": "^6.15.0", + "xg-xcharts": "^0.2.2" + } + }, + "node_modules/@types/debug": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", + "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/geojson": { + "version": "7946.0.8", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.8.tgz", + "integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==" + }, + "node_modules/@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" + }, + "node_modules/@types/node": { + "version": "14.18.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.10.tgz", + "integrity": "sha512-6iihJ/Pp5fsFJ/aEDGyvT4pHGmCpq7ToQ/yf4bl5SbVAvwpspYJ+v3jO7n8UyjhQVHTy+KNszOozDdv+O6sovQ==" + }, + "node_modules/accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dependencies": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-2.7.0.tgz", + "integrity": "sha1-q259nYhqrKiwhbwzEreaGYQz8Oc=", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-1.0.9.tgz", + "integrity": "sha1-VbtemGkVB7dFedBRNBMhfDgMVM8=", + "dependencies": { + "acorn": "^2.1.0" + } + }, + "node_modules/alert": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/alert/-/alert-5.0.10.tgz", + "integrity": "sha512-veNmvhw+wPry802GoKfpXlqAeJ6JAF5JQ7C0Sw/ukMZ+KINvQITHFQO8IIaXjqJ/TBpQnp5A/y0bBknI50/LZQ==", + "dependencies": { + "is-program-installed": "2.2.0" + }, + "bin": { + "alert": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "dependencies": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "engines": { + "node": ">=0.4.2" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "node_modules/asap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/asap/-/asap-1.0.0.tgz", + "integrity": "sha1-sqRdpf36ILBJb8N2jMJ8EvqRan0=" + }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/bignumber.js": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", + "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==", + "engines": { + "node": "*" + } + }, + "node_modules/billboard.js": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/billboard.js/-/billboard.js-3.3.3.tgz", + "integrity": "sha512-ciwsCXtdZt0dtScP4/lCeLnugiWNNrY++gv64m35jXIf2hH92PQEm/56SXcBuvsCmrH2FYpl0uDOxcWN/QALfw==", + "dependencies": { + "d3-axis": "^3.0.0", + "d3-brush": "^3.0.0", + "d3-drag": "^3.0.0", + "d3-dsv": "^3.0.1", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-selection": "^3.0.0", + "d3-shape": "^3.1.0", + "d3-time-format": "^4.1.0", + "d3-transition": "^3.0.1", + "d3-zoom": "^3.0.0" + } + }, + "node_modules/body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "dependencies": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "dependencies": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/character-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-1.2.1.tgz", + "integrity": "sha1-wN3kqxgnE7kZuXCVmhI+zBow/NY=" + }, + "node_modules/chart.js": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.7.1.tgz", + "integrity": "sha512-8knRegQLFnPQAheZV8MjxIXc5gQEfDFD897BJgv/klO/vtIyFFmgMXrNfgrXpbTr/XbTturxRgxIXx/Y+ASJBA==" + }, + "node_modules/clean-css": { + "version": "3.4.28", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.28.tgz", + "integrity": "sha1-vxlF6C/ICPVWlebd6uwBQA79A/8=", + "dependencies": { + "commander": "2.8.x", + "source-map": "0.4.x" + }, + "bin": { + "cleancss": "bin/cleancss" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clean-css/node_modules/commander": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "dependencies": { + "graceful-readlink": ">= 1.0.0" + }, + "engines": { + "node": ">= 0.6.x" + } + }, + "node_modules/cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dependencies": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "node_modules/commander": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.6.0.tgz", + "integrity": "sha1-nfflL7Kgyw+4kFjugMMQQiXzfh0=", + "engines": { + "node": ">= 0.6.x" + } + }, + "node_modules/constantinople": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.0.2.tgz", + "integrity": "sha1-S5RdmTeQe82Y7ldRIsOBdRZUQUE=", + "dependencies": { + "acorn": "^2.1.0" + } + }, + "node_modules/content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-parser": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", + "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", + "dependencies": { + "cookie": "0.4.1", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/css": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/css/-/css-1.0.8.tgz", + "integrity": "sha1-k4aBHKgrzMnuf7WnMrHioxfIo+c=", + "dependencies": { + "css-parse": "1.0.4", + "css-stringify": "1.0.5" + } + }, + "node_modules/css-parse": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.0.4.tgz", + "integrity": "sha1-OLBQP7+dqfVOnB29pg4UXHcRe90=" + }, + "node_modules/css-stringify": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/css-stringify/-/css-stringify-1.0.5.tgz", + "integrity": "sha1-sNBClG2ylTu50pKQCmy19tASIDE=" + }, + "node_modules/d3": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.3.0.tgz", + "integrity": "sha512-MDRLJCMK232OJQRqGljQ/gCxtB8k3/sLKFjftMjzPB3nKVUODpdW9Rb3vcq7U8Ka5YKoZkAmp++Ur6I+6iNWIw==", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "3", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.0.1.tgz", + "integrity": "sha512-6/SlHkDOBLyQSJ1j1Ghs82OIUXpKWlR0hCsw0XrLSQhuUPuCSmLQ1QPH98vpnQxMUQM2/gfAkUEWsupVpd9JGw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-3.0.1.tgz", + "integrity": "sha512-0Oc4D0KyhwhM7ZL0RMnfGycLN7hxHB8CMmwZ3+H26PWAG0ozNuYG5hXSDNgmP1SgJkQMrlG6cP20HoaSbvcJTQ==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.2.tgz", + "integrity": "sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-dsv/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.0.1.tgz", + "integrity": "sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA==", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.1.tgz", + "integrity": "sha512-LtAIu54UctRmhGKllleflmHalttH3zkfSi4NlKrTAoFKjC+AFBJohsCAdgCBYQwH0F8hIOGY89X1pPqAchlMkA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.0.1.tgz", + "integrity": "sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", + "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.1.0.tgz", + "integrity": "sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ==", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.0.0.tgz", + "integrity": "sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3/node_modules/d3-array": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.1.1.tgz", + "integrity": "sha512-33qQ+ZoZlli19IFiQx4QEpf2CBEayMRzhlisJHSCsSUbDXv6ZishqS1x7uFVClKG4Wr7rZVHvaAttoLow6GqdQ==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delaunator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz", + "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==", + "dependencies": { + "robust-predicates": "^3.0.0" + } + }, + "node_modules/denque": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", + "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "node_modules/dottie": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.2.tgz", + "integrity": "sha512-fmrwR04lsniq/uSr8yikThDTrM7epXHBAAjH9TbeH3rEA8tdCO7mRzB9hdmdGyJCxF8KERo9CITcm3kGuoyMhg==" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "dependencies": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.3", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.2", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" + }, + "node_modules/graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" + }, + "node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflection": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.2.tgz", + "integrity": "sha512-cmZlljCRTBFouT8UzMzrGcVEvkv6D/wBdcdKG7J1QH5cXjtU75Dm+P27v9EKu/Y43UYyCJd1WC4zLebRrC8NBw==", + "engines": [ + "node >= 0.4.0" + ] + }, + "node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "node_modules/is-program-installed": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-program-installed/-/is-program-installed-2.2.0.tgz", + "integrity": "sha512-QMtzB/n6+G0iXDfi4ErIn1mZEZgoTLQxjgBdvMG2iPN/xWmjjXT53XOfaqcCT9At4B7QiMBY3Hvf8N4DB7MNtA==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "node_modules/jade": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/jade/-/jade-1.11.0.tgz", + "integrity": "sha1-nIDlOMEtP7lcjZu5VZ+gzAQEBf0=", + "dependencies": { + "character-parser": "1.2.1", + "clean-css": "^3.1.9", + "commander": "~2.6.0", + "constantinople": "~3.0.1", + "jstransformer": "0.0.2", + "mkdirp": "~0.5.0", + "transformers": "2.1.0", + "uglify-js": "^2.4.19", + "void-elements": "~2.0.1", + "with": "~4.0.0" + }, + "bin": { + "jade": "bin/jade.js" + } + }, + "node_modules/jstransformer": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-0.0.2.tgz", + "integrity": "sha1-eq4pqQPRls+glz2IXT5HlH7Ndqs=", + "dependencies": { + "is-promise": "^2.0.0", + "promise": "^6.0.1" + } + }, + "node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "node_modules/longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mariadb": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/mariadb/-/mariadb-2.5.5.tgz", + "integrity": "sha512-6dklvcKWuuaV1JjAwnE2ezR+jTt7JrZHftgeHHBmjB0wgfaUpdxol1DPWclwMcCrsO9yoM0FuCOiCcCgXc//9Q==", + "dependencies": { + "@types/geojson": "^7946.0.7", + "@types/node": "^14.14.28", + "denque": "^1.5.0", + "iconv-lite": "^0.6.3", + "long": "^4.0.0", + "moment-timezone": "^0.5.33", + "please-upgrade-node": "^3.2.0" + }, + "engines": { + "node": ">= 10.13" + } + }, + "node_modules/mariadb/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "bin": { + "mime": "cli.js" + } + }, + "node_modules/mime-db": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.34", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", + "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "dependencies": { + "mime-db": "1.51.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.34", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.34.tgz", + "integrity": "sha512-3zAEHh2hKUs3EXLESx/wsgw6IQdusOT8Bxm3D9UrHPQR7zlMmzwybC8zHEM1tQ4LJwP7fcxrWr8tuBg05fFCbg==", + "dependencies": { + "moment": ">= 2.9.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/morgan": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", + "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", + "dependencies": { + "basic-auth": "~2.0.0", + "debug": "2.6.9", + "depd": "~1.1.2", + "on-finished": "~2.3.0", + "on-headers": "~1.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/mysql": { + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz", + "integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==", + "dependencies": { + "bignumber.js": "9.0.0", + "readable-stream": "2.3.7", + "safe-buffer": "5.1.2", + "sqlstring": "2.3.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-localstorage": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-localstorage/-/node-localstorage-2.2.1.tgz", + "integrity": "sha512-vv8fJuOUCCvSPjDjBLlMqYMHob4aGjkmrkaE42/mZr0VT+ZAU10jRF8oTnX9+pgU9/vYJ8P7YT3Vd6ajkmzSCw==", + "dependencies": { + "write-file-atomic": "^1.1.4" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/optimist": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", + "integrity": "sha1-yQlBrVnkJzMokjB00s8ufLxuwNk=", + "dependencies": { + "wordwrap": "~0.0.2" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "node_modules/pg-connection-string": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", + "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" + }, + "node_modules/please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dependencies": { + "semver-compare": "^1.0.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/promise": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-6.1.0.tgz", + "integrity": "sha1-LOcp9rlLRcJoka0GAsXJDgTG7vY=", + "dependencies": { + "asap": "~1.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "dependencies": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/retry-as-promised": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-5.0.0.tgz", + "integrity": "sha512-6S+5LvtTl2ggBumk04hBo/4Uf6fRJUwIgunGZ7CYEBCeufGFW1Pu6ucUf/UskHeWOIsUcLOGLFXPig5tR5V1nA==" + }, + "node_modules/right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "dependencies": { + "align-text": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/robust-predicates": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz", + "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==" + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=" + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=" + }, + "node_modules/send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/sequelize": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.15.0.tgz", + "integrity": "sha512-Ks2jSaKMfICZ8jMlhH401fLw5ikE8Vqt6slcR2peKOn4lA3H+LRfXdlnAl/CUDO1MflFl7PhifnzPxwhamciGQ==", + "dependencies": { + "@types/debug": "^4.1.7", + "debug": "^4.3.3", + "dottie": "^2.0.2", + "inflection": "^1.13.1", + "lodash": "^4.17.21", + "moment": "^2.29.1", + "moment-timezone": "^0.5.34", + "pg-connection-string": "^2.5.0", + "retry-as-promised": "^5.0.0", + "semver": "^7.3.5", + "sequelize-pool": "^7.1.0", + "toposort-class": "^1.0.1", + "uuid": "^8.3.2", + "validator": "^13.7.0", + "wkx": "^0.5.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/sequelize-pool": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz", + "integrity": "sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/sequelize/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/sequelize/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "node_modules/slide": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", + "engines": { + "node": "*" + } + }, + "node_modules/source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/sqlstring": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", + "integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/toposort-class": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", + "integrity": "sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg=" + }, + "node_modules/transformers": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/transformers/-/transformers-2.1.0.tgz", + "integrity": "sha1-XSPLNVYd2F3Gf7hIIwm0fVPM6ac=", + "dependencies": { + "css": "~1.0.8", + "promise": "~2.0", + "uglify-js": "~2.2.5" + } + }, + "node_modules/transformers/node_modules/is-promise": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-1.0.1.tgz", + "integrity": "sha1-MVc3YcBX4zwukaq56W2gjO++duU=" + }, + "node_modules/transformers/node_modules/promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-2.0.0.tgz", + "integrity": "sha1-RmSKqdYFr10ucMMCS/WUNtoCuA4=", + "dependencies": { + "is-promise": "~1" + } + }, + "node_modules/transformers/node_modules/source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/transformers/node_modules/uglify-js": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.2.5.tgz", + "integrity": "sha1-puAqcNg5eSuXgEiLe4sYTAlcmcc=", + "dependencies": { + "optimist": "~0.3.5", + "source-map": "~0.1.7" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dependencies": { + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + }, + "optionalDependencies": { + "uglify-to-browserify": "~1.0.0" + } + }, + "node_modules/uglify-js/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "optional": true + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validator": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", + "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/with": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/with/-/with-4.0.3.tgz", + "integrity": "sha1-7v0VTp550sjTQXtkeo8U2f7M4U4=", + "dependencies": { + "acorn": "^1.0.1", + "acorn-globals": "^1.0.3" + } + }, + "node_modules/with/node_modules/acorn": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-1.2.2.tgz", + "integrity": "sha1-yM4n3grMdtiW0rH6099YjZ6C8BQ=", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/wkx": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", + "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/write-file-atomic": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz", + "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=", + "dependencies": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "slide": "^1.1.5" + } + }, + "node_modules/xg-xcharts": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/xg-xcharts/-/xg-xcharts-0.2.2.tgz", + "integrity": "sha1-PpqYF7c7+9D4UQ/PSyCpT3c0yJo=", + "dependencies": { + "d3": ">=4.1.0" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dependencies": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + }, + "dependencies": { + "@types/debug": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", + "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", + "requires": { + "@types/ms": "*" + } + }, + "@types/geojson": { + "version": "7946.0.8", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.8.tgz", + "integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==" + }, + "@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" + }, + "@types/node": { + "version": "14.18.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.10.tgz", + "integrity": "sha512-6iihJ/Pp5fsFJ/aEDGyvT4pHGmCpq7ToQ/yf4bl5SbVAvwpspYJ+v3jO7n8UyjhQVHTy+KNszOozDdv+O6sovQ==" + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-2.7.0.tgz", + "integrity": "sha1-q259nYhqrKiwhbwzEreaGYQz8Oc=" + }, + "acorn-globals": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-1.0.9.tgz", + "integrity": "sha1-VbtemGkVB7dFedBRNBMhfDgMVM8=", + "requires": { + "acorn": "^2.1.0" + } + }, + "alert": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/alert/-/alert-5.0.10.tgz", + "integrity": "sha512-veNmvhw+wPry802GoKfpXlqAeJ6JAF5JQ7C0Sw/ukMZ+KINvQITHFQO8IIaXjqJ/TBpQnp5A/y0bBknI50/LZQ==", + "requires": { + "is-program-installed": "2.2.0" + } + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "requires": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "asap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/asap/-/asap-1.0.0.tgz", + "integrity": "sha1-sqRdpf36ILBJb8N2jMJ8EvqRan0=" + }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "bignumber.js": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", + "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" + }, + "billboard.js": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/billboard.js/-/billboard.js-3.3.3.tgz", + "integrity": "sha512-ciwsCXtdZt0dtScP4/lCeLnugiWNNrY++gv64m35jXIf2hH92PQEm/56SXcBuvsCmrH2FYpl0uDOxcWN/QALfw==", + "requires": { + "d3-axis": "^3.0.0", + "d3-brush": "^3.0.0", + "d3-drag": "^3.0.0", + "d3-dsv": "^3.0.1", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-selection": "^3.0.0", + "d3-shape": "^3.1.0", + "d3-time-format": "^4.1.0", + "d3-transition": "^3.0.1", + "d3-zoom": "^3.0.0" + } + }, + "body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" + } + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "requires": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + } + }, + "character-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-1.2.1.tgz", + "integrity": "sha1-wN3kqxgnE7kZuXCVmhI+zBow/NY=" + }, + "chart.js": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.7.1.tgz", + "integrity": "sha512-8knRegQLFnPQAheZV8MjxIXc5gQEfDFD897BJgv/klO/vtIyFFmgMXrNfgrXpbTr/XbTturxRgxIXx/Y+ASJBA==" + }, + "clean-css": { + "version": "3.4.28", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.28.tgz", + "integrity": "sha1-vxlF6C/ICPVWlebd6uwBQA79A/8=", + "requires": { + "commander": "2.8.x", + "source-map": "0.4.x" + }, + "dependencies": { + "commander": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "requires": { + "graceful-readlink": ">= 1.0.0" + } + } + } + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "commander": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.6.0.tgz", + "integrity": "sha1-nfflL7Kgyw+4kFjugMMQQiXzfh0=" + }, + "constantinople": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.0.2.tgz", + "integrity": "sha1-S5RdmTeQe82Y7ldRIsOBdRZUQUE=", + "requires": { + "acorn": "^2.1.0" + } + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + }, + "cookie-parser": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", + "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", + "requires": { + "cookie": "0.4.1", + "cookie-signature": "1.0.6" + } + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "css": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/css/-/css-1.0.8.tgz", + "integrity": "sha1-k4aBHKgrzMnuf7WnMrHioxfIo+c=", + "requires": { + "css-parse": "1.0.4", + "css-stringify": "1.0.5" + } + }, + "css-parse": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.0.4.tgz", + "integrity": "sha1-OLBQP7+dqfVOnB29pg4UXHcRe90=" + }, + "css-stringify": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/css-stringify/-/css-stringify-1.0.5.tgz", + "integrity": "sha1-sNBClG2ylTu50pKQCmy19tASIDE=" + }, + "d3": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.3.0.tgz", + "integrity": "sha512-MDRLJCMK232OJQRqGljQ/gCxtB8k3/sLKFjftMjzPB3nKVUODpdW9Rb3vcq7U8Ka5YKoZkAmp++Ur6I+6iNWIw==", + "requires": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "3", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "dependencies": { + "d3-array": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.1.1.tgz", + "integrity": "sha512-33qQ+ZoZlli19IFiQx4QEpf2CBEayMRzhlisJHSCsSUbDXv6ZishqS1x7uFVClKG4Wr7rZVHvaAttoLow6GqdQ==", + "requires": { + "internmap": "1 - 2" + } + } + } + }, + "d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "requires": { + "internmap": "^1.0.0" + } + }, + "d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==" + }, + "d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "requires": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + } + }, + "d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "requires": { + "d3-path": "1 - 3" + } + }, + "d3-color": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.0.1.tgz", + "integrity": "sha512-6/SlHkDOBLyQSJ1j1Ghs82OIUXpKWlR0hCsw0XrLSQhuUPuCSmLQ1QPH98vpnQxMUQM2/gfAkUEWsupVpd9JGw==" + }, + "d3-contour": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-3.0.1.tgz", + "integrity": "sha512-0Oc4D0KyhwhM7ZL0RMnfGycLN7hxHB8CMmwZ3+H26PWAG0ozNuYG5hXSDNgmP1SgJkQMrlG6cP20HoaSbvcJTQ==", + "requires": { + "d3-array": "2 - 3" + } + }, + "d3-delaunay": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.2.tgz", + "integrity": "sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==", + "requires": { + "delaunator": "5" + } + }, + "d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==" + }, + "d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "requires": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + } + }, + "d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "requires": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==" + }, + "d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "requires": { + "d3-dsv": "1 - 3" + } + }, + "d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "requires": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + } + }, + "d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==" + }, + "d3-geo": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.0.1.tgz", + "integrity": "sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA==", + "requires": { + "d3-array": "2.5.0 - 3" + } + }, + "d3-hierarchy": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.1.tgz", + "integrity": "sha512-LtAIu54UctRmhGKllleflmHalttH3zkfSi4NlKrTAoFKjC+AFBJohsCAdgCBYQwH0F8hIOGY89X1pPqAchlMkA==" + }, + "d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "requires": { + "d3-color": "1 - 3" + } + }, + "d3-path": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.0.1.tgz", + "integrity": "sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w==" + }, + "d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==" + }, + "d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==" + }, + "d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==" + }, + "d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "requires": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + } + }, + "d3-scale-chromatic": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", + "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", + "requires": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + } + }, + "d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==" + }, + "d3-shape": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.1.0.tgz", + "integrity": "sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ==", + "requires": { + "d3-path": "1 - 3" + } + }, + "d3-time": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.0.0.tgz", + "integrity": "sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==", + "requires": { + "d3-array": "2 - 3" + } + }, + "d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "requires": { + "d3-time": "1 - 3" + } + }, + "d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==" + }, + "d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "requires": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + } + }, + "d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "requires": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "delaunator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz", + "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==", + "requires": { + "robust-predicates": "^3.0.0" + } + }, + "denque": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", + "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "dottie": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.2.tgz", + "integrity": "sha512-fmrwR04lsniq/uSr8yikThDTrM7epXHBAAjH9TbeH3rEA8tdCO7mRzB9hdmdGyJCxF8KERo9CITcm3kGuoyMhg==" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "requires": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.3", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.2", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + } + } + }, + "finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" + }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + }, + "inflection": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.2.tgz", + "integrity": "sha512-cmZlljCRTBFouT8UzMzrGcVEvkv6D/wBdcdKG7J1QH5cXjtU75Dm+P27v9EKu/Y43UYyCJd1WC4zLebRrC8NBw==" + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-program-installed": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-program-installed/-/is-program-installed-2.2.0.tgz", + "integrity": "sha512-QMtzB/n6+G0iXDfi4ErIn1mZEZgoTLQxjgBdvMG2iPN/xWmjjXT53XOfaqcCT9At4B7QiMBY3Hvf8N4DB7MNtA==" + }, + "is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "jade": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/jade/-/jade-1.11.0.tgz", + "integrity": "sha1-nIDlOMEtP7lcjZu5VZ+gzAQEBf0=", + "requires": { + "character-parser": "1.2.1", + "clean-css": "^3.1.9", + "commander": "~2.6.0", + "constantinople": "~3.0.1", + "jstransformer": "0.0.2", + "mkdirp": "~0.5.0", + "transformers": "2.1.0", + "uglify-js": "^2.4.19", + "void-elements": "~2.0.1", + "with": "~4.0.0" + } + }, + "jstransformer": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-0.0.2.tgz", + "integrity": "sha1-eq4pqQPRls+glz2IXT5HlH7Ndqs=", + "requires": { + "is-promise": "^2.0.0", + "promise": "^6.0.1" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "mariadb": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/mariadb/-/mariadb-2.5.5.tgz", + "integrity": "sha512-6dklvcKWuuaV1JjAwnE2ezR+jTt7JrZHftgeHHBmjB0wgfaUpdxol1DPWclwMcCrsO9yoM0FuCOiCcCgXc//9Q==", + "requires": { + "@types/geojson": "^7946.0.7", + "@types/node": "^14.14.28", + "denque": "^1.5.0", + "iconv-lite": "^0.6.3", + "long": "^4.0.0", + "moment-timezone": "^0.5.33", + "please-upgrade-node": "^3.2.0" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + }, + "mime-db": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" + }, + "mime-types": { + "version": "2.1.34", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", + "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "requires": { + "mime-db": "1.51.0" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + }, + "moment-timezone": { + "version": "0.5.34", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.34.tgz", + "integrity": "sha512-3zAEHh2hKUs3EXLESx/wsgw6IQdusOT8Bxm3D9UrHPQR7zlMmzwybC8zHEM1tQ4LJwP7fcxrWr8tuBg05fFCbg==", + "requires": { + "moment": ">= 2.9.0" + } + }, + "morgan": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", + "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", + "requires": { + "basic-auth": "~2.0.0", + "debug": "2.6.9", + "depd": "~1.1.2", + "on-finished": "~2.3.0", + "on-headers": "~1.0.1" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "mysql": { + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz", + "integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==", + "requires": { + "bignumber.js": "9.0.0", + "readable-stream": "2.3.7", + "safe-buffer": "5.1.2", + "sqlstring": "2.3.1" + } + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "node-localstorage": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-localstorage/-/node-localstorage-2.2.1.tgz", + "integrity": "sha512-vv8fJuOUCCvSPjDjBLlMqYMHob4aGjkmrkaE42/mZr0VT+ZAU10jRF8oTnX9+pgU9/vYJ8P7YT3Vd6ajkmzSCw==", + "requires": { + "write-file-atomic": "^1.1.4" + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, + "optimist": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", + "integrity": "sha1-yQlBrVnkJzMokjB00s8ufLxuwNk=", + "requires": { + "wordwrap": "~0.0.2" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "pg-connection-string": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", + "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" + }, + "please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "requires": { + "semver-compare": "^1.0.0" + } + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "promise": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-6.1.0.tgz", + "integrity": "sha1-LOcp9rlLRcJoka0GAsXJDgTG7vY=", + "requires": { + "asap": "~1.0.0" + } + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "retry-as-promised": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-5.0.0.tgz", + "integrity": "sha512-6S+5LvtTl2ggBumk04hBo/4Uf6fRJUwIgunGZ7CYEBCeufGFW1Pu6ucUf/UskHeWOIsUcLOGLFXPig5tR5V1nA==" + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "requires": { + "align-text": "^0.1.1" + } + }, + "robust-predicates": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz", + "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==" + }, + "rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=" + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + } + }, + "sequelize": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.15.0.tgz", + "integrity": "sha512-Ks2jSaKMfICZ8jMlhH401fLw5ikE8Vqt6slcR2peKOn4lA3H+LRfXdlnAl/CUDO1MflFl7PhifnzPxwhamciGQ==", + "requires": { + "@types/debug": "^4.1.7", + "debug": "^4.3.3", + "dottie": "^2.0.2", + "inflection": "^1.13.1", + "lodash": "^4.17.21", + "moment": "^2.29.1", + "moment-timezone": "^0.5.34", + "pg-connection-string": "^2.5.0", + "retry-as-promised": "^5.0.0", + "semver": "^7.3.5", + "sequelize-pool": "^7.1.0", + "toposort-class": "^1.0.1", + "uuid": "^8.3.2", + "validator": "^13.7.0", + "wkx": "^0.5.0" + }, + "dependencies": { + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "sequelize-pool": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz", + "integrity": "sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==" + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "slide": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=" + }, + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "requires": { + "amdefine": ">=0.0.4" + } + }, + "sqlstring": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", + "integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A=" + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "toposort-class": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", + "integrity": "sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg=" + }, + "transformers": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/transformers/-/transformers-2.1.0.tgz", + "integrity": "sha1-XSPLNVYd2F3Gf7hIIwm0fVPM6ac=", + "requires": { + "css": "~1.0.8", + "promise": "~2.0", + "uglify-js": "~2.2.5" + }, + "dependencies": { + "is-promise": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-1.0.1.tgz", + "integrity": "sha1-MVc3YcBX4zwukaq56W2gjO++duU=" + }, + "promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-2.0.0.tgz", + "integrity": "sha1-RmSKqdYFr10ucMMCS/WUNtoCuA4=", + "requires": { + "is-promise": "~1" + } + }, + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "requires": { + "amdefine": ">=0.0.4" + } + }, + "uglify-js": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.2.5.tgz", + "integrity": "sha1-puAqcNg5eSuXgEiLe4sYTAlcmcc=", + "requires": { + "optimist": "~0.3.5", + "source-map": "~0.1.7" + } + } + } + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "requires": { + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "optional": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "validator": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", + "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" + }, + "with": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/with/-/with-4.0.3.tgz", + "integrity": "sha1-7v0VTp550sjTQXtkeo8U2f7M4U4=", + "requires": { + "acorn": "^1.0.1", + "acorn-globals": "^1.0.3" + }, + "dependencies": { + "acorn": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-1.2.2.tgz", + "integrity": "sha1-yM4n3grMdtiW0rH6099YjZ6C8BQ=" + } + } + }, + "wkx": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", + "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", + "requires": { + "@types/node": "*" + } + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" + }, + "write-file-atomic": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz", + "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=", + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "slide": "^1.1.5" + } + }, + "xg-xcharts": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/xg-xcharts/-/xg-xcharts-0.2.2.tgz", + "integrity": "sha1-PpqYF7c7+9D4UQ/PSyCpT3c0yJo=", + "requires": { + "d3": ">=4.1.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + } +} diff --git a/dockers/quotas/html/express_webapp/package.json b/dockers/quotas/html/express_webapp/package.json new file mode 100644 index 0000000..ed2e004 --- /dev/null +++ b/dockers/quotas/html/express_webapp/package.json @@ -0,0 +1,24 @@ +{ + "name": "express-webapp", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "node ./bin/www" + }, + "dependencies": { + "alert": "^5.0.10", + "billboard.js": "^3.3.3", + "chart.js": "^3.7.1", + "cookie-parser": "~1.4.4", + "debug": "~2.6.9", + "express": "~4.16.1", + "http-errors": "~1.6.3", + "jade": "~1.11.0", + "mariadb": "^2.5.5", + "morgan": "~1.9.1", + "mysql": "^2.18.1", + "node-localstorage": "^2.2.1", + "sequelize": "^6.15.0", + "xg-xcharts": "^0.2.2" + } +} diff --git a/dockers/quotas/html/express_webapp/public/images/favicon.ico b/dockers/quotas/html/express_webapp/public/images/favicon.ico new file mode 100644 index 0000000..47de6bf Binary files /dev/null and b/dockers/quotas/html/express_webapp/public/images/favicon.ico differ diff --git a/dockers/quotas/html/express_webapp/public/images/groupe.png b/dockers/quotas/html/express_webapp/public/images/groupe.png new file mode 100644 index 0000000..94602f7 Binary files /dev/null and b/dockers/quotas/html/express_webapp/public/images/groupe.png differ diff --git a/dockers/quotas/html/express_webapp/public/images/individuel.png b/dockers/quotas/html/express_webapp/public/images/individuel.png new file mode 100644 index 0000000..0cb6db3 Binary files /dev/null and b/dockers/quotas/html/express_webapp/public/images/individuel.png differ diff --git a/dockers/quotas/html/express_webapp/public/images/quotas.png b/dockers/quotas/html/express_webapp/public/images/quotas.png new file mode 100644 index 0000000..76f0a28 Binary files /dev/null and b/dockers/quotas/html/express_webapp/public/images/quotas.png differ diff --git a/dockers/quotas/html/express_webapp/public/images/serviceglobal.png b/dockers/quotas/html/express_webapp/public/images/serviceglobal.png new file mode 100644 index 0000000..f9c8118 Binary files /dev/null and b/dockers/quotas/html/express_webapp/public/images/serviceglobal.png differ diff --git a/dockers/quotas/html/express_webapp/public/stylesheets/style.css b/dockers/quotas/html/express_webapp/public/stylesheets/style.css new file mode 100644 index 0000000..9faa177 --- /dev/null +++ b/dockers/quotas/html/express_webapp/public/stylesheets/style.css @@ -0,0 +1,210 @@ +/* +_____________________________________________________________________________________ +Ce fichier CSS est script permettent de modifier l'apparence de nos page web + +utiliser par : -groupes.jade + -index.jade + -indexAdmin.jade + -serviceg.jade + -users.jade + -usersError.jade + -usersfound.jade + +Auteurs : Lauryne GAULTIER, GUIQUERRO Nathaniel +_____________________________________________________________________________________ + +*/ + +html{ + overflow: hidden +} + +body { + background-color: #f2f3f1; + text-align: center; +} + +#entete, #menu, #contenu, #footer { + padding:1px 0; + font-family: "Courier New", monospace; +} + +#entete { + font-family: "Source Sans Pro", Helvetica, sans-serif; + background-color:#eeecec; + color: #585858; +} + + #entete:hover{ + border-bottom-color: transparent; + color: #3dbbf5 !important; + } + +#main { + position: relative; +} + +#menu { + background-color:#eeecec; + float:left; + position : fixed; + top:0; + bottom:0; + width: 30%; +} + +#contenu { + font-family: "Source Sans Pro", Helvetica, sans-serif; + font-size: 125%; + color: #585858; + text-align: center; + position: fixed; + top:20; + left:50%; + overflow: visible; + margin-bottom: 50px; +} + +#gauche{ + position: fixed; + left: 35%; +} + +#droite{ + position: fixed; + left: 65%; +} + +#footer { + font-family: "Source Sans Pro", Helvetica, sans-serif; + background-color:#eeecec; + margin-top :5%; + position: relative; + bottom: 0; + left: 0; + right: 0; + top: 506px; + color: #585858; + text-align:center; + clear:both; +} + +button { + display: inline-block; + background-color: #585858; + border-radius: 10px; + border: 4px double #cccccc; + color: #eeeeee; + text-align: center; + font-size: 28px; + padding: 20px; + width: 500px; + transition: all 0.5s; + cursor: pointer; + margin: 15px; +} + + button span { + cursor: pointer; + display: inline-block; + position: relative; + transition: 0.5s; + } + + button span:after { + content: '\00bb'; + position: absolute; + opacity: 0; + top: 0; + right: -20px; + transition: 0.5s; + } + + button:hover { + background-color: #3dbbf5 + } + + button:hover span { + padding-right: 25px; + } + + button:hover span:after { + opacity: 1; + right: 0; + } + +#textAR{ + margin-left:10%; +} + +#valide, + #valide::after { + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + -o-transition: all 0.3s; + transition: all 0.3s; + } + + #valide{ + background: none; + border: 4px solid rgb(53, 53, 53); + border-radius: 10px; + color: rgb(53, 53, 53); + display: block; + font-size: 0.5em; + font-weight: bold; + margin: 10px auto; + padding: 2em 6em; + text-transform: uppercase; + } + + #valide::before, + #valide::after { + background: rgb(53, 53, 53); + content: ''; + position: absolute; + z-index: -1; + } + + #valide:hover { + color: #3dbbf5 + } + + #valide::after { + height: 0; + left: 0; + top: 0; + width: 100%; + } + + #valide::after { + height: 0; + left: 50%; + top: 50%; + width: 0; + } + + #valide:hover:after { + height: 100%; + left: 0; + top: 0; + width: 100%; + } + + #champ.textbox[type=text] { + width: 60%; + border-radius:20px; + padding: 12px 20px; + text-align: center; + font-family: Moderne Sans, sans-serif; + color: #1a1919; + font-size: 2vmin; + box-sizing: border-box; + border: 3px solid rgb(104, 103, 103); + transition: 0.5s; + outline: none; + } + + #champ.textbox[type=text]:focus { + border: 3px solid rgb(37, 37, 37); + } diff --git a/dockers/quotas/html/express_webapp/routes/groupes.js b/dockers/quotas/html/express_webapp/routes/groupes.js new file mode 100644 index 0000000..064d83d --- /dev/null +++ b/dockers/quotas/html/express_webapp/routes/groupes.js @@ -0,0 +1,16 @@ +var express = require('express'); +var router = express.Router(); + +//_________________________________________________________________ +// script route pour la page groupe.jade +// +// Auteur : Nathaniel GUIQUERRO +//_________________________________________________________________ + +/* GET users listing. */ +router.get('/', function(req, res, next) { + console.log("J'envoie le render de la page Groupe"); + res.render('groupes'); +}); + +module.exports = router; diff --git a/dockers/quotas/html/express_webapp/routes/index.js b/dockers/quotas/html/express_webapp/routes/index.js new file mode 100644 index 0000000..4fd4e97 --- /dev/null +++ b/dockers/quotas/html/express_webapp/routes/index.js @@ -0,0 +1,15 @@ +var express = require('express'); +var router = express.Router(); + +//_________________________________________________________________ +// script route pour la page index.jade +// +// Auteur : Nathaniel GUIQUERRO +//_________________________________________________________________ + +/* GET home page. */ +router.get('/', function(req, res, next) { + res.render('index', { title: 'Express' }); +}); + +module.exports = router; diff --git a/dockers/quotas/html/express_webapp/routes/indexAdmin.js b/dockers/quotas/html/express_webapp/routes/indexAdmin.js new file mode 100644 index 0000000..3130203 --- /dev/null +++ b/dockers/quotas/html/express_webapp/routes/indexAdmin.js @@ -0,0 +1,15 @@ +var express = require('express'); +var router = express.Router(); + +//_________________________________________________________________ +// script route pour la page indexAdmin.jade +// +// Auteur : Nathaniel GUIQUERRO +//_________________________________________________________________ + +/* GET home page. */ +router.get('/', function(req, res, next) { + res.render('indexAdmin', { title: 'Express' }); +}); + +module.exports = router; diff --git a/dockers/quotas/html/express_webapp/routes/serviceg.js b/dockers/quotas/html/express_webapp/routes/serviceg.js new file mode 100644 index 0000000..9de700c --- /dev/null +++ b/dockers/quotas/html/express_webapp/routes/serviceg.js @@ -0,0 +1,68 @@ +var express = require('express'); +var router = express.Router(); +const {Sequelize} = require("sequelize"); +//___________________________________________________________________________________ +// Ce petit script permet de connaitre la taille des services de kaz +// -Nextcloud +// -Postfix +// -Agora +//! +// le script cherche dans la base mariaDB les differents données qui correspond à chaque +// services +// +// Auteur : Nathaniel Guiquerro Lauryne Gaultier +// Retourne : Une page serviceg.jade qui permet de afficher les tailles globales des +// services kaz +// Exemple : Taille Postfix : 50 Mio +// Taille Nextcloud : 50 Mio +// Taille Agora : 10 Mio +//___________________________________________________________________________________ +function formatBytes(bytes, decimals = 2) { + if (bytes === 0) return '0 Bytes'; + + const k = 1024; + const dm = decimals < 0 ? 0 : decimals; + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + + const i = Math.floor(Math.log(bytes) / Math.log(k)); + + return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; +} + +router.get('/', function(req, res, next) { + console.log("Demande de tailles aux scriptes de services"); + + //__________________________________________________ + // + // Services Global de kaz + // concu de : Postfix, Nextcloud, Agora + // + //__________________________________________________ + + // XXX Felix + // const sequelize = new Sequelize("quotas","root","tartelette",{ + // host: "quotasDB", + // dialect:"mariadb" + // }); + + (async ()=>{ + try{ + await sequelize.authenticate(); + //connecté + const query = "SELECT * FROM Global Order by date desc"; + sequelize.query(query).then(([results, metadata]) => { + var R = {"Agora: ":formatBytes(results[0].agora), + "Postfix: ":formatBytes(results[0].postfix), + "NextCloud: ":formatBytes(results[0].nextcloud), + "Total: ":formatBytes(results[0].total)}; + console.log(R); + res.render('serviceg',{data:R}); + }) + } catch (error){ + //pas connecté + console.log(error); + } + })(); +}); + +module.exports = router; diff --git a/dockers/quotas/html/express_webapp/routes/users.js b/dockers/quotas/html/express_webapp/routes/users.js new file mode 100644 index 0000000..a442233 --- /dev/null +++ b/dockers/quotas/html/express_webapp/routes/users.js @@ -0,0 +1,209 @@ + +var express = require('express'); +var router = express.Router(); +const {Sequelize} = require("sequelize"); + +//___________________________________________________________________________________ +// Ce petit script permet de connaitre la taille memoire d'un utilisateur + +// Auteur : Nathaniel Guiquerro Lauryne Gaultier +// +// GET : Quand on charge la page on tombe sur un formulaire +// il faut alors renseigner le email et un nombre entre 1 et 30 +// ex : contact1@kaz.local et 25 +// POST: SI l'email existe ou le nombre n'est pas correcte alors on va rechercher si +// il existe les quotas avec le script ../../quotas/UtilisateurTailleGlobal.sh +// SINON on renvoie l'erreur du script ../../quotas/UtilisateurTailleGlobal.sh +//___________________________________________________________________________________ + + +//fonction de convertiseur d'uniter automatique +function formatBytes(bytes, decimals = 2) { + if (bytes === 0) return '0 Bytes'; + + const k = 1024; + const dm = decimals < 0 ? 0 : decimals; + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + + const i = Math.floor(Math.log(bytes) / Math.log(k)); + + return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; +} + +//fonction qui converti la donnée en paramètre +// bytes -----> Mb +function getMB(bytes){ + return bytes/1000000; +} + +async function getData(email , nb ){ + var tabDeDonnee = []; + + // XXX Felix + // const sequelize = new Sequelize("quotas","root","tartelette",{ + // host: "quotasDB", + // dialect:"mariadb" + // }); + + try{ + await sequelize.authenticate(); + //connecté + const query ="SELECT * FROM Utilisateur WHERE utilisateur='"+email+"' ORDER BY date desc ;"; + sequelize.query(query).then(([results, metadata]) => { + + var i=0; + while(i { + var i=0; + while(i30) res.render('usersError'); + + // XXX Felix + // const sequelize = new Sequelize("quotas","root","tartelette",{ + // host: "quotasDB", + // dialect:"mariadb" + // }); + + (async ()=>{ + try{ + await sequelize.authenticate(); + var dat = await getData(email,nombreJ); + var lim = await getLimit(email,nombreJ); + + setTimeout(function(){ + console.log("Ready") + + var tabDonne = splitDonne(dat); + var tabTime = splitTime(dat); + + + //connecté + const query ="SELECT * FROM Utilisateur WHERE utilisateur='"+email+"' ORDER BY date desc LIMIT 1;"; + sequelize.query(query).then(([results, metadata]) => { + + var i=0; + var trouve =false; + while(i{ + try{ + await sequelize.authenticate(); + //connecté + const query ="SELECT * FROM Utilisateur ORDER BY date desc LIMIT 1;"; + sequelize.query(query).then(([results, metadata]) => { + var i=0; + var trouve =false; + while(i + title KAZ Quotas + meta(charset='UTF-8') + body + #entete + h1(onclick="window.location.href = 'https://kaz.bzh/'") LE NUMERIQUE ETHIQUE ET LOCAL + h2 Bienvenue + p Ci-dessous se trouve les différente catégorie de quotas + #main + #menu + img(id="object-position-1" src='/images/quotas.png' width='100%') + #contenu + button(onclick="window.location.href = '/users';") Individuels + #footer + p Site réalisé par les étudiants du département informatique sous la direction de F. Merciol & N. Le Sommer et G. Kerbellec durant l'année 2021-2022 diff --git a/dockers/quotas/html/express_webapp/views/indexAdmin.jade b/dockers/quotas/html/express_webapp/views/indexAdmin.jade new file mode 100644 index 0000000..52467e7 --- /dev/null +++ b/dockers/quotas/html/express_webapp/views/indexAdmin.jade @@ -0,0 +1,39 @@ + +//- + _________________________________________________________________ + Page jade correspondant à l'affichage à la page principale + qui regroupe plusieurs boutons pour pouvoir accéder aux différentes + pages des quotas + + # Auteur : Lauryne GAULTIER + _________________________________________________________________ + +doctype html +html + head + link(href='/stylesheets/style.css' rel='stylesheet' type='text/css') + + title KAZ Quotas ADMIN + meta(charset='UTF-8') + body + #entete + h1(onclick="window.location.href = 'https://kaz.bzh/'") LE NUMERIQUE ETHIQUE ET LOCAL + p Ci-dessous se trouve les différente catégorie de quotas + p Vous Vous trouvez dans la partie ADMINISTRATEUR du service + #main + #menu + img(id="object-position-1" src='/images/quotas.png' width='100%') + #contenu + br + br + button(onclick="window.location.href = '/admin/serviceg';") Service Global + br + button(onclick="window.location.href = '/admin/users';") Individuels + br + button(onclick="window.location.href = '/admin/groupes';") Groupes + br + br + button(onclick="window.location.href = '/';") Menu Kaznaute + br + #footer + p Site réalisé par les étudiants du département informatique sous la direction de F. Merciol & N. Le Sommer et G. Kerbellec durant l'année 2021-2022 diff --git a/dockers/quotas/html/express_webapp/views/layout.jade b/dockers/quotas/html/express_webapp/views/layout.jade new file mode 100644 index 0000000..15af079 --- /dev/null +++ b/dockers/quotas/html/express_webapp/views/layout.jade @@ -0,0 +1,7 @@ +doctype html +html + head + title= title + link(rel='stylesheet', href='/stylesheets/style.css') + body + block content diff --git a/dockers/quotas/html/express_webapp/views/serviceg.jade b/dockers/quotas/html/express_webapp/views/serviceg.jade new file mode 100644 index 0000000..a1a68a8 --- /dev/null +++ b/dockers/quotas/html/express_webapp/views/serviceg.jade @@ -0,0 +1,34 @@ +doctype html +//- + _________________________________________________________________ + Page jade correspondant à l'affichage des quotas des differents + service qu'offre kaz comme : + -Nexcloud + -Postfix + -Agora + + Auteur : Lauryne GAULTIER + _________________________________________________________________ + +html + head + link(href='/stylesheets/style.css' rel='stylesheet' type='text/css') + title KAZ Quotas Global + meta(charset='UTF-8') + body + #entete + h1(onclick="window.location.href = 'https://kaz.bzh/'") Tableau de bord Quotas Global + p voici les différent service de KAZ avec leur poids respectif + #main + #menu + img(src='/images/serviceglobal.png' width='100%') + #contenu + each val,index in data + tr + br + td= index+ ' ' + val + + #footer + button(onclick="window.location.href = '/admin';") Retour + br + p Site réalisé par les étudiants du département informatique sous la direction de F. Merciol & N. Le Sommer et G. Kerbellec durant l'année 2021-2022 diff --git a/dockers/quotas/html/express_webapp/views/users.jade b/dockers/quotas/html/express_webapp/views/users.jade new file mode 100644 index 0000000..d5e4f50 --- /dev/null +++ b/dockers/quotas/html/express_webapp/views/users.jade @@ -0,0 +1,41 @@ +doctype html +//- + _________________________________________________________________ + Page jade correspondant à l'affichage des quotas d'un utilisateur + la page comporte un champ de texte où l'on peut entrer l'email + d'un utilisateur pour regarder les quotas qu'il utilise on peu + aussi entrer un nombre entre 1 et 30 pour pouvoir choisir le nombre + de jour afficher dans le graphe + + Auteurs : Lauryne GAULTIER, GUIQUERRO Nathaniel + _________________________________________________________________ + +html + head + link(href='/stylesheets/style.css' rel='stylesheet' type='text/css') + title KAZ Quotas Individuels + meta(charset='UTF-8') + body + #entete + h1(onclick="window.location.href = 'https://kaz.bzh/'") Tableau de bord Quotas Individuels + p(id="textAR") rechercher l'email d'un utilisateur pour savoir combien de place il utilise en total dans chaque service + #main + #menu + img(src='/images/individuel.png' width='100%') + #contenu + form(action='/users' method='POST') + label(for='email') Recherche : + br + input#champ.textbox(type='email' name='email' required='' placeholder="Email utilisateur") + br + label(for='nombreJ') Nombre de Jours à afficher : + br + input#champ.textbox(type='number' name='nombreJ' required='' placeholder="1") + br + p le nombre de jours doivent être entre 1 et 30 + br + input.myButton(id="valide" type='submit' value='Valider') + #footer + button(onclick="window.location.href = '/';") Retour + br + p Site réalisé par les étudiants du département informatique sous la direction de F. Merciol & N. Le Sommer et G. Kerbellec durant l'année 2021-2022 diff --git a/dockers/quotas/html/express_webapp/views/usersError.jade b/dockers/quotas/html/express_webapp/views/usersError.jade new file mode 100644 index 0000000..80ae1a8 --- /dev/null +++ b/dockers/quotas/html/express_webapp/views/usersError.jade @@ -0,0 +1,40 @@ +doctype html +//- + _________________________________________________________________ + Page jade correspondant à l'affichage des quatas d'un utilisateur + la page comporte un champs de texte ou l'on peut entrer l'email + d'un utilisateur pour regarder le quotas qu'il utilise + + Auteur : Lauryne GAULTIER + _________________________________________________________________ + +html + head + link(href='/stylesheets/style.css' rel='stylesheet' type='text/css') + title KAZ Quotas Individuels + meta(charset='UTF-8') + body + #entete + h1(onclick="window.location.href = 'https://kaz.bzh/'") Tableau de bord Quotas Individuels + p rechercher l'email d'un utilisateur pour savoir combien de place il utilise en total dans chaque service + #main + #menu + img(src='/images/individuel.png' width='100%') + #contenu + p L'utilisateur saisie précedamment n'est pas correct ou le nombre de jours n'est pas correcte + form(action='/users' method='POST') + label(for='email') Email: + br + input#email.textbox(type='email' name='email' required='') + br + label(for='nombreJ') Nombre de Jours à afficher : + br + input#champ.textbox(type='number' name='nombreJ' required='' placeholder="1") + br + p le nombre de jours doivent être entre 1 et 30 + br + input.myButton(id="valide" type='submit' value='Valider') + #footer + button(onclick="window.location.href = '/';") Retour + br + p Site réalisé par les étudiants du département informatique sous la direction de F. Merciol & N. Le Sommer et G. Kerbellec durant l'année 2021-2022 diff --git a/dockers/quotas/html/express_webapp/views/usersfound.jade b/dockers/quotas/html/express_webapp/views/usersfound.jade new file mode 100644 index 0000000..d8396bb --- /dev/null +++ b/dockers/quotas/html/express_webapp/views/usersfound.jade @@ -0,0 +1,110 @@ +doctype html +//- + _________________________________________________________________ + Page jade correspondant au résultat de la recherche de + l'utilisateur, renvoie les quotas actuellement utiliser par + l'utilisateur via un graphe en forme de camembert où on peut + trouver la taille des mails, du cloud, mais aussi l'espace libre qui + reste souvent la limitation de l'utilisateur + + il y a aussi un autre graphe qui permet de voir (suit le nombre + de jour choisi a la page précédente) le total de place utiliser par + rapport a la limite des jours précédents. + + Auteurs : Lauryne GAULTIER, GUIQUERRO Nathaniel + _________________________________________________________________ + +html + head(ng-app='myApp' ng-controller='myCtrl') + link(href='/stylesheets/style.css' rel='stylesheet' type='text/css') + title KAZ Quotas Individuels + meta(charset='UTF-8') + #entete + h1(onclick="window.location.href = 'https://kaz.bzh/'") Résultat de la recherche pour + h1 #{data.nom} + #main + #menu + img(src='/images/individuel.png' width='100%') + #contenu + #gauche + p(style='display:none') #{data.dataChart} + p(style='display:none') #{data.labels} + p(style='display:none') #{data.Lim} + canvas#myChart(display= 'inline-block' width='500' height='600') + script(src='https://cdn.jsdelivr.net/npm/chart.js') + script. + var dataC = (document.getElementsByTagName('p')[0].innerHTML).split(','); + var lC = (document.getElementsByTagName('p')[1].innerHTML).split(','); + var limC = (document.getElementsByTagName('p')[2].innerHTML).split(','); + + + + var ctx = document.getElementById('myChart').getContext('2d'); + var myChart = new Chart(ctx, { + type: 'bar', + data: { + datasets: [{ + label: 'Unité de mesure :MB', + data: dataC, + backgroundColor: [ + 'rgba(242, 132, 158, 0.2)' + ], + borderColor: [ + 'rgba(242, 132, 158, 1)' + ], + borderWidth: 1 + }, { + label: 'Limite quotas', + data: limC, + type: 'line', + borderColor: 'black' + }], + labels: lC + } + }); + #droite + p(style='display:none') #{data.Postfix} + p(style='display:none') #{data.NextCloud} + p Votre Quota actuel #{data.Dateduprélevement} + canvas#myChart2(display='inline-block' height='300') + script. + var dPost = (document.getElementsByTagName('p')[3].innerHTML).split(','); + var dNext = (document.getElementsByTagName('p')[4].innerHTML).split(','); + var dDate = (document.getElementsByTagName('p')[5].innerHTML).split(','); + var dLabel = ('Votre Quota actuel' + dDate); + + pPost = ((dPost*100)/limC[limC.length - 1]); + pNetf = ((dNext*100)/limC[limC.length - 1]); + var pLibre = (100-(pPost+pNetf)); + + var tableauDonne = [pPost,pNetf,pLibre]; + + + var ctxy = document.getElementById('myChart2').getContext('2d'); + var myCharty = new Chart(ctxy, { + type: 'pie', + data: { + datasets: [{ + label: dLabel, + data: tableauDonne, + backgroundColor: [ + 'rgba(255, 99, 132, 0.2)', + 'rgba(54, 162, 235, 0.2)', + 'rgba(255, 206, 86, 0.2)' + ], + borderColor: [ + 'rgba(255, 99, 132, 1)', + 'rgba(54, 162, 235, 1)', + 'rgba(255, 206, 86, 1)' + ], + borderWidth: 1, + hoverOffset: 4 + }], + labels: ["Mail","NextCloud","Espace Libre"] + } + }); + #footer + button(onclick="window.location.href = '/users';") Retour + br + p Site réalisé par les étudiants du département informatique sous la direction de F. Merciol & N. Le Sommer et G. Kerbellec durant l'année 2021-2022 + diff --git a/dockers/quotas/html/groupes.html b/dockers/quotas/html/groupes.html new file mode 100644 index 0000000..6cd5d46 --- /dev/null +++ b/dockers/quotas/html/groupes.html @@ -0,0 +1,32 @@ + + + + + + +KAZ : Aperçu + + + +
+

Tableau de bord Quotas

+

Ci-dessous se trouve .........

+
+ +
+ + +
+
+
+
+
+
+ + + + \ No newline at end of file diff --git a/dockers/quotas/html/images/favicon.ico b/dockers/quotas/html/images/favicon.ico new file mode 100644 index 0000000..47de6bf Binary files /dev/null and b/dockers/quotas/html/images/favicon.ico differ diff --git a/dockers/quotas/html/images/groupe.png b/dockers/quotas/html/images/groupe.png new file mode 100644 index 0000000..94602f7 Binary files /dev/null and b/dockers/quotas/html/images/groupe.png differ diff --git a/dockers/quotas/html/images/individuel.png b/dockers/quotas/html/images/individuel.png new file mode 100644 index 0000000..0cb6db3 Binary files /dev/null and b/dockers/quotas/html/images/individuel.png differ diff --git a/dockers/quotas/html/images/quotas.png b/dockers/quotas/html/images/quotas.png new file mode 100644 index 0000000..76f0a28 Binary files /dev/null and b/dockers/quotas/html/images/quotas.png differ diff --git a/dockers/quotas/html/images/serviceglobal.png b/dockers/quotas/html/images/serviceglobal.png new file mode 100644 index 0000000..f9c8118 Binary files /dev/null and b/dockers/quotas/html/images/serviceglobal.png differ diff --git a/dockers/quotas/html/index.html b/dockers/quotas/html/index.html new file mode 100644 index 0000000..011a135 --- /dev/null +++ b/dockers/quotas/html/index.html @@ -0,0 +1,35 @@ + + + + + + +KAZ : Aperçu + + + +
+

Le numérique éthique et local

+

Ci-dessous se trouve .........

+
+ +
+ + +
+
+ +
+ +
+
+
+ + + + + diff --git a/dockers/quotas/html/individuels.html b/dockers/quotas/html/individuels.html new file mode 100644 index 0000000..87fbf8e --- /dev/null +++ b/dockers/quotas/html/individuels.html @@ -0,0 +1,32 @@ + + + + + + +KAZ : Aperçu + + + +
+

Tableau de bord Quotas

+

Ci-dessous se trouve .........

+
+ +
+ + +
+
+
+
+
+
+ + + + \ No newline at end of file diff --git a/dockers/quotas/html/kaz.quota/package-lock.json b/dockers/quotas/html/kaz.quota/package-lock.json new file mode 100644 index 0000000..dc13862 --- /dev/null +++ b/dockers/quotas/html/kaz.quota/package-lock.json @@ -0,0 +1,167 @@ +{ + "name": "kaz.quota", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "express-generator": "^4.16.1" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/ejs": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz", + "integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/express-generator": { + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/express-generator/-/express-generator-4.16.1.tgz", + "integrity": "sha512-tWYEx5Y/Llos2qC6yAETmdqEMEPqNUzJ8btGcSZ2zSr8RYOalzffhvh9zx5OQTctvOgJ9kKYxyvFGAIuUuF/wA==", + "dependencies": { + "commander": "2.15.1", + "ejs": "2.6.1", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "sorted-object": "2.0.1" + }, + "bin": { + "express": "bin/express-cli.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "node_modules/mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", + "dependencies": { + "minimist": "0.0.8" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/sorted-object": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/sorted-object/-/sorted-object-2.0.1.tgz", + "integrity": "sha1-fWMfS9OnmKJK8d/8+/6DM3pd9fw=" + } + }, + "dependencies": { + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "ejs": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz", + "integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==" + }, + "express-generator": { + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/express-generator/-/express-generator-4.16.1.tgz", + "integrity": "sha512-tWYEx5Y/Llos2qC6yAETmdqEMEPqNUzJ8btGcSZ2zSr8RYOalzffhvh9zx5OQTctvOgJ9kKYxyvFGAIuUuF/wA==", + "requires": { + "commander": "2.15.1", + "ejs": "2.6.1", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "sorted-object": "2.0.1" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "sorted-object": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/sorted-object/-/sorted-object-2.0.1.tgz", + "integrity": "sha1-fWMfS9OnmKJK8d/8+/6DM3pd9fw=" + } + } +} diff --git a/dockers/quotas/html/kaz.quota/package.json b/dockers/quotas/html/kaz.quota/package.json new file mode 100644 index 0000000..8f283c8 --- /dev/null +++ b/dockers/quotas/html/kaz.quota/package.json @@ -0,0 +1,14 @@ +{ + "name": "kaz.quota", + "version": "1.0.0", + "description": "This is used to print quota on a HTML page", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "GUIQUERRO Nathaniel & GAULTIER Lauryne", + "license": "ISC", + "dependencies": { + "express-generator": "^4.16.1" + } +} diff --git a/dockers/quotas/html/package-lock.json b/dockers/quotas/html/package-lock.json new file mode 100644 index 0000000..9734ff2 --- /dev/null +++ b/dockers/quotas/html/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "html", + "lockfileVersion": 2, + "requires": true, + "packages": {} +} diff --git a/dockers/quotas/html/package.json b/dockers/quotas/html/package.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/dockers/quotas/html/package.json @@ -0,0 +1 @@ +{} diff --git a/dockers/quotas/html/serviceG.html b/dockers/quotas/html/serviceG.html new file mode 100644 index 0000000..7aad059 --- /dev/null +++ b/dockers/quotas/html/serviceG.html @@ -0,0 +1,33 @@ + + + + + + +KAZ : Aperçu + + + +
+

Tableau de bord Quotas

+

Ci-dessous se trouve .........

+
+ +
+ + +
+
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/dockers/quotas/html/style.css b/dockers/quotas/html/style.css new file mode 100644 index 0000000..d10a209 --- /dev/null +++ b/dockers/quotas/html/style.css @@ -0,0 +1,74 @@ + + +body { + background-color: #f2f3f1; + text-align: center; +} + +#entete, #menu, #contenu, #footer { + padding:1px 0; + } + #entete { + background-color:#FF9900; + text-align:center; + } + #main { + max-with:960px; + margin:auto; + } + #menu { + float:left; + width:240px; + background-color:#FF3366; + } + #contenu { + margin-left:245px; + position: absolute; + top: 50%; + left: 40%; + transform: translate(-50%, -50%); + } + #footer { + background-color:#669933; + text-align:center; + clear:both; + } + + button { + display: inline-block; + background-color: #585858; + border-radius: 10px; + border: 4px double #cccccc; + color: #eeeeee; + text-align: center; + font-size: 28px; + padding: 20px; + width: 500px; + transition: all 0.5s; + cursor: pointer; + margin: 15px; + } + button span { + cursor: pointer; + display: inline-block; + position: relative; + transition: 0.5s; + } + button span:after { + content: '\00bb'; + position: absolute; + opacity: 0; + top: 0; + right: -20px; + transition: 0.5s; + } + button:hover { + background-color: #f2849e + } + button:hover span { + padding-right: 25px; + } + button:hover span:after { + opacity: 1; + right: 0; + } diff --git a/dockers/quotas/initdb.d/quotas.sql b/dockers/quotas/initdb.d/quotas.sql new file mode 100644 index 0000000..058dac5 --- /dev/null +++ b/dockers/quotas/initdb.d/quotas.sql @@ -0,0 +1,28 @@ +CREATE DATABASE IF NOT EXISTS quotas; +USE quotas; + +CREATE TABLE IF NOT EXISTS Global ( + date TIMESTAMP PRIMARY KEY, + total BIGINT, + agora BIGINT, + postfix BIGINT, + nextcloud BIGINT +); + +CREATE TABLE IF NOT EXISTS Utilisateur ( + date TIMESTAMP, + utilisateur VARCHAR(50) , + limite INT, + total BIGINT, + postfix BIGINT, + nextcloud BIGINT +); + +CREATE TABLE IF NOT EXISTS Associations ( + date TIMESTAMP PRIMARY KEY, + associations VARCHAR(50), + total BIGINT, + agora BIGINT, + postfix BIGINT, + nextcloud BIGINT +); diff --git a/dockers/roundcube/.env b/dockers/roundcube/.env new file mode 120000 index 0000000..406acd1 --- /dev/null +++ b/dockers/roundcube/.env @@ -0,0 +1 @@ +../../config/dockers.env \ No newline at end of file diff --git a/dockers/roundcube/Readme.txt b/dockers/roundcube/Readme.txt new file mode 100644 index 0000000..35681f5 --- /dev/null +++ b/dockers/roundcube/Readme.txt @@ -0,0 +1,50 @@ +dans config.inc.php je rajoute: + +#add by FAB just for testing +#pour lire les mails et en envoyer: on désactive la vérification des pairs +#est-ce dangereux ? je ne pense car on reste tout est sur la mêmce machine (roundcube/postfix/dovecot) +$config['smtp_conn_options'] = array( +'ssl' => array( +'verify_peer' => false, +'verify_peer_name' => false, +), +); + +$config['imap_conn_options'] = array( +'ssl' => array( +'verify_peer' => false, +'verify_peer_name' => false, +), +); + +#didier le 2 aout 2021 + +docker exec -it roundcubeServ composer.phar require roundcube/carddav --update-no-dev +a faire qu' une fois puisque puisque pour ajouter le plugins carddav, la commande ce dessus installe le plugin dans le volume monté dans le docker-compose.yml +la commande l' active aussi dans roundcube + + +TODO: +* mettre mariadb à la place de sqllite +--> fait +* maj du dcker-compose à la sauce KAZ avec docker.env +--> fait +* redémarrer avec le proxy ngnix +--> fait + +QUESTION: +******** +* a quoi sert le fichier Dockerfile avec EXPOSE 9002 ? +* pourquoi j'ai du virer le 9902 de proxy_pass http://webmail.__DOMAIN__/; +dans le fichier nginx.tmpl.conf alors que le port est indiqué pour le pad: + proxy_pass http://pad.__DOMAIN__:9001/; +* pourquoi j'ai "compose: up -d roundcube WARNING: Some networks were defined but are not used by any service: mailNet" ? +* pourquoi reste t-il des volumes de type 2874d6aa7b462b37783bef5e00628a077602af9678e852ddaf9663041fecbf5e + dans /var/lib/docker/volumes ? + + +Pour virer tous les volumes dangling: +# docker volume rm `docker volume ls -q -f dangling=true` + +i# comme nous sommes sur la machine de dev, il faut saisir l' ip du postfixServ en smtp.dev.kaz.bzh en ce moment +c' est 172.24.0.2 diff --git a/dockers/roundcube/config/add-to-config.inc.php b/dockers/roundcube/config/add-to-config.inc.php new file mode 100644 index 0000000..b1798cc --- /dev/null +++ b/dockers/roundcube/config/add-to-config.inc.php @@ -0,0 +1,14 @@ +# KAZ custom +$config['imap_conn_options'] = array( + 'ssl' => array( + 'verify_peer' => false, + 'verfify_peer_name' => false, + ), +); +$config['smtp_conn_options'] = array( + 'ssl' => array( + 'verify_peer' => false, + 'verify_peer_name' => false, + ), +); +include 'custom_config.inc.php'; diff --git a/dockers/roundcube/config/custom_config.inc.php b/dockers/roundcube/config/custom_config.inc.php new file mode 100644 index 0000000..283db33 --- /dev/null +++ b/dockers/roundcube/config/custom_config.inc.php @@ -0,0 +1,4 @@ + /dev/null; then + printKazMsg "\n *** Premier lancement de roundcube" + + cat "config/add-to-config.inc.php" >> "${CONF_DIR}/config.inc.php" + cp "config/custom_config.inc.php" "${CONF_DIR}/" + cp "config/kazmel.png" "${IMG_DIR}/" +fi diff --git a/dockers/sympa/.env b/dockers/sympa/.env new file mode 120000 index 0000000..406acd1 --- /dev/null +++ b/dockers/sympa/.env @@ -0,0 +1 @@ +../../config/dockers.env \ No newline at end of file diff --git a/dockers/sympa/Dockerfile b/dockers/sympa/Dockerfile new file mode 100644 index 0000000..4485cbd --- /dev/null +++ b/dockers/sympa/Dockerfile @@ -0,0 +1,127 @@ +# Didier le 28 avril 2021 +# conf postfix ok +# sympa ne fonctionne pas en auto, lancer le contenu de la dernire ligne dans le container +# le fichier preseed contient l' installation en mysql, mais il faudra revenir dessus en lancant sympa_wizard +# ne pas oublier de verifier la fin du fichier sympa.conf qui doit contenir les lignes contenu dans le sympa.sh + +# Fanch janvier 2022 +# migration en bullseye, tentative de rendre générique +FROM debian:bullseye + +######################################## +# APT local cache +# work around because COPY failed if no source file +COPY .dummy .apt-mirror-confi[g] .proxy-confi[g] / +RUN cp /.proxy-config /etc/profile.d/proxy.sh 2> /dev/null || true +RUN if [ -f /.apt-mirror-config ] ; then . /.apt-mirror-config && sed -i \ + -e "s%s\?://deb.debian.org%://${APT_MIRROR_DEBIAN}%g" \ + -e "s%s\?://security.debian.org%://${APT_MIRROR_DEBIAN_SECURITY}%g" \ + -e "s%s\?://archive.ubuntu.com%://${APT_MIRROR_UBUNTU}%g" \ + -e "s%s\?://security.ubuntu.com%://${APT_MIRROR_UBUNTU_SECURITY}%g" \ + /etc/apt/sources.list; fi + +######################################## +RUN apt-get update \ + && apt-get install -y locales locales-all \ + && sed -i '/fr_FR.UTF-8/s/^# //g' /etc/locale.gen && locale-gen +ENV LC_ALL fr_FR.UTF-8 +ENV LANG fr_FR.UTF-8 +ENV LANGUAGE fr_FR:fr +RUN update-locale LANG=fr_FR.UTF-8 \ + && echo sympa >/etc/hostname + +RUN apt-get -y install libboost-program-options-dev libboost-system-dev libboost-filesystem-dev libcurl4-gnutls-dev +#RUN apt-get -y install emacs elpa-php-mode +RUN apt-get -y install procps dos2unix + +# creation du user filter,son repertoire home, copie des fichiers +RUN mkdir /home/filter ; useradd -d /home/filter filter ; chown filter /home/filter +RUN apt-get install -y --fix-missing doxygen dos2unix git \ + build-essential make g++ libboost-program-options-dev libboost-system-dev libboost-filesystem-dev libcurl4-gnutls-dev libssl-dev +WORKDIR /home/ +RUN git clone https://git.kaz.bzh/KAZ/depollueur.git +WORKDIR /home/depollueur/ +RUN make +RUN cp build/out/* /home/filter/ +RUN cp src/bash/* /home/filter/ + +RUN chown filter /home/filter/*; chmod 755 /home/filter/* + +# creation du repertoire filter et application des bons droits pour le filtre +RUN mkdir -p /var/log/mail; touch /var/log/mail/filter.log ; chown filter /var/log/mail/filter.log ; chmod 777 /var/log/mail/filter.log +RUN mkdir -p /var/spool/filter ; chmod 775 /var/spool/filter ; chown filter /var/spool/filter +RUN mkdir -p /var/log/mail/pb ; chmod a+rwx /var/log/mail/pb + +COPY dockers/sympa/config/sympa.preseed /tmp/sympa.preseed +RUN debconf-set-selections < /tmp/sympa.preseed \ + && apt-get update \ + && DEBIAN_FRONTEND=noninteractive apt-get -y install --no-install-recommends sympa apache2 postfix supervisor anacron spawn-fcgi libapache2-mod-fcgid apg curl \ + && a2enmod rewrite ssl proxy_fcgi \ + && a2ensite default-ssl \ + && a2enconf sympa-soap \ + \ + && sed \ + -e 's%SSLCertificateKeyFile\s.*$%SSLCertificateKeyFile ENVKEY%g' \ + -e 's%SSLCertificateFile\s.*$%SSLCertificateFile ENVCERT%g' \ + -i /etc/apache2/sites-available/default-ssl.conf \ + && sed \ + -e '/ServerName/a Redirect / https://MAILNAME.DOMAINNAME/' \ + -i /etc/apache2/sites-available/000-default.conf \ + && echo '\n \n\n' > /var/www/html/index.html \ + && sed -e 's%ScriptAlias.*%ProxyPass "/wws" "fcgi://localhost:8442/"%' -i /etc/apache2/conf-enabled/sympa.conf \ + && sed -e 's%ScriptAlias.*%ProxyPass "/sympasoap" "fcgi://localhost:8443/"%' -i /etc/apache2/conf-enabled/sympa-soap.conf + +# pour le confort : modif du .bashrc de root +RUN sed -i 's/# alias/alias/g' /root/.bashrc \ + && sed -i 's/# export/export/g' /root/.bashrc \ + && sed -i 's/# eval/eval/g' /root/.bashrc + +COPY dockers/sympa/config/supervisord.conf /etc/supervisord.conf + +RUN touch /var/log/sympa.log \ + && chmod 640 /var/log/sympa.log \ + && echo "local1.* -/var/log/sympa.log" >>/etc/rsyslog.conf + +# configuration de postfix +COPY dockers/sympa/config/transport /etc/postfix/ +COPY dockers/sympa/config/postfix.sh /tmp +RUN bash /tmp/postfix.sh + +# # modif des fichiers de postfix pour filter +RUN cat /home/filter/master.cf.update >>/etc/postfix/master.cf \ + && sed -i 's/^\(smtp .*smtpd\)$/\1\n\t-o content_filter=filter:dummy/' /etc/postfix/master.cf + +# pour qu'admin@kaz.bzh recoivent les mails d'anomalie +RUN echo "root: ADMIN_EMAIL" >> /etc/aliases \ + && postalias hash:/etc/aliases + +# Configuration de sympa +RUN echo aliases_program postalias >>/etc/sympa/sympa/sympa.conf \ + && echo sendmail /usr/sbin/sendmail >>/etc/sympa/sympa/sympa.conf \ + && echo soap_url /sympasoap >>/etc/sympa/sympa/sympa.conf \ + && echo dmarc_protection.mode dmarc_reject >>/etc/sympa/sympa/sympa.conf \ + && cp /usr/share/doc/sympa/examples/script/sympa_soap_client.pl.gz /usr/lib/sympa/bin/ \ + && gunzip /usr/lib/sympa/bin/sympa_soap_client.pl.gz \ + && chmod +x /usr/lib/sympa/bin/sympa_soap_client.pl \ + && chown sympa:sympa /usr/lib/sympa/lib/sympa/* \ + && chmod u+s /usr/lib/sympa/lib/sympa/* + +COPY dockers/sympa/config/aliases.sympa.postfix /etc/sympa/aliases.sympa.postfix +COPY dockers/sympa/config/trusted_applications.conf /etc/sympa/trusted_applications.conf + + +RUN postconf "alias_maps = hash:/etc/aliases,hash:/etc/sympa/aliases.sympa.postfix,hash:/etc/mail/sympa/aliases" \ + && postconf "alias_database = hash:/etc/aliases,hash:/etc/sympa/aliases.sympa.postfix" \ + && postalias hash:/etc/sympa/aliases.sympa.postfix \ + && usermod -a -G sympa postfix + +COPY dockers/sympa/config/postfix-wrapper.sh /usr/local/bin/ +RUN chmod a+x /usr/local/bin/postfix-wrapper.sh + +COPY dockers/sympa/config/start.sh / +ENTRYPOINT ["/bin/bash", "/start.sh"] + +EXPOSE 80 443 25 + +# docs sympa : https://listes.renater.fr/sympa/arc/sympa-fr/2021-02/msg00026.html +# https://listes.renater.fr/sympa/arc/sympa-fr/2012-05/msg00042.html diff --git a/dockers/sympa/alerting/filter.sh b/dockers/sympa/alerting/filter.sh new file mode 100755 index 0000000..fc4bc08 --- /dev/null +++ b/dockers/sympa/alerting/filter.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +FILTER=$(docker exec sympaServ cat /var/log/mail.err | grep filter.sh) +COUNT=$(docker exec sympaServ cat /var/log/mail.err | grep -c filter.sh) + +if [ "$COUNT" -gt 2 ]; then + echo $FILTER + echo "alerte filter sympa : $COUNT" +fi diff --git a/dockers/sympa/alerting/mailq.sh b/dockers/sympa/alerting/mailq.sh new file mode 100755 index 0000000..2c1b8d3 --- /dev/null +++ b/dockers/sympa/alerting/mailq.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +QUEUE=$(docker exec sympaServ mailq | grep "^[A-F0-9]") +COUNT=$(docker exec sympaServ mailq | grep -c "^[A-F0-9]") + +if [ "$COUNT" -gt 4 ]; then + echo $QUEUE + echo "alerte mailq sympa : $COUNT" +fi diff --git a/dockers/sympa/alerting/sympa.sh b/dockers/sympa/alerting/sympa.sh new file mode 100755 index 0000000..e6a7761 --- /dev/null +++ b/dockers/sympa/alerting/sympa.sh @@ -0,0 +1,80 @@ +#!/bin/bash +# supervision de sympa +#KAZ_ROOT=$(cd "$(dirname $0)"/..; pwd) +KAZ_ROOT=/kaz +. $KAZ_ROOT/bin/.commonFunctions.sh +setKazVars + +. $DOCKERS_ENV +. $KAZ_ROOT/secret/SetAllPass.sh + +DOCKER_CMD="docker exec sympaServ" +URL_AGORA=$(echo $matterHost).$(echo $domain) + +docker exec ${mattermostServName} bin/mmctl --suppress-warnings auth login $httpProto://$URL_AGORA --name local-server --username $mattermost_user --password $mattermost_pass >/dev/null 2>&1 + +DateFrom() { + OLDLANG=$LANG + LANG=C + declare -A TABDATE + TABDATE[0,1]=Jan + TABDATE[0,2]=Feb + TABDATE[0,3]=Mar + TABDATE[0,4]=Apr + TABDATE[0,5]=May + TABDATE[0,6]=Jun + TABDATE[0,7]=Jul + TABDATE[0,8]=Aug + TABDATE[0,9]=Sep + TABDATE[0,10]=Oct + TABDATE[0,11]=Nov + TABDATE[0,12]=Dec + + MOISCOURANT=$(date +%m | sed -e 's/^0//') + MOISPRECEDENT=$(expr ${MOISCOURANT} - 1 ) + JOURCOURANT=$(date +%d) + HEUREPRECEDENTE=$(date -d '6 hour ago' +%H) + + GAMMEDATE=$(echo ${TABDATE[0,$MOISCOURANT]} ${JOURCOURANT};echo "|";echo ${TABDATE[0,$MOISPRECEDENT]} ${JOURCOURANT}) + LANG=$OLDLANG + if [ "$1" = "-h" ] + then + echo "${TABDATE[0,$MOISCOURANT]} ${JOURCOURANT} ${HEUREPRECEDENTE}" + else + echo ${GAMMEDATE} + fi +} + +PERIODE_RECHERCHE=$(DateFrom -h) +echo "Recherche à partir de ${PERIODE_RECHERCHE} heure(s)" + +OLDIFS=$IFS +IFS=" " + +FILTER_ERR=$(${DOCKER_CMD} grep -E "${PERIODE_RECHERCHE}" /var/log/mail.err | grep filter.sh | awk '{print $4}' | sort -u) +COUNT_FILTER=$(${DOCKER_CMD} grep -E "${PERIODE_RECHERCHE}" /var/log/mail.err | grep filter.sh | awk '{print $4}' | sort -u | wc -w) + +if [ "$COUNT_FILTER" -gt 1 ] +then + echo "---------------------------------------------------------- " + echo $FILTER_ERR + docker exec mattermostServ bin/mmctl post create kaz:Sysadmin-alertes --message "Recherche à partir de ${PERIODE_RECHERCHE} Heure(s)" >/dev/null 2>&1 + docker exec mattermostServ bin/mmctl post create kaz:Sysadmin-alertes --message "L' id message(s): $FILTER_ERR" >/dev/null 2>&1 + echo "---------------------------------------------------------- " + echo "alerte filter sympa : $COUNT_FILTER " + docker exec mattermostServ bin/mmctl post create kaz:Sysadmin-alertes --message "alerte filter sympa : $COUNT_FILTER" >/dev/null 2>&1 +fi + + +QUEUE_MAIL=$(${DOCKER_CMD} mailq | grep @) +COUNT_MAILQ=$(${DOCKER_CMD} mailq | grep -v makerspace56 | grep -c "^[A-F0-9]") + +if [ "$COUNT_MAILQ" -gt 50 ]; then + echo "---------------------------------------------------------- " + echo ${QUEUE_MAIL} + echo "---------------------------------------------------------- " + echo "alerte mailq sympa : ${COUNT_MAILQ}" + docker exec mattermostServ bin/mmctl post create kaz:Sysadmin-alertes --message "Recherche de ${PERIODE_RECHERCHE}" >/dev/null 2>&1 + docker exec mattermostServ bin/mmctl post create kaz:Sysadmin-alertes --message "${QUEUE_MAIL}" >/dev/null 2>&1 + docker exec mattermostServ bin/mmctl post create kaz:Sysadmin-alertes --message "alerte mailq sympa : ${COUNT_MAILQ}" >/dev/null 2>&1 +fi diff --git a/dockers/sympa/build.sh b/dockers/sympa/build.sh new file mode 100755 index 0000000..cd467a0 --- /dev/null +++ b/dockers/sympa/build.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +KAZ_ROOT=$(cd $(dirname $0)/../..; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +printKazMsg "\n *** Création du Dockerfile Sympa" + +cd "${KAZ_ROOT}" +docker build --no-cache -t sympakaz . -f dockers/sympa/Dockerfile diff --git a/dockers/sympa/config/aliases.sympa.postfix b/dockers/sympa/config/aliases.sympa.postfix new file mode 100644 index 0000000..45c9161 --- /dev/null +++ b/dockers/sympa/config/aliases.sympa.postfix @@ -0,0 +1,11 @@ +# Robot aliases for Sympa. +sympa: "| /usr/lib/sympa/bin/queue sympa@MAILNAME.DOMAINNAME" +listmaster: "| /usr/lib/sympa/bin/queue listmaster@MAILNAME.DOMAINNAME" +bounce: "| /usr/lib/sympa/bin/bouncequeue sympa@MAILNAME.DOMAINNAME" +abuse-feedback-report: "| /usr/lib/sympa/bin/bouncequeue sympa@MAILNAME.DOMAINNAME" +sympa-request: postmaster +sympa-owner: postmaster +#listserv: sympa +#listserv-request: sympa-request +#majordomo: sympa +#listserv-owner: sympa-owner diff --git a/dockers/sympa/config/postfix-wrapper.sh b/dockers/sympa/config/postfix-wrapper.sh new file mode 100755 index 0000000..dbefe43 --- /dev/null +++ b/dockers/sympa/config/postfix-wrapper.sh @@ -0,0 +1,32 @@ +#! /bin/bash + +# You cannot start postfix in some foreground mode and +# it's more or less important that docker doesn't kill +# postfix and its chilren if you stop the container. +# +# Use this script with supervisord and it will take +# care about starting and stopping postfix correctly. +# +# supervisord config snippet for postfix-wrapper: +# +# [program:postfix] +# process_name = postfix +# command = /path/to/postfix-wrapper.sh +# startsecs = 0 +# autorestart = false +# + +trap "service postfix stop" SIGINT +trap "service postfix stop" SIGTERM +trap "service postfix reload" SIGHUP + +service postfix start + +# lets give postfix some time to start +sleep 60 + +# wait until postfix is dead (triggered by trap) +while kill -0 "$(< /var/spool/postfix/pid/master.pid)" +do + sleep 5 +done diff --git a/dockers/sympa/config/postfix.sh b/dockers/sympa/config/postfix.sh new file mode 100755 index 0000000..e052033 --- /dev/null +++ b/dockers/sympa/config/postfix.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +postconf "postscreen_dnsbl_action = enforce" +postconf "smtpd_banner = ESMTP" +postconf "postscreen_dnsbl_sites = zen.spamhaus.org*3 bl.mailspike.net b.barracudacentral.org*2 bl.spameatingmonkey.net dnsbl.sorbs.net psbl.surriel.com list.dnswl.org=127.0.[0..255].0*-2 list.dnswl.org=127.0.[0..255].1*-3 list.dnswl.org=127.0.[0..255].[2..3]*-4" +postconf "postscreen_dnsbl_threshold = 3" +postconf "postscreen_dnsbl_whitelist_threshold = -1" +postconf "postscreen_greet_action = enforce" +postconf "postscreen_bare_newline_action = enforce" +postconf "smtputf8_enable = no" +postconf "smtpd_tls_cert_file=ENVCERT" +postconf "smtpd_tls_key_file=ENVKEY" +postconf "myhostname = MAILNAME.DOMAINNAME" +postconf "transport_maps = hash:/etc/postfix/transport" +postconf "message_size_limit = 1024000000" +postconf "slow_destination_concurrency_limit = 2" +postconf "slow_destination_recipient_limit = 20" +postconf "slow_destination_rate_delay = 5s" +postconf "slow_destination_concurrency_failed_cohort_limit=10" +postconf "veryslow_destination_concurrency_limit = 2" +postconf "veryslow_destination_recipient_limit = 2" +postconf "veryslow_destination_rate_delay = 10s" +postconf "veryslow_destination_concurrency_failed_cohort_limit=10" + +postmap /etc/postfix/transport + +grep "slow_destination" /etc/postfix/master.cf >/dev/null 2>&1 +if [ "$?" -ne "0" ] +then + echo -e "slow unix - - n - 5 smtp\n \ + -o syslog_name=postfix-slow" \ + >>/etc/postfix/master.cf +fi + +grep "veryslow_destination" /etc/postfix/master.cf >/dev/null 2>&1 +if [ "$?" -ne "0" ] +then + echo -e "veryslow unix - - n - 5 smtp\n \ + -o syslog_name=postfix-veryslow" \ + >>/etc/postfix/master.cf +fi diff --git a/dockers/sympa/config/start.sh b/dockers/sympa/config/start.sh new file mode 100755 index 0000000..91b9ed8 --- /dev/null +++ b/dockers/sympa/config/start.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +echo "Configuring for ${DOMAIN_SYMPA}" +sed -i -e "s/MAILNAME.DOMAINNAME/${DOMAIN_SYMPA}/g" /etc/postfix/main.cf +sed -i -e "s/MAILNAME.DOMAINNAME/${DOMAIN_SYMPA}/g" /etc/sympa/sympa/sympa.conf +sed -i -e "s/MAILNAME.DOMAINNAME/${DOMAIN_SYMPA}/g" /etc/mailname +sed -i -e "s/ADMIN_EMAIL/$ADMINEMAIL/g" /etc/aliases +postalias /etc/aliases + + +sed -i -e "s%ENVKEY%$KEY%g" /etc/postfix/main.cf +sed -i -e "s%ENVCERT%$CERT%g" /etc/postfix/main.cf + +sed -i -e "s%ENVKEY%$KEY%g" /etc/apache2/sites-available/default-ssl.conf +sed -i -e "s%ENVCERT%$CERT%g" /etc/apache2/sites-available/default-ssl.conf +sed -i -e "s/MAILNAME.DOMAINNAME/${DOMAIN_SYMPA}/g" /etc/apache2/sites-available/000-default.conf +sed -i -e "s/MAILNAME.DOMAINNAME/${DOMAIN_SYMPA}/g" /var/www/html/index.html + +sed -i -e "s/MAILNAME.DOMAINNAME/${DOMAIN_SYMPA}/g" /etc/sympa/aliases.sympa.postfix +postalias hash:/etc/sympa/aliases.sympa.postfix +/usr/lib/sympa/bin/sympa_newaliases.pl + +postmap /etc/postfix/transport + +# sed -i -e "s/LISTMASTERS/$LISTMASTERS/g" /etc/sympa/sympa/sympa.conf + +# Passage de SQLite à MySQL +sed -i -e "s/db_name.*//g" /etc/sympa/sympa/sympa.conf +sed -i -e "s/db_type.*//g" /etc/sympa/sympa/sympa.conf +echo -e "db_type mysql\ndb_host db\ndb_port 3306\ndb_name ${MYSQL_DATABASE}\ndb_user ${MYSQL_USER}\ndb_passwd ${MYSQL_PASSWORD}" >> /etc/sympa/sympa/sympa.conf + +# set des listmasters +sed -i -e "s/listmaster.*//g" /etc/sympa/sympa/sympa.conf +echo -e "listmaster $LISTMASTERS" >> /etc/sympa/sympa/sympa.conf + +# passage en https +sed -i -e "s/http:/https:/g" /etc/sympa/sympa/sympa.conf + +# Initialisation/Vérification de la database +until /usr/lib/sympa/bin/sympa.pl --health_check # creates the database if needed, wait for the DB to be ready +do + sleep 0.1 +done + +# préparation du SOAP +sed -i -e "s%SOAP_USER%${SOAP_USER}%g" /etc/sympa/trusted_applications.conf +MD5PASS=$(/usr/lib/sympa/bin/sympa.pl --md5_digest=${SOAP_PASSWORD} | cut -d':' -f2 | sed -e "s/[[:space:]]*//g") +sed -i -e "s%SOAP_MD5PASS%${MD5PASS}%g" /etc/sympa/trusted_applications.conf + +echo "Starting supervisord" +supervisord -n -c /etc/supervisord.conf diff --git a/dockers/sympa/config/supervisord.conf b/dockers/sympa/config/supervisord.conf new file mode 100644 index 0000000..f6b8907 --- /dev/null +++ b/dockers/sympa/config/supervisord.conf @@ -0,0 +1,90 @@ +[supervisord] +user = root +loglevel = warn +nodaemon = true +strip_ansi = true +logfile = /var/log/supervisor/supervisord.log ; default $CWD/supervisord.log +pidfile = /var/run/supervisord.pid ; default supervisord.pid +childlogdir = /var/log/supervisor ; default $TEMP ('AUTO' child log dir) + +[unix_http_server] +file = /dev/shm/supervisor.sock +chmod = 0700 +chown = nobody:nogroup +username = docker-mailserver +password = docker-mailserver-password + +[supervisorctl] +serverurl = unix:///dev/shm/supervisor.sock ; use a 'unix://' path for a unix socket +username = docker-mailserver +password = docker-mailserver-password + +; must remain in config file for RPC (supervisorctl/web interface) to work, additional +; interfaces may be added by defining them in separate rpcinterface: sections +[rpcinterface:supervisor] +supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface + + +[program:rsyslog] +#command=/etc/init.d/rsyslog restart +command=/usr/sbin/rsyslogd -n +stdout_events_enabled=true +stderr_events_enabled=true + +[program:postfix] +startsecs=0 +stopwaitsecs=55 +autostart=true +autorestart=true +command=/usr/local/bin/postfix-wrapper.sh +#command=/usr/lib/postfix/sbin/master -d +#command=/etc/init.d/postfix restart +stdout_events_enabled=true +stderr_events_enabled=true + +[program:apache2] +#command=/etc/init.d/apache2 restart +command=/usr/sbin/apache2ctl -c "ErrorLog /dev/stdout" -DFOREGROUND +stdout_events_enabled=true +stderr_events_enabled=true + +[program:anacron] +#command=/etc/init.d/anacron restart +command=/usr/sbin/anacron -sd +stdout_events_enabled=true +stderr_events_enabled=true + +[program:sympa-outgoing] +command=/usr/lib/sympa/bin/bulk.pl -F +stdout_events_enabled=true +stderr_events_enabled=true + +[program:sympa-archive] +command=/usr/lib/sympa/bin/archived.pl -F +stdout_events_enabled=true +stderr_events_enabled=true + +[program:sympa-bounce] +command=/usr/lib/sympa/bin/bounced.pl -F +stdout_events_enabled=true +stderr_events_enabled=true + +[program:sympa-task] +command=/usr/lib/sympa/bin/task_manager.pl -F +stdout_events_enabled=true +stderr_events_enabled=true + +[program:sympa] +command=/usr/lib/sympa/bin/sympa_msg.pl --foreground +stdout_events_enabled=true +stderr_events_enabled=true + +[program:sympa-fcgi] +command=/usr/bin/spawn-fcgi -n -u sympa -g sympa -a 127.0.0.1 -p 8442 /usr/lib/cgi-bin/sympa/wwsympa.fcgi +stdout_events_enabled=true +stderr_events_enabled=true + +[program:sympa-soap-fcgi] +command=/usr/bin/spawn-fcgi -n -u sympa -g sympa -a 127.0.0.1 -p 8443 /usr/lib/cgi-bin/sympa/sympa_soap_server.fcgi +stdout_events_enabled=true +stderr_events_enabled=true diff --git a/dockers/sympa/config/sympa.preseed b/dockers/sympa/config/sympa.preseed new file mode 100644 index 0000000..5d105fe --- /dev/null +++ b/dockers/sympa/config/sympa.preseed @@ -0,0 +1,15 @@ +postfix postfix/mailname string MAILNAME.DOMAINNAME +postfix postfix/main_mailer_type string 'Pas de configuration' + +sympa sympa/dbconfig-install boolean true +sympa sympa/database-type select sqlite3 +sympa sympa/sympa_newaliases-wrapper-setuid-root boolean true +sympa sympa/listmaster string LISTMASTERS + +sympa wwsympa/webserver_type select Apache 2 +sympa sympa/use_soap boolean false + +# à garder dans un coin +# sympa sympa/use_wwsympa boolean true +# sympa wwsympa/fastcgi boolean true +# sympa sympa/use_soap boolean true diff --git a/dockers/sympa/config/transport b/dockers/sympa/config/transport new file mode 100644 index 0000000..1421798 --- /dev/null +++ b/dockers/sympa/config/transport @@ -0,0 +1,13 @@ +orange.fr veryslow: +orange.com veryslow: +wanadoo.com veryslow: +wanadoo.fr veryslow: +gmail.com slow: +yahoo.com slow: +yahoo.fr slow: +outlook.com veryslow: +outlook.fr veryslow: +hotmail.fr veryslow: +hotmail.com veryslow: +live.com veryslow: +live.fr veryslow: diff --git a/dockers/sympa/config/trusted_applications.conf b/dockers/sympa/config/trusted_applications.conf new file mode 100644 index 0000000..b920069 --- /dev/null +++ b/dockers/sympa/config/trusted_applications.conf @@ -0,0 +1,5 @@ +trusted_application + name SOAP_USER + md5password SOAP_MD5PASS + # the md5 digest of the application pasword. You can get it with sympa.pl --md5_digest= + proxy_for_variables USER_EMAIL,remote_host diff --git a/dockers/sympa/docker-compose.yml b/dockers/sympa/docker-compose.yml new file mode 100644 index 0000000..281d01d --- /dev/null +++ b/dockers/sympa/docker-compose.yml @@ -0,0 +1,72 @@ +version: '3.3' + +services: + + mail: + image: sympakaz:latest + container_name: ${sympaServName} + restart: ${restartPolicy} + depends_on: + - db + networks: + - sympaNet + - jirafeauNet + links: + - db + external_links: + - db:${fileHost} + - ${jirafeauServName}:${fileHost} + ports: + - ${SYMPA_IP}:25:25 + - ${SYMPA_IP}:80:80 + - ${SYMPA_IP}:443:443 + env_file: + - ../../secret/env-${sympaServName} + environment: + - DOMAIN_SYMPA=${domain_sympa} + volumes: + - sympaLog:/var/log + - sympaIcons:/usr/share/sympa/static_content/icons + - sympaVar:/var/lib/sympa + - sympaSpool:/var/spool/sympa + - sympaPostfixSpool:/var/spool/postfix + - sympaEtcMail:/etc/mail + - filterConfig:/home/filter/config/ + - ./config/transport:/etc/postfix/transport:rw + - /etc/localtime:/etc/localtime:ro + - /etc/timezone:/etc/timezone:ro + - /etc/letsencrypt:/etc/letsencrypt:ro + + db: + image: mariadb:10.5 + container_name: ${sympaDBName} + restart: ${restartPolicy} + networks: + - sympaNet + env_file: + - ../../secret/env-${sympaDBName} + volumes: + - sympaDB:/var/lib/mysql + - /home/sauve/:/svg/ + - /etc/localtime:/etc/localtime:ro + - /etc/timezone:/etc/timezone:ro + + +volumes: + sympaDB: + sympaLog: + sympaIcons: + sympaVar: + sympaSpool: + sympaPostfixSpool: + sympaEtcMail: + filterConfig: + external: true + +networks: + sympaNet: + external: true + name: sympaNet + jirafeauNet: + external: true + name: jirafeauNet diff --git a/dockers/sympa/first.sh b/dockers/sympa/first.sh new file mode 100755 index 0000000..8d97699 --- /dev/null +++ b/dockers/sympa/first.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +KAZ_ROOT=$(cd $(dirname $0)/../..; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +cd $(dirname $0) +. "${DOCKERS_ENV}" +. "${KAZ_KEY_DIR}/SetAllPass.sh" + +DockerServName="${sympaServName}" + +checkDockerRunning "${DockerServName}" "Sympa" || exit + +printKazMsg "\n *** Premier lancement de Sympa" + +# docker exec "${DockerServName}" bash -c "DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends sympa || echo ok" diff --git a/dockers/sympa/reload.sh b/dockers/sympa/reload.sh new file mode 100755 index 0000000..1d77d79 --- /dev/null +++ b/dockers/sympa/reload.sh @@ -0,0 +1,10 @@ +#!/bin/bash + + +# mis à jour du filtre (si pas de ./build) +cd $(dirname $0)/.. +for i in eMailShrinker filter.sh filterTest.sh; do + docker cp "postfix/filter/$i" sympaServ:/home/filter/ +done +#Correction des droits sur le filter.sh +docker exec sympaServ chmod a+rx /home/filter/filter.sh /home/filter/filterTest.sh diff --git a/dockers/sympa/updateFirewall.sh b/dockers/sympa/updateFirewall.sh new file mode 100755 index 0000000..8a45b83 --- /dev/null +++ b/dockers/sympa/updateFirewall.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# à lancer sur l'hôte pour paramétrer iptables +# ça peut brailler car certaines choses devraient être faites une et une seule fois, mais ce script peut être réappelé à chaque lancement du docker. + +#cleaning, may throw errors at first launch +#iptables -t nat -D POSTROUTING -o ens18 -j ipbis +#iptables -t nat -F ipbis +#iptables -t nat -X ipbis + +iptables -t nat -N ipbis +iptables -t nat -F ipbis +iptables -t nat -I ipbis -o ens18 -p tcp --source `docker inspect -f '{{.NetworkSettings.Networks.sympaNet.IPAddress}}' sympaServ` -j SNAT --to `ifconfig ens18:0 | grep "inet" | awk '{print $2}'` +iptables -t nat -I ipbis -o ens18 -p tcp --source `docker inspect -f '{{.NetworkSettings.Networks.jirafeauNet.IPAddress}}' sympaServ` -j SNAT --to `ifconfig ens18:0 | grep "inet" | awk '{print $2}'` +iptables -t nat -A ipbis -j RETURN +iptables -t nat -D POSTROUTING -o ens18 -j ipbis +iptables -t nat -I POSTROUTING -o ens18 -j ipbis diff --git a/dockers/traefik/.env b/dockers/traefik/.env new file mode 120000 index 0000000..406acd1 --- /dev/null +++ b/dockers/traefik/.env @@ -0,0 +1 @@ +../../config/dockers.env \ No newline at end of file diff --git a/dockers/traefik/conf/dynamic/certificates.yml.tmpl b/dockers/traefik/conf/dynamic/certificates.yml.tmpl new file mode 100644 index 0000000..3676fd3 --- /dev/null +++ b/dockers/traefik/conf/dynamic/certificates.yml.tmpl @@ -0,0 +1,20 @@ +#tls: +# certificates: +# - certFile: __SSL_CERT__ +# keyFile: __SSL_KEY__ +# +# stores: +# default: +# defaultCertificate: +# certFile: __SSL_CERT__ +# keyFile: __SSL_KEY__ +# options: +# default: +# minVersion: VersionTLS12 +# cipherSuites: +# - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 +# - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 +# - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 +# - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 +# - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 +# - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 diff --git a/dockers/traefik/conf/dynamic/conf.yml b/dockers/traefik/conf/dynamic/conf.yml new file mode 100644 index 0000000..dd18f50 --- /dev/null +++ b/dockers/traefik/conf/dynamic/conf.yml @@ -0,0 +1,17 @@ +http: + middlewares: + hsts: + headers: + stsincludesubdomains: true + stspreload: true + stsseconds: 31536000 # 1 an + nextcloud-redirectregex1: + redirectregex: + permanent: true + regex: https?://([^/]*)/.well-known/(card|cal)dav + replacement: https://${1}/remote.php/dav/ + nextcloud-redirectregex2: + redirectregex: + permanent: true + regex: https?://([^/]*)(/.well-known[^#]*) + replacement: https://${1}/index.php${2} diff --git a/dockers/traefik/conf/traefik.yml.old b/dockers/traefik/conf/traefik.yml.old new file mode 100644 index 0000000..75e3c6e --- /dev/null +++ b/dockers/traefik/conf/traefik.yml.old @@ -0,0 +1,54 @@ +providers: + file: + directory: "/etc/traefik/dynamic" + watch: true + docker: {} + +entryPoints: + web: + address: ":80" + websecure: + address: ":443" + http: + tls: + certResolver: letsencrypt + # Ajout d'un point d'entrée sur le port 8289 + metrics: + address: ":8289" + +#serversTransport: +# rootCAs: +# - /etc/letsencrypt/local/rootCA.pem + + +api: + dashboard: true + +accessLog: + filePath: "/var/log/traefik/access.log" + format: json + +certificatesresolvers: + letsencrypt: + acme: + # email: sysadmins@kaz.bzh + storage: /letsencrypt/acme.json + # caServer: "https://acme-staging.api.letsencrypt.org/directory" + httpChallenge: + entryPoint: web + +# Ajout de la partie métrique qui concerne Prometheus +metrics: + prometheus: + # Nom du point d'entrée défini au dessus + entryPoint: metrics + # On configure la latence des métriques + buckets: + - 0.1 + - 0.3 + - 1.2 + - 5.0 + # Ajout des métriques sur les points d'entrée + addEntryPointsLabels: true + # Ajout des services + addServicesLabels: true diff --git a/dockers/traefik/docker-compose.tmpl.yml.dist b/dockers/traefik/docker-compose.tmpl.yml.dist new file mode 100644 index 0000000..2d3b8c2 --- /dev/null +++ b/dockers/traefik/docker-compose.tmpl.yml.dist @@ -0,0 +1,219 @@ +version: '3' + +services: + reverse-proxy: + # The official v2 Traefik docker image + image: traefik:v2.10.7 + container_name: ${traefikServName} + restart: ${restartPolicy} + # Enables the web UI and tells Traefik to listen to docker + ports: + # The HTTP port + - ${MAIN_IP}:80:80 + - ${MAIN_IP}:443:443 + # The Web UI (enabled by --api.insecure=true) + # - ${MAIN_IP}:8289:8289 + volumes: + # So that Traefik can listen to the Docker events + - /var/run/docker.sock:/var/run/docker.sock:ro + - ./conf:/etc/traefik/ + - letsencrypt:/letsencrypt + environment: + - TRAEFIK_PROVIDERS_DOCKER=true + - TRAEFIK_PROVIDERS_DOCKER_EXPOSEDBYDEFAULT=false + - TRAEFIK_API=true + - TRAEFIK_PROVIDERS_FILE_DIRECTORY=/etc/traefik/dynamic + - TRAEFIK_ENTRYPOINTS_web_ADDRESS=:80 + - TRAEFIK_ENTRYPOINTS_web_HTTP_REDIRECTIONS_ENTRYPOINT_TO=websecure + - TRAEFIK_ENTRYPOINTS_websecure_ADDRESS=:443 + - TRAEFIK_ENTRYPOINTS_websecure_HTTP_TLS_CERTRESOLVER=letsencrypt + #- TRAEFIK_ENTRYPOINTS_metrics_ADDRESS=:8289 + #- TRAEFIK_METRICS_PROMETHEUS_ENTRYPOINT=metrics + - TRAEFIK_CERTIFICATESRESOLVERS_letsencrypt_ACME_EMAIL=admin@${domain} + - TRAEFIK_CERTIFICATESRESOLVERS_letsencrypt_ACME_CASERVER=${acme_server} + - TRAEFIK_CERTIFICATESRESOLVERS_letsencrypt_ACME_STORAGE=/letsencrypt/acme.json + - TRAEFIK_CERTIFICATESRESOLVERS_letsencrypt_ACME_TLSCHALLENGE=true + - TRAEFIK_LOG_LEVEL=DEBUG + - TRAEFIK_ENTRYPOINTS_websecure_HTTP_MIDDLEWARES=hsts@file + #- LEGO_CA_CERTIFICATES=/etc/traefik/root_ca.crt + #- TRAEFIK_CERTIFICATESRESOLVERS_letsencrypt_ACME_HTTPCHALLENGE=true + #- TRAEFIK_CERTIFICATESRESOLVERS_letsencrypt_ACME_HTTPCHALLENGE_ENTRYPOINT=web + - TRAEFIK_API_DASHBOARD=true + labels: + - "traefik.enable=true" + - "traefik.http.routers.traefik_https.rule=Host(`${site}.${domain}`) && PathPrefix(`/api`, `/dashboard`)" + - "traefik.http.routers.traefik_https.rule=Host(`${site}.${domain}`)" + - "traefik.http.routers.traefik_https.entrypoints=websecure" + # - "traefik.http.routers.traefik_https.tls=true" + - "traefik.http.routers.traefik_https.service=api@internal" + - "traefik.http.routers.traefik_https.middlewares=test-adminipwhitelist@file,traefik-auth" + # - "traefik.http.routers.traefik_https.tls.certresolver=letsencrypt" + - "traefik.http.middlewares.traefik-auth.basicauth.usersfile=/etc/traefik/passfile" + networks: + - traefikNet +{{web + - webNet +}} +{{jirafeau + - jirafeauNet +}} +{{ethercalc + - ethercalcNet +}} +{{etherpad + - etherpadNet +}} +{{framadate + - framadateNet +}} +{{ldap + - ldapNet +}} +{{mobilizon + - mobilizonNet +}} +{{cloud + - cloudNet +}} +{{collabora + - collaboraNet +}} +{{paheko + - pahekoNet +}} +{{mattermost + - mattermostNet +}} +{{roundcube + - roundcubeNet +}} +{{gitea + - giteaNet +}} +{{dokuwiki + - dokuwikiNet +}} +{{postfix + - postfixNet +}} +{{vaultwarden + - vaultwardenNet +}} +{{imapsync + - imapsyncNet +}} +{{castopod + - castopodNet +}} +{{apikaz + - apikazNet +}} + +#### BEGIN ORGA USE_NET +#### END ORGA USE_NET + +networks: + traefikNet: + external: true + name: traefikNet +{{web + webNet: + external: true + name: webNet +}} +{{jirafeau + jirafeauNet: + external: true + name: jirafeauNet +}} +{{ethercalc + ethercalcNet: + external: true + name: ethercalcNet +}} +{{etherpad + etherpadNet: + external: true + name: etherpadNet +}} +{{framadate + framadateNet: + external: true + name: framadateNet +}} +{{ldap + ldapNet: + external: true + name: ldapNet +}} +{{mobilizon + mobilizonNet: + external: true + name: mobilizonNet +}} +{{cloud + cloudNet: + external: true + name: cloudNet +}} +{{collabora + collaboraNet: + external: true + name: collaboraNet +}} +{{paheko + pahekoNet: + external: true + name: pahekoNet +}} +{{mattermost + mattermostNet: + external: true + name: mattermostNet +}} +{{roundcube + roundcubeNet: + external: true + name: roundcubeNet +}} +{{gitea + giteaNet: + external: true + name: giteaNet +}} +{{dokuwiki + dokuwikiNet: + external: true + name: dokuwikiNet +}} +{{postfix + postfixNet: + external: true + name: postfixNet +}} +{{vaultwarden + vaultwardenNet: + external: true + name: vaultwardenNet +}} +{{imapsync + imapsyncNet: + external: true + name: imapsyncNet +}} +{{castopod + castopodNet: + external: true + name: castopodNet +}} +{{api + apikazNet: + external: true + name: apikazNet +}} + +#### BEGIN ORGA DEF_NET +#### END ORGA DEF_NET + +volumes: + letsencrypt: diff --git a/dockers/traefik/proxy-gen.sh b/dockers/traefik/proxy-gen.sh new file mode 100755 index 0000000..109951b --- /dev/null +++ b/dockers/traefik/proxy-gen.sh @@ -0,0 +1,168 @@ +#!/bin/bash + +KAZ_ROOT=$(cd "$(dirname $0)/../.."; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars +. "${DOCKERS_ENV}" +. "${KAZ_ROOT}/secret/SetAllPass.sh" + +printKazMsg "\n *** Proxy update config" + +#NGINX_TMPL=config/nginx.tmpl.conf +#NGINX_CONF=config/nginx.conf +DOCKER_DIST=docker-compose.tmpl.yml.dist +DOCKER_TMPL=docker-compose.tmpl.yml +DOCKER_CONF=docker-compose.yml +PASSFILE=conf/passfile + +ALLOW_ADMIN_IP_FILE="/kaz/secret/allow_admin_ip" +ALLOW_IP_FILE="/kaz/config/proxy/allow_ip" + +# TODO +# for service in agora cloud paheko wiki wp; do +# touch "${KAZ_CONF_PROXY_DIR}/${service}_kaz_map" +# touch "${KAZ_CONF_PROXY_DIR}/${service}_kaz_name" +# done + +cd $(dirname $0) +# update ip allowed +TRAEFIK_ALLOW_IP_FILE=conf/dynamic/allow_ip.yml +if [ ! -f "${TRAEFIK_ALLOW_IP_FILE}" ]; then + cat > "${TRAEFIK_ALLOW_IP_FILE}" < "${PROXY_PORT_CFG}" < "${PROXY_REDIRECT}" <> ${PASSFILE} +[[ -f "${DOCKER_TMPL}" ]] || cp "${DOCKER_DIST}" "${DOCKER_TMPL}" +if [ -f "conf/root_ca.crt" ]; then + sed -i "s|#- LEGO|- LEGO|g" ${DOCKER_TMPL} +fi +"${APPLY_TMPL}" -time "${DOCKER_TMPL}" "${DOCKER_CONF}" +# "${APPLY_TMPL}" -time "${NGINX_TMPL}" "${NGINX_CONF}" + +#("${KAZ_COMP_DIR}/web/web-gen.sh" ) & diff --git a/dockers/traefik/reload.sh b/dockers/traefik/reload.sh new file mode 100755 index 0000000..bbe95cf --- /dev/null +++ b/dockers/traefik/reload.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +# Do nothing +# Théoriquement traefik gère tout seul sauf les changements dans le traefik.yml \ No newline at end of file diff --git a/dockers/vaultwarden/.env b/dockers/vaultwarden/.env new file mode 120000 index 0000000..406acd1 --- /dev/null +++ b/dockers/vaultwarden/.env @@ -0,0 +1 @@ +../../config/dockers.env \ No newline at end of file diff --git a/dockers/vaultwarden/docker-compose.yml b/dockers/vaultwarden/docker-compose.yml new file mode 100644 index 0000000..261c446 --- /dev/null +++ b/dockers/vaultwarden/docker-compose.yml @@ -0,0 +1,65 @@ +version: '3.9' +services: + + vaultwarden: + image: vaultwarden/server + container_name: ${vaultwardenServName} + restart: ${restartPolicy} + depends_on: + db: + condition: service_healthy + links: + - db + environment: + - SIGNUPS_DOMAINS_WHITELIST=${domain} + - SIGNUPS_VERIFY=true + - SMTP_HOST=smtp + - SMTP_FROM=${vaultwardenHost}@${domain} + - SMTP_PORT=25 + - SMTP_SECURITY=off + - DOMAIN=https://${vaultwardenHost}.${domain} + env_file: + - ../../secret/env-${vaultwardenServName} + networks: + - vaultwardenNet + - postfixNet + volumes: + - vaultwarden-data:/data + - ./templates:/data/templates + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + labels: + - "traefik.enable=true" + - "traefik.http.routers.${vaultwardenServName}.rule=Host(`${vaultwardenHost}.${domain}`)" + - "traefik.docker.network=vaultwardenNet" + + db: + image: mariadb:10.5 + container_name: ${vaultwardenDBName} + restart: ${restartPolicy} + networks: + - vaultwardenNet + env_file: + - ../../secret/env-${vaultwardenDBName} + volumes: + - vaultwardenDB:/var/lib/mysql + - /home/sauve/:/svg/ + - /etc/localtime:/etc/localtime:ro + - /etc/timezone:/etc/timezone:ro + healthcheck: + test: ["CMD", "mysqladmin", "ping", "--silent"] + interval: 30s + timeout: 30s + retries: 5 + +volumes: + vaultwarden-data: + vaultwardenDB: + +networks: + vaultwardenNet: + external: true + name: vaultwardenNet + postfixNet: + external: true + name: postfixNet diff --git a/dockers/vaultwarden/templates/email/admin_reset_password.hbs b/dockers/vaultwarden/templates/email/admin_reset_password.hbs new file mode 100644 index 0000000..8d38177 --- /dev/null +++ b/dockers/vaultwarden/templates/email/admin_reset_password.hbs @@ -0,0 +1,6 @@ +Master Password Has Been Changed + +The master password for {{user_name}} has been changed by an administrator in your {{org_name}} organization. If you did not initiate this request, please reach out to your administrator immediately. + +=== +Github: https://github.com/dani-garcia/vaultwarden diff --git a/dockers/vaultwarden/templates/email/admin_reset_password.html.hbs b/dockers/vaultwarden/templates/email/admin_reset_password.html.hbs new file mode 100644 index 0000000..d9749d2 --- /dev/null +++ b/dockers/vaultwarden/templates/email/admin_reset_password.html.hbs @@ -0,0 +1,11 @@ +Master Password Has Been Changed + +{{> email/email_header }} + + + + +
+ The master password for {{user_name}} has been changed by an administrator in your {{org_name}} organization. If you did not initiate this request, please reach out to your administrator immediately. +
+{{> email/email_footer }} diff --git a/dockers/vaultwarden/templates/email/change_email.hbs b/dockers/vaultwarden/templates/email/change_email.hbs new file mode 100644 index 0000000..170af6f --- /dev/null +++ b/dockers/vaultwarden/templates/email/change_email.hbs @@ -0,0 +1,8 @@ +Your Email Change + +To finalize changing your email address enter the following code in web vault: {{token}} + +If you did not try to change an email address, you can safely ignore this email. + +=== +Github: https://github.com/dani-garcia/vaultwarden diff --git a/dockers/vaultwarden/templates/email/change_email.html.hbs b/dockers/vaultwarden/templates/email/change_email.html.hbs new file mode 100644 index 0000000..aecf885 --- /dev/null +++ b/dockers/vaultwarden/templates/email/change_email.html.hbs @@ -0,0 +1,16 @@ +Your Email Change + +{{> email/email_header }} + + + + + + + +
+ To finalize changing your email address enter the following code in web vault: {{token}} +
+ If you did not try to change an email address, you can safely ignore this email. +
+{{> email/email_footer }} \ No newline at end of file diff --git a/dockers/vaultwarden/templates/email/delete_account.hbs b/dockers/vaultwarden/templates/email/delete_account.hbs new file mode 100644 index 0000000..56ef5c7 --- /dev/null +++ b/dockers/vaultwarden/templates/email/delete_account.hbs @@ -0,0 +1,8 @@ +Delete Your Account + +Click the link below to delete your account. + +Delete Your Account: {{url}}/#/verify-recover-delete?userId={{user_id}}&token={{token}}&email={{email}} + +If you did not request this email to delete your account, you can safely ignore this email. +{{> email/email_footer_text }} \ No newline at end of file diff --git a/dockers/vaultwarden/templates/email/delete_account.html.hbs b/dockers/vaultwarden/templates/email/delete_account.html.hbs new file mode 100644 index 0000000..a129ce5 --- /dev/null +++ b/dockers/vaultwarden/templates/email/delete_account.html.hbs @@ -0,0 +1,24 @@ +Delete Your Account + +{{> email/email_header }} + + + + + + + + + + +
+ click the link below to delete your account. +
+ + Delete Your Account + +
+ If you did not request this email to delete your account, you can safely ignore this email. +
+{{> email/email_footer }} \ No newline at end of file diff --git a/dockers/vaultwarden/templates/email/email_footer.hbs b/dockers/vaultwarden/templates/email/email_footer.hbs new file mode 100644 index 0000000..7bf3068 --- /dev/null +++ b/dockers/vaultwarden/templates/email/email_footer.hbs @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/dockers/vaultwarden/templates/email/email_footer_text.hbs b/dockers/vaultwarden/templates/email/email_footer_text.hbs new file mode 100644 index 0000000..c741b95 --- /dev/null +++ b/dockers/vaultwarden/templates/email/email_footer_text.hbs @@ -0,0 +1,3 @@ + +=== +Github: https://github.com/dani-garcia/vaultwarden diff --git a/dockers/vaultwarden/templates/email/email_header.hbs b/dockers/vaultwarden/templates/email/email_header.hbs new file mode 100644 index 0000000..811f997 --- /dev/null +++ b/dockers/vaultwarden/templates/email/email_header.hbs @@ -0,0 +1,94 @@ + + + + + + Vaultwarden + + + + + + + + +
+ + +
+ + + ",e.document[0]).appendTo(n)):"tr"===s?e._createTrPlaceholder(e.currentItem,n):"img"===s&&n.attr("src",e.currentItem.attr("src")),i||n.css("visibility","hidden"),n},update:function(t,n){(!i||s.forcePlaceholderSize)&&(n.height()||n.height(e.currentItem.innerHeight()-parseInt(e.currentItem.css("paddingTop")||0,10)-parseInt(e.currentItem.css("paddingBottom")||0,10)),n.width()||n.width(e.currentItem.innerWidth()-parseInt(e.currentItem.css("paddingLeft")||0,10)-parseInt(e.currentItem.css("paddingRight")||0,10)))}}),e.placeholder=t(s.placeholder.element.call(e.element,e.currentItem)),e.currentItem.after(e.placeholder),s.placeholder.update(e,e.placeholder)},_createTrPlaceholder:function(e,i){var s=this;e.children().each(function(){t("",s.document[0]).attr("colspan",t(this).attr("colspan")||1).appendTo(i)})},_contactContainers:function(e){var i,s,n,o,a,r,h,l,c,u,d=null,p=null;for(i=this.containers.length-1;i>=0;i--)if(!t.contains(this.currentItem[0],this.containers[i].element[0]))if(this._intersectsWith(this.containers[i].containerCache)){if(d&&t.contains(this.containers[i].element[0],d.element[0]))continue;d=this.containers[i],p=i}else this.containers[i].containerCache.over&&(this.containers[i]._trigger("out",e,this._uiHash(this)),this.containers[i].containerCache.over=0);if(d)if(1===this.containers.length)this.containers[p].containerCache.over||(this.containers[p]._trigger("over",e,this._uiHash(this)),this.containers[p].containerCache.over=1);else{for(n=1e4,o=null,c=d.floating||this._isFloating(this.currentItem),a=c?"left":"top",r=c?"width":"height",u=c?"pageX":"pageY",s=this.items.length-1;s>=0;s--)t.contains(this.containers[p].element[0],this.items[s].item[0])&&this.items[s].item[0]!==this.currentItem[0]&&(h=this.items[s].item.offset()[a],l=!1,e[u]-h>this.items[s][r]/2&&(l=!0),n>Math.abs(e[u]-h)&&(n=Math.abs(e[u]-h),o=this.items[s],this.direction=l?"up":"down"));if(!o&&!this.options.dropOnEmpty)return;if(this.currentContainer===this.containers[p])return this.currentContainer.containerCache.over||(this.containers[p]._trigger("over",e,this._uiHash()),this.currentContainer.containerCache.over=1),void 0;o?this._rearrange(e,o,null,!0):this._rearrange(e,null,this.containers[p].element,!0),this._trigger("change",e,this._uiHash()),this.containers[p]._trigger("change",e,this._uiHash(this)),this.currentContainer=this.containers[p],this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[p]._trigger("over",e,this._uiHash(this)),this.containers[p].containerCache.over=1}},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper)?t(i.helper.apply(this.element[0],[e,this.currentItem])):"clone"===i.helper?this.currentItem.clone():this.currentItem;return s.parents("body").length||t("parent"!==i.appendTo?i.appendTo:this.currentItem[0].parentNode)[0].appendChild(s[0]),s[0]===this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(!s[0].style.width||i.forceHelperSize)&&s.width(this.currentItem.width()),(!s[0].style.height||i.forceHelperSize)&&s.height(this.currentItem.height()),s},_adjustOffsetFromHelper:function(e){"string"==typeof e&&(e=e.split(" ")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),"left"in e&&(this.offset.click.left=e.left+this.margins.left),"right"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),"top"in e&&(this.offset.click.top=e.top+this.margins.top),"bottom"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var e=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===this.document[0].body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&t.ui.ie)&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var t=this.currentItem.position();return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:t.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,n=this.options;"parent"===n.containment&&(n.containment=this.helper[0].parentNode),("document"===n.containment||"window"===n.containment)&&(this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,"document"===n.containment?this.document.width():this.window.width()-this.helperProportions.width-this.margins.left,("document"===n.containment?this.document.height()||document.body.parentNode.scrollHeight:this.window.height()||this.document[0].body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]),/^(document|window|parent)$/.test(n.containment)||(e=t(n.containment)[0],i=t(n.containment).offset(),s="hidden"!==t(e).css("overflow"),this.containment=[i.left+(parseInt(t(e).css("borderLeftWidth"),10)||0)+(parseInt(t(e).css("paddingLeft"),10)||0)-this.margins.left,i.top+(parseInt(t(e).css("borderTopWidth"),10)||0)+(parseInt(t(e).css("paddingTop"),10)||0)-this.margins.top,i.left+(s?Math.max(e.scrollWidth,e.offsetWidth):e.offsetWidth)-(parseInt(t(e).css("borderLeftWidth"),10)||0)-(parseInt(t(e).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,i.top+(s?Math.max(e.scrollHeight,e.offsetHeight):e.offsetHeight)-(parseInt(t(e).css("borderTopWidth"),10)||0)-(parseInt(t(e).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top])},_convertPositionTo:function(e,i){i||(i=this.position);var s="absolute"===e?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,o=/(html|body)/i.test(n[0].tagName);return{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():o?0:n.scrollTop())*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():o?0:n.scrollLeft())*s}},_generatePosition:function(e){var i,s,n=this.options,o=e.pageX,a=e.pageY,r="absolute"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,h=/(html|body)/i.test(r[0].tagName);return"relative"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&this.scrollParent[0]!==this.offsetParent[0]||(this.offset.relative=this._getRelativeOffset()),this.originalPosition&&(this.containment&&(e.pageX-this.offset.click.leftthis.containment[2]&&(o=this.containment[2]+this.offset.click.left),e.pageY-this.offset.click.top>this.containment[3]&&(a=this.containment[3]+this.offset.click.top)),n.grid&&(i=this.originalPageY+Math.round((a-this.originalPageY)/n.grid[1])*n.grid[1],a=this.containment?i-this.offset.click.top>=this.containment[1]&&i-this.offset.click.top<=this.containment[3]?i:i-this.offset.click.top>=this.containment[1]?i-n.grid[1]:i+n.grid[1]:i,s=this.originalPageX+Math.round((o-this.originalPageX)/n.grid[0])*n.grid[0],o=this.containment?s-this.offset.click.left>=this.containment[0]&&s-this.offset.click.left<=this.containment[2]?s:s-this.offset.click.left>=this.containment[0]?s-n.grid[0]:s+n.grid[0]:s)),{top:a-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():h?0:r.scrollTop()),left:o-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():h?0:r.scrollLeft())}},_rearrange:function(t,e,i,s){i?i[0].appendChild(this.placeholder[0]):e.item[0].parentNode.insertBefore(this.placeholder[0],"down"===this.direction?e.item[0]:e.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var n=this.counter; +this._delay(function(){n===this.counter&&this.refreshPositions(!s)})},_clear:function(t,e){function i(t,e,i){return function(s){i._trigger(t,s,e._uiHash(e))}}this.reverting=!1;var s,n=[];if(!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null,this.helper[0]===this.currentItem[0]){for(s in this._storedCSS)("auto"===this._storedCSS[s]||"static"===this._storedCSS[s])&&(this._storedCSS[s]="");this.currentItem.css(this._storedCSS),this._removeClass(this.currentItem,"ui-sortable-helper")}else this.currentItem.show();for(this.fromOutside&&!e&&n.push(function(t){this._trigger("receive",t,this._uiHash(this.fromOutside))}),!this.fromOutside&&this.domPosition.prev===this.currentItem.prev().not(".ui-sortable-helper")[0]&&this.domPosition.parent===this.currentItem.parent()[0]||e||n.push(function(t){this._trigger("update",t,this._uiHash())}),this!==this.currentContainer&&(e||(n.push(function(t){this._trigger("remove",t,this._uiHash())}),n.push(function(t){return function(e){t._trigger("receive",e,this._uiHash(this))}}.call(this,this.currentContainer)),n.push(function(t){return function(e){t._trigger("update",e,this._uiHash(this))}}.call(this,this.currentContainer)))),s=this.containers.length-1;s>=0;s--)e||n.push(i("deactivate",this,this.containers[s])),this.containers[s].containerCache.over&&(n.push(i("out",this,this.containers[s])),this.containers[s].containerCache.over=0);if(this.storedCursor&&(this.document.find("body").css("cursor",this.storedCursor),this.storedStylesheet.remove()),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex","auto"===this._storedZIndex?"":this._storedZIndex),this.dragging=!1,e||this._trigger("beforeStop",t,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.cancelHelperRemoval||(this.helper[0]!==this.currentItem[0]&&this.helper.remove(),this.helper=null),!e){for(s=0;n.length>s;s++)n[s].call(this,t);this._trigger("stop",t,this._uiHash())}return this.fromOutside=!1,!this.cancelHelperRemoval},_trigger:function(){t.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(e){var i=e||this;return{helper:i.helper,placeholder:i.placeholder||t([]),position:i.position,originalPosition:i.originalPosition,offset:i.positionAbs,item:i.currentItem,sender:e?e.element:null}}}),t.widget("ui.spinner",{version:"1.12.1",defaultElement:"",widgetEventPrefix:"spin",options:{classes:{"ui-spinner":"ui-corner-all","ui-spinner-down":"ui-corner-br","ui-spinner-up":"ui-corner-tr"},culture:null,icons:{down:"ui-icon-triangle-1-s",up:"ui-icon-triangle-1-n"},incremental:!0,max:null,min:null,numberFormat:null,page:10,step:1,change:null,spin:null,start:null,stop:null},_create:function(){this._setOption("max",this.options.max),this._setOption("min",this.options.min),this._setOption("step",this.options.step),""!==this.value()&&this._value(this.element.val(),!0),this._draw(),this._on(this._events),this._refresh(),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_getCreateOptions:function(){var e=this._super(),i=this.element;return t.each(["min","max","step"],function(t,s){var n=i.attr(s);null!=n&&n.length&&(e[s]=n)}),e},_events:{keydown:function(t){this._start(t)&&this._keydown(t)&&t.preventDefault()},keyup:"_stop",focus:function(){this.previous=this.element.val()},blur:function(t){return this.cancelBlur?(delete this.cancelBlur,void 0):(this._stop(),this._refresh(),this.previous!==this.element.val()&&this._trigger("change",t),void 0)},mousewheel:function(t,e){if(e){if(!this.spinning&&!this._start(t))return!1;this._spin((e>0?1:-1)*this.options.step,t),clearTimeout(this.mousewheelTimer),this.mousewheelTimer=this._delay(function(){this.spinning&&this._stop(t)},100),t.preventDefault()}},"mousedown .ui-spinner-button":function(e){function i(){var e=this.element[0]===t.ui.safeActiveElement(this.document[0]);e||(this.element.trigger("focus"),this.previous=s,this._delay(function(){this.previous=s}))}var s;s=this.element[0]===t.ui.safeActiveElement(this.document[0])?this.previous:this.element.val(),e.preventDefault(),i.call(this),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur,i.call(this)}),this._start(e)!==!1&&this._repeat(null,t(e.currentTarget).hasClass("ui-spinner-up")?1:-1,e)},"mouseup .ui-spinner-button":"_stop","mouseenter .ui-spinner-button":function(e){return t(e.currentTarget).hasClass("ui-state-active")?this._start(e)===!1?!1:(this._repeat(null,t(e.currentTarget).hasClass("ui-spinner-up")?1:-1,e),void 0):void 0},"mouseleave .ui-spinner-button":"_stop"},_enhance:function(){this.uiSpinner=this.element.attr("autocomplete","off").wrap("").parent().append("")},_draw:function(){this._enhance(),this._addClass(this.uiSpinner,"ui-spinner","ui-widget ui-widget-content"),this._addClass("ui-spinner-input"),this.element.attr("role","spinbutton"),this.buttons=this.uiSpinner.children("a").attr("tabIndex",-1).attr("aria-hidden",!0).button({classes:{"ui-button":""}}),this._removeClass(this.buttons,"ui-corner-all"),this._addClass(this.buttons.first(),"ui-spinner-button ui-spinner-up"),this._addClass(this.buttons.last(),"ui-spinner-button ui-spinner-down"),this.buttons.first().button({icon:this.options.icons.up,showLabel:!1}),this.buttons.last().button({icon:this.options.icons.down,showLabel:!1}),this.buttons.height()>Math.ceil(.5*this.uiSpinner.height())&&this.uiSpinner.height()>0&&this.uiSpinner.height(this.uiSpinner.height())},_keydown:function(e){var i=this.options,s=t.ui.keyCode;switch(e.keyCode){case s.UP:return this._repeat(null,1,e),!0;case s.DOWN:return this._repeat(null,-1,e),!0;case s.PAGE_UP:return this._repeat(null,i.page,e),!0;case s.PAGE_DOWN:return this._repeat(null,-i.page,e),!0}return!1},_start:function(t){return this.spinning||this._trigger("start",t)!==!1?(this.counter||(this.counter=1),this.spinning=!0,!0):!1},_repeat:function(t,e,i){t=t||500,clearTimeout(this.timer),this.timer=this._delay(function(){this._repeat(40,e,i)},t),this._spin(e*this.options.step,i)},_spin:function(t,e){var i=this.value()||0;this.counter||(this.counter=1),i=this._adjustValue(i+t*this._increment(this.counter)),this.spinning&&this._trigger("spin",e,{value:i})===!1||(this._value(i),this.counter++)},_increment:function(e){var i=this.options.incremental;return i?t.isFunction(i)?i(e):Math.floor(e*e*e/5e4-e*e/500+17*e/200+1):1},_precision:function(){var t=this._precisionOf(this.options.step);return null!==this.options.min&&(t=Math.max(t,this._precisionOf(this.options.min))),t},_precisionOf:function(t){var e=""+t,i=e.indexOf(".");return-1===i?0:e.length-i-1},_adjustValue:function(t){var e,i,s=this.options;return e=null!==s.min?s.min:0,i=t-e,i=Math.round(i/s.step)*s.step,t=e+i,t=parseFloat(t.toFixed(this._precision())),null!==s.max&&t>s.max?s.max:null!==s.min&&s.min>t?s.min:t},_stop:function(t){this.spinning&&(clearTimeout(this.timer),clearTimeout(this.mousewheelTimer),this.counter=0,this.spinning=!1,this._trigger("stop",t))},_setOption:function(t,e){var i,s,n;return"culture"===t||"numberFormat"===t?(i=this._parse(this.element.val()),this.options[t]=e,this.element.val(this._format(i)),void 0):(("max"===t||"min"===t||"step"===t)&&"string"==typeof e&&(e=this._parse(e)),"icons"===t&&(s=this.buttons.first().find(".ui-icon"),this._removeClass(s,null,this.options.icons.up),this._addClass(s,null,e.up),n=this.buttons.last().find(".ui-icon"),this._removeClass(n,null,this.options.icons.down),this._addClass(n,null,e.down)),this._super(t,e),void 0)},_setOptionDisabled:function(t){this._super(t),this._toggleClass(this.uiSpinner,null,"ui-state-disabled",!!t),this.element.prop("disabled",!!t),this.buttons.button(t?"disable":"enable")},_setOptions:r(function(t){this._super(t)}),_parse:function(t){return"string"==typeof t&&""!==t&&(t=window.Globalize&&this.options.numberFormat?Globalize.parseFloat(t,10,this.options.culture):+t),""===t||isNaN(t)?null:t},_format:function(t){return""===t?"":window.Globalize&&this.options.numberFormat?Globalize.format(t,this.options.numberFormat,this.options.culture):t},_refresh:function(){this.element.attr({"aria-valuemin":this.options.min,"aria-valuemax":this.options.max,"aria-valuenow":this._parse(this.element.val())})},isValid:function(){var t=this.value();return null===t?!1:t===this._adjustValue(t)},_value:function(t,e){var i;""!==t&&(i=this._parse(t),null!==i&&(e||(i=this._adjustValue(i)),t=this._format(i))),this.element.val(t),this._refresh()},_destroy:function(){this.element.prop("disabled",!1).removeAttr("autocomplete role aria-valuemin aria-valuemax aria-valuenow"),this.uiSpinner.replaceWith(this.element)},stepUp:r(function(t){this._stepUp(t)}),_stepUp:function(t){this._start()&&(this._spin((t||1)*this.options.step),this._stop())},stepDown:r(function(t){this._stepDown(t)}),_stepDown:function(t){this._start()&&(this._spin((t||1)*-this.options.step),this._stop())},pageUp:r(function(t){this._stepUp((t||1)*this.options.page)}),pageDown:r(function(t){this._stepDown((t||1)*this.options.page)}),value:function(t){return arguments.length?(r(this._value).call(this,t),void 0):this._parse(this.element.val())},widget:function(){return this.uiSpinner}}),t.uiBackCompat!==!1&&t.widget("ui.spinner",t.ui.spinner,{_enhance:function(){this.uiSpinner=this.element.attr("autocomplete","off").wrap(this._uiSpinnerHtml()).parent().append(this._buttonHtml())},_uiSpinnerHtml:function(){return""},_buttonHtml:function(){return""}}),t.ui.spinner,t.widget("ui.tabs",{version:"1.12.1",delay:300,options:{active:null,classes:{"ui-tabs":"ui-corner-all","ui-tabs-nav":"ui-corner-all","ui-tabs-panel":"ui-corner-bottom","ui-tabs-tab":"ui-corner-top"},collapsible:!1,event:"click",heightStyle:"content",hide:null,show:null,activate:null,beforeActivate:null,beforeLoad:null,load:null},_isLocal:function(){var t=/#.*$/;return function(e){var i,s;i=e.href.replace(t,""),s=location.href.replace(t,"");try{i=decodeURIComponent(i)}catch(n){}try{s=decodeURIComponent(s)}catch(n){}return e.hash.length>1&&i===s}}(),_create:function(){var e=this,i=this.options;this.running=!1,this._addClass("ui-tabs","ui-widget ui-widget-content"),this._toggleClass("ui-tabs-collapsible",null,i.collapsible),this._processTabs(),i.active=this._initialActive(),t.isArray(i.disabled)&&(i.disabled=t.unique(i.disabled.concat(t.map(this.tabs.filter(".ui-state-disabled"),function(t){return e.tabs.index(t)}))).sort()),this.active=this.options.active!==!1&&this.anchors.length?this._findActive(i.active):t(),this._refresh(),this.active.length&&this.load(i.active)},_initialActive:function(){var e=this.options.active,i=this.options.collapsible,s=location.hash.substring(1);return null===e&&(s&&this.tabs.each(function(i,n){return t(n).attr("aria-controls")===s?(e=i,!1):void 0}),null===e&&(e=this.tabs.index(this.tabs.filter(".ui-tabs-active"))),(null===e||-1===e)&&(e=this.tabs.length?0:!1)),e!==!1&&(e=this.tabs.index(this.tabs.eq(e)),-1===e&&(e=i?!1:0)),!i&&e===!1&&this.anchors.length&&(e=0),e},_getCreateEventData:function(){return{tab:this.active,panel:this.active.length?this._getPanelForTab(this.active):t()}},_tabKeydown:function(e){var i=t(t.ui.safeActiveElement(this.document[0])).closest("li"),s=this.tabs.index(i),n=!0;if(!this._handlePageNav(e)){switch(e.keyCode){case t.ui.keyCode.RIGHT:case t.ui.keyCode.DOWN:s++;break;case t.ui.keyCode.UP:case t.ui.keyCode.LEFT:n=!1,s--;break;case t.ui.keyCode.END:s=this.anchors.length-1;break;case t.ui.keyCode.HOME:s=0;break;case t.ui.keyCode.SPACE:return e.preventDefault(),clearTimeout(this.activating),this._activate(s),void 0;case t.ui.keyCode.ENTER:return e.preventDefault(),clearTimeout(this.activating),this._activate(s===this.options.active?!1:s),void 0;default:return}e.preventDefault(),clearTimeout(this.activating),s=this._focusNextTab(s,n),e.ctrlKey||e.metaKey||(i.attr("aria-selected","false"),this.tabs.eq(s).attr("aria-selected","true"),this.activating=this._delay(function(){this.option("active",s)},this.delay))}},_panelKeydown:function(e){this._handlePageNav(e)||e.ctrlKey&&e.keyCode===t.ui.keyCode.UP&&(e.preventDefault(),this.active.trigger("focus"))},_handlePageNav:function(e){return e.altKey&&e.keyCode===t.ui.keyCode.PAGE_UP?(this._activate(this._focusNextTab(this.options.active-1,!1)),!0):e.altKey&&e.keyCode===t.ui.keyCode.PAGE_DOWN?(this._activate(this._focusNextTab(this.options.active+1,!0)),!0):void 0},_findNextTab:function(e,i){function s(){return e>n&&(e=0),0>e&&(e=n),e}for(var n=this.tabs.length-1;-1!==t.inArray(s(),this.options.disabled);)e=i?e+1:e-1;return e},_focusNextTab:function(t,e){return t=this._findNextTab(t,e),this.tabs.eq(t).trigger("focus"),t},_setOption:function(t,e){return"active"===t?(this._activate(e),void 0):(this._super(t,e),"collapsible"===t&&(this._toggleClass("ui-tabs-collapsible",null,e),e||this.options.active!==!1||this._activate(0)),"event"===t&&this._setupEvents(e),"heightStyle"===t&&this._setupHeightStyle(e),void 0)},_sanitizeSelector:function(t){return t?t.replace(/[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g,"\\$&"):""},refresh:function(){var e=this.options,i=this.tablist.children(":has(a[href])");e.disabled=t.map(i.filter(".ui-state-disabled"),function(t){return i.index(t)}),this._processTabs(),e.active!==!1&&this.anchors.length?this.active.length&&!t.contains(this.tablist[0],this.active[0])?this.tabs.length===e.disabled.length?(e.active=!1,this.active=t()):this._activate(this._findNextTab(Math.max(0,e.active-1),!1)):e.active=this.tabs.index(this.active):(e.active=!1,this.active=t()),this._refresh()},_refresh:function(){this._setOptionDisabled(this.options.disabled),this._setupEvents(this.options.event),this._setupHeightStyle(this.options.heightStyle),this.tabs.not(this.active).attr({"aria-selected":"false","aria-expanded":"false",tabIndex:-1}),this.panels.not(this._getPanelForTab(this.active)).hide().attr({"aria-hidden":"true"}),this.active.length?(this.active.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0}),this._addClass(this.active,"ui-tabs-active","ui-state-active"),this._getPanelForTab(this.active).show().attr({"aria-hidden":"false"})):this.tabs.eq(0).attr("tabIndex",0)},_processTabs:function(){var e=this,i=this.tabs,s=this.anchors,n=this.panels;this.tablist=this._getList().attr("role","tablist"),this._addClass(this.tablist,"ui-tabs-nav","ui-helper-reset ui-helper-clearfix ui-widget-header"),this.tablist.on("mousedown"+this.eventNamespace,"> li",function(e){t(this).is(".ui-state-disabled")&&e.preventDefault()}).on("focus"+this.eventNamespace,".ui-tabs-anchor",function(){t(this).closest("li").is(".ui-state-disabled")&&this.blur()}),this.tabs=this.tablist.find("> li:has(a[href])").attr({role:"tab",tabIndex:-1}),this._addClass(this.tabs,"ui-tabs-tab","ui-state-default"),this.anchors=this.tabs.map(function(){return t("a",this)[0]}).attr({role:"presentation",tabIndex:-1}),this._addClass(this.anchors,"ui-tabs-anchor"),this.panels=t(),this.anchors.each(function(i,s){var n,o,a,r=t(s).uniqueId().attr("id"),h=t(s).closest("li"),l=h.attr("aria-controls");e._isLocal(s)?(n=s.hash,a=n.substring(1),o=e.element.find(e._sanitizeSelector(n))):(a=h.attr("aria-controls")||t({}).uniqueId()[0].id,n="#"+a,o=e.element.find(n),o.length||(o=e._createPanel(a),o.insertAfter(e.panels[i-1]||e.tablist)),o.attr("aria-live","polite")),o.length&&(e.panels=e.panels.add(o)),l&&h.data("ui-tabs-aria-controls",l),h.attr({"aria-controls":a,"aria-labelledby":r}),o.attr("aria-labelledby",r)}),this.panels.attr("role","tabpanel"),this._addClass(this.panels,"ui-tabs-panel","ui-widget-content"),i&&(this._off(i.not(this.tabs)),this._off(s.not(this.anchors)),this._off(n.not(this.panels)))},_getList:function(){return this.tablist||this.element.find("ol, ul").eq(0)},_createPanel:function(e){return t("
").attr("id",e).data("ui-tabs-destroy",!0)},_setOptionDisabled:function(e){var i,s,n;for(t.isArray(e)&&(e.length?e.length===this.anchors.length&&(e=!0):e=!1),n=0;s=this.tabs[n];n++)i=t(s),e===!0||-1!==t.inArray(n,e)?(i.attr("aria-disabled","true"),this._addClass(i,null,"ui-state-disabled")):(i.removeAttr("aria-disabled"),this._removeClass(i,null,"ui-state-disabled"));this.options.disabled=e,this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,e===!0)},_setupEvents:function(e){var i={};e&&t.each(e.split(" "),function(t,e){i[e]="_eventHandler"}),this._off(this.anchors.add(this.tabs).add(this.panels)),this._on(!0,this.anchors,{click:function(t){t.preventDefault()}}),this._on(this.anchors,i),this._on(this.tabs,{keydown:"_tabKeydown"}),this._on(this.panels,{keydown:"_panelKeydown"}),this._focusable(this.tabs),this._hoverable(this.tabs)},_setupHeightStyle:function(e){var i,s=this.element.parent();"fill"===e?(i=s.height(),i-=this.element.outerHeight()-this.element.height(),this.element.siblings(":visible").each(function(){var e=t(this),s=e.css("position");"absolute"!==s&&"fixed"!==s&&(i-=e.outerHeight(!0))}),this.element.children().not(this.panels).each(function(){i-=t(this).outerHeight(!0)}),this.panels.each(function(){t(this).height(Math.max(0,i-t(this).innerHeight()+t(this).height()))}).css("overflow","auto")):"auto"===e&&(i=0,this.panels.each(function(){i=Math.max(i,t(this).height("").height())}).height(i))},_eventHandler:function(e){var i=this.options,s=this.active,n=t(e.currentTarget),o=n.closest("li"),a=o[0]===s[0],r=a&&i.collapsible,h=r?t():this._getPanelForTab(o),l=s.length?this._getPanelForTab(s):t(),c={oldTab:s,oldPanel:l,newTab:r?t():o,newPanel:h};e.preventDefault(),o.hasClass("ui-state-disabled")||o.hasClass("ui-tabs-loading")||this.running||a&&!i.collapsible||this._trigger("beforeActivate",e,c)===!1||(i.active=r?!1:this.tabs.index(o),this.active=a?t():o,this.xhr&&this.xhr.abort(),l.length||h.length||t.error("jQuery UI Tabs: Mismatching fragment identifier."),h.length&&this.load(this.tabs.index(o),e),this._toggle(e,c))},_toggle:function(e,i){function s(){o.running=!1,o._trigger("activate",e,i)}function n(){o._addClass(i.newTab.closest("li"),"ui-tabs-active","ui-state-active"),a.length&&o.options.show?o._show(a,o.options.show,s):(a.show(),s())}var o=this,a=i.newPanel,r=i.oldPanel;this.running=!0,r.length&&this.options.hide?this._hide(r,this.options.hide,function(){o._removeClass(i.oldTab.closest("li"),"ui-tabs-active","ui-state-active"),n()}):(this._removeClass(i.oldTab.closest("li"),"ui-tabs-active","ui-state-active"),r.hide(),n()),r.attr("aria-hidden","true"),i.oldTab.attr({"aria-selected":"false","aria-expanded":"false"}),a.length&&r.length?i.oldTab.attr("tabIndex",-1):a.length&&this.tabs.filter(function(){return 0===t(this).attr("tabIndex")}).attr("tabIndex",-1),a.attr("aria-hidden","false"),i.newTab.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0})},_activate:function(e){var i,s=this._findActive(e);s[0]!==this.active[0]&&(s.length||(s=this.active),i=s.find(".ui-tabs-anchor")[0],this._eventHandler({target:i,currentTarget:i,preventDefault:t.noop}))},_findActive:function(e){return e===!1?t():this.tabs.eq(e)},_getIndex:function(e){return"string"==typeof e&&(e=this.anchors.index(this.anchors.filter("[href$='"+t.ui.escapeSelector(e)+"']"))),e},_destroy:function(){this.xhr&&this.xhr.abort(),this.tablist.removeAttr("role").off(this.eventNamespace),this.anchors.removeAttr("role tabIndex").removeUniqueId(),this.tabs.add(this.panels).each(function(){t.data(this,"ui-tabs-destroy")?t(this).remove():t(this).removeAttr("role tabIndex aria-live aria-busy aria-selected aria-labelledby aria-hidden aria-expanded")}),this.tabs.each(function(){var e=t(this),i=e.data("ui-tabs-aria-controls");i?e.attr("aria-controls",i).removeData("ui-tabs-aria-controls"):e.removeAttr("aria-controls")}),this.panels.show(),"content"!==this.options.heightStyle&&this.panels.css("height","")},enable:function(e){var i=this.options.disabled;i!==!1&&(void 0===e?i=!1:(e=this._getIndex(e),i=t.isArray(i)?t.map(i,function(t){return t!==e?t:null}):t.map(this.tabs,function(t,i){return i!==e?i:null})),this._setOptionDisabled(i))},disable:function(e){var i=this.options.disabled;if(i!==!0){if(void 0===e)i=!0;else{if(e=this._getIndex(e),-1!==t.inArray(e,i))return;i=t.isArray(i)?t.merge([e],i).sort():[e]}this._setOptionDisabled(i)}},load:function(e,i){e=this._getIndex(e);var s=this,n=this.tabs.eq(e),o=n.find(".ui-tabs-anchor"),a=this._getPanelForTab(n),r={tab:n,panel:a},h=function(t,e){"abort"===e&&s.panels.stop(!1,!0),s._removeClass(n,"ui-tabs-loading"),a.removeAttr("aria-busy"),t===s.xhr&&delete s.xhr};this._isLocal(o[0])||(this.xhr=t.ajax(this._ajaxSettings(o,i,r)),this.xhr&&"canceled"!==this.xhr.statusText&&(this._addClass(n,"ui-tabs-loading"),a.attr("aria-busy","true"),this.xhr.done(function(t,e,n){setTimeout(function(){a.html(t),s._trigger("load",i,r),h(n,e)},1)}).fail(function(t,e){setTimeout(function(){h(t,e)},1)})))},_ajaxSettings:function(e,i,s){var n=this;return{url:e.attr("href").replace(/#.*$/,""),beforeSend:function(e,o){return n._trigger("beforeLoad",i,t.extend({jqXHR:e,ajaxSettings:o},s))}}},_getPanelForTab:function(e){var i=t(e).attr("aria-controls");return this.element.find(this._sanitizeSelector("#"+i))}}),t.uiBackCompat!==!1&&t.widget("ui.tabs",t.ui.tabs,{_processTabs:function(){this._superApply(arguments),this._addClass(this.tabs,"ui-tab")}}),t.ui.tabs,t.widget("ui.tooltip",{version:"1.12.1",options:{classes:{"ui-tooltip":"ui-corner-all ui-widget-shadow"},content:function(){var e=t(this).attr("title")||"";return t("").text(e).html()},hide:!0,items:"[title]:not([disabled])",position:{my:"left top+15",at:"left bottom",collision:"flipfit flip"},show:!0,track:!1,close:null,open:null},_addDescribedBy:function(e,i){var s=(e.attr("aria-describedby")||"").split(/\s+/);s.push(i),e.data("ui-tooltip-id",i).attr("aria-describedby",t.trim(s.join(" ")))},_removeDescribedBy:function(e){var i=e.data("ui-tooltip-id"),s=(e.attr("aria-describedby")||"").split(/\s+/),n=t.inArray(i,s);-1!==n&&s.splice(n,1),e.removeData("ui-tooltip-id"),s=t.trim(s.join(" ")),s?e.attr("aria-describedby",s):e.removeAttr("aria-describedby")},_create:function(){this._on({mouseover:"open",focusin:"open"}),this.tooltips={},this.parents={},this.liveRegion=t("
").attr({role:"log","aria-live":"assertive","aria-relevant":"additions"}).appendTo(this.document[0].body),this._addClass(this.liveRegion,null,"ui-helper-hidden-accessible"),this.disabledTitles=t([])},_setOption:function(e,i){var s=this;this._super(e,i),"content"===e&&t.each(this.tooltips,function(t,e){s._updateContent(e.element)})},_setOptionDisabled:function(t){this[t?"_disable":"_enable"]()},_disable:function(){var e=this;t.each(this.tooltips,function(i,s){var n=t.Event("blur");n.target=n.currentTarget=s.element[0],e.close(n,!0)}),this.disabledTitles=this.disabledTitles.add(this.element.find(this.options.items).addBack().filter(function(){var e=t(this);return e.is("[title]")?e.data("ui-tooltip-title",e.attr("title")).removeAttr("title"):void 0}))},_enable:function(){this.disabledTitles.each(function(){var e=t(this);e.data("ui-tooltip-title")&&e.attr("title",e.data("ui-tooltip-title"))}),this.disabledTitles=t([])},open:function(e){var i=this,s=t(e?e.target:this.element).closest(this.options.items);s.length&&!s.data("ui-tooltip-id")&&(s.attr("title")&&s.data("ui-tooltip-title",s.attr("title")),s.data("ui-tooltip-open",!0),e&&"mouseover"===e.type&&s.parents().each(function(){var e,s=t(this);s.data("ui-tooltip-open")&&(e=t.Event("blur"),e.target=e.currentTarget=this,i.close(e,!0)),s.attr("title")&&(s.uniqueId(),i.parents[this.id]={element:this,title:s.attr("title")},s.attr("title",""))}),this._registerCloseHandlers(e,s),this._updateContent(s,e))},_updateContent:function(t,e){var i,s=this.options.content,n=this,o=e?e.type:null;return"string"==typeof s||s.nodeType||s.jquery?this._open(e,t,s):(i=s.call(t[0],function(i){n._delay(function(){t.data("ui-tooltip-open")&&(e&&(e.type=o),this._open(e,t,i))})}),i&&this._open(e,t,i),void 0)},_open:function(e,i,s){function n(t){l.of=t,a.is(":hidden")||a.position(l)}var o,a,r,h,l=t.extend({},this.options.position);if(s){if(o=this._find(i))return o.tooltip.find(".ui-tooltip-content").html(s),void 0;i.is("[title]")&&(e&&"mouseover"===e.type?i.attr("title",""):i.removeAttr("title")),o=this._tooltip(i),a=o.tooltip,this._addDescribedBy(i,a.attr("id")),a.find(".ui-tooltip-content").html(s),this.liveRegion.children().hide(),h=t("
").html(a.find(".ui-tooltip-content").html()),h.removeAttr("name").find("[name]").removeAttr("name"),h.removeAttr("id").find("[id]").removeAttr("id"),h.appendTo(this.liveRegion),this.options.track&&e&&/^mouse/.test(e.type)?(this._on(this.document,{mousemove:n}),n(e)):a.position(t.extend({of:i},this.options.position)),a.hide(),this._show(a,this.options.show),this.options.track&&this.options.show&&this.options.show.delay&&(r=this.delayedShow=setInterval(function(){a.is(":visible")&&(n(l.of),clearInterval(r))},t.fx.interval)),this._trigger("open",e,{tooltip:a})}},_registerCloseHandlers:function(e,i){var s={keyup:function(e){if(e.keyCode===t.ui.keyCode.ESCAPE){var s=t.Event(e);s.currentTarget=i[0],this.close(s,!0)}}};i[0]!==this.element[0]&&(s.remove=function(){this._removeTooltip(this._find(i).tooltip)}),e&&"mouseover"!==e.type||(s.mouseleave="close"),e&&"focusin"!==e.type||(s.focusout="close"),this._on(!0,i,s)},close:function(e){var i,s=this,n=t(e?e.currentTarget:this.element),o=this._find(n);return o?(i=o.tooltip,o.closing||(clearInterval(this.delayedShow),n.data("ui-tooltip-title")&&!n.attr("title")&&n.attr("title",n.data("ui-tooltip-title")),this._removeDescribedBy(n),o.hiding=!0,i.stop(!0),this._hide(i,this.options.hide,function(){s._removeTooltip(t(this))}),n.removeData("ui-tooltip-open"),this._off(n,"mouseleave focusout keyup"),n[0]!==this.element[0]&&this._off(n,"remove"),this._off(this.document,"mousemove"),e&&"mouseleave"===e.type&&t.each(this.parents,function(e,i){t(i.element).attr("title",i.title),delete s.parents[e]}),o.closing=!0,this._trigger("close",e,{tooltip:i}),o.hiding||(o.closing=!1)),void 0):(n.removeData("ui-tooltip-open"),void 0)},_tooltip:function(e){var i=t("
").attr("role","tooltip"),s=t("
").appendTo(i),n=i.uniqueId().attr("id");return this._addClass(s,"ui-tooltip-content"),this._addClass(i,"ui-tooltip","ui-widget ui-widget-content"),i.appendTo(this._appendTo(e)),this.tooltips[n]={element:e,tooltip:i}},_find:function(t){var e=t.data("ui-tooltip-id");return e?this.tooltips[e]:null},_removeTooltip:function(t){t.remove(),delete this.tooltips[t.attr("id")]},_appendTo:function(t){var e=t.closest(".ui-front, dialog");return e.length||(e=this.document[0].body),e},_destroy:function(){var e=this;t.each(this.tooltips,function(i,s){var n=t.Event("blur"),o=s.element;n.target=n.currentTarget=o[0],e.close(n,!0),t("#"+i).remove(),o.data("ui-tooltip-title")&&(o.attr("title")||o.attr("title",o.data("ui-tooltip-title")),o.removeData("ui-tooltip-title"))}),this.liveRegion.remove()}}),t.uiBackCompat!==!1&&t.widget("ui.tooltip",t.ui.tooltip,{options:{tooltipClass:null},_tooltip:function(){var t=this._superApply(arguments);return this.options.tooltipClass&&t.tooltip.addClass(this.options.tooltipClass),t}}),t.ui.tooltip}); \ No newline at end of file diff --git a/dockers/web/html/colorlib-regform-16/vendor/jquery/jquery.min.js b/dockers/web/html/colorlib-regform-16/vendor/jquery/jquery.min.js new file mode 100755 index 0000000..4b98bac --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/vendor/jquery/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";var n=[],r=e.document,i=Object.getPrototypeOf,o=n.slice,a=n.concat,s=n.push,u=n.indexOf,l={},c=l.toString,f=l.hasOwnProperty,p=f.toString,d=p.call(Object),h={},g=function e(t){return"function"==typeof t&&"number"!=typeof t.nodeType},y=function e(t){return null!=t&&t===t.window},v={type:!0,src:!0,noModule:!0};function m(e,t,n){var i,o=(t=t||r).createElement("script");if(o.text=e,n)for(i in v)n[i]&&(o[i]=n[i]);t.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[c.call(e)]||"object":typeof e}var b="3.3.1",w=function(e,t){return new w.fn.init(e,t)},T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;w.fn=w.prototype={jquery:"3.3.1",constructor:w,length:0,toArray:function(){return o.call(this)},get:function(e){return null==e?o.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=w.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return w.each(this,e)},map:function(e){return this.pushStack(w.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(o.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n0&&t-1 in e)}var E=function(e){var t,n,r,i,o,a,s,u,l,c,f,p,d,h,g,y,v,m,x,b="sizzle"+1*new Date,w=e.document,T=0,C=0,E=ae(),k=ae(),S=ae(),D=function(e,t){return e===t&&(f=!0),0},N={}.hasOwnProperty,A=[],j=A.pop,q=A.push,L=A.push,H=A.slice,O=function(e,t){for(var n=0,r=e.length;n+~]|"+M+")"+M+"*"),z=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),X=new RegExp(W),U=new RegExp("^"+R+"$"),V={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Q=/^[^{]+\{\s*\[native \w/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/[+~]/,Z=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ee=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},re=function(){p()},ie=me(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{L.apply(A=H.call(w.childNodes),w.childNodes),A[w.childNodes.length].nodeType}catch(e){L={apply:A.length?function(e,t){q.apply(e,H.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function oe(e,t,r,i){var o,s,l,c,f,h,v,m=t&&t.ownerDocument,T=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==T&&9!==T&&11!==T)return r;if(!i&&((t?t.ownerDocument||t:w)!==d&&p(t),t=t||d,g)){if(11!==T&&(f=J.exec(e)))if(o=f[1]){if(9===T){if(!(l=t.getElementById(o)))return r;if(l.id===o)return r.push(l),r}else if(m&&(l=m.getElementById(o))&&x(t,l)&&l.id===o)return r.push(l),r}else{if(f[2])return L.apply(r,t.getElementsByTagName(e)),r;if((o=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return L.apply(r,t.getElementsByClassName(o)),r}if(n.qsa&&!S[e+" "]&&(!y||!y.test(e))){if(1!==T)m=t,v=e;else if("object"!==t.nodeName.toLowerCase()){(c=t.getAttribute("id"))?c=c.replace(te,ne):t.setAttribute("id",c=b),s=(h=a(e)).length;while(s--)h[s]="#"+c+" "+ve(h[s]);v=h.join(","),m=K.test(e)&&ge(t.parentNode)||t}if(v)try{return L.apply(r,m.querySelectorAll(v)),r}catch(e){}finally{c===b&&t.removeAttribute("id")}}}return u(e.replace(B,"$1"),t,r,i)}function ae(){var e=[];function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}return t}function se(e){return e[b]=!0,e}function ue(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function le(e,t){var n=e.split("|"),i=n.length;while(i--)r.attrHandle[n[i]]=t}function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function fe(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function de(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ie(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function he(e){return se(function(t){return t=+t,se(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function ge(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}n=oe.support={},o=oe.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},p=oe.setDocument=function(e){var t,i,a=e?e.ownerDocument||e:w;return a!==d&&9===a.nodeType&&a.documentElement?(d=a,h=d.documentElement,g=!o(d),w!==d&&(i=d.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",re,!1):i.attachEvent&&i.attachEvent("onunload",re)),n.attributes=ue(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ue(function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=Q.test(d.getElementsByClassName),n.getById=ue(function(e){return h.appendChild(e).id=b,!d.getElementsByName||!d.getElementsByName(b).length}),n.getById?(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&g)return t.getElementsByClassName(e)},v=[],y=[],(n.qsa=Q.test(d.querySelectorAll))&&(ue(function(e){h.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+P+")"),e.querySelectorAll("[id~="+b+"-]").length||y.push("~="),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||y.push(".#.+[+~]")}),ue(function(e){e.innerHTML="";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),h.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(n.matchesSelector=Q.test(m=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&ue(function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),v.push("!=",W)}),y=y.length&&new RegExp(y.join("|")),v=v.length&&new RegExp(v.join("|")),t=Q.test(h.compareDocumentPosition),x=t||Q.test(h.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===d||e.ownerDocument===w&&x(w,e)?-1:t===d||t.ownerDocument===w&&x(w,t)?1:c?O(c,e)-O(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===d?-1:t===d?1:i?-1:o?1:c?O(c,e)-O(c,t):0;if(i===o)return ce(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?ce(a[r],s[r]):a[r]===w?-1:s[r]===w?1:0},d):d},oe.matches=function(e,t){return oe(e,null,null,t)},oe.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&p(e),t=t.replace(z,"='$1']"),n.matchesSelector&&g&&!S[t+" "]&&(!v||!v.test(t))&&(!y||!y.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return oe(t,d,null,[e]).length>0},oe.contains=function(e,t){return(e.ownerDocument||e)!==d&&p(e),x(e,t)},oe.attr=function(e,t){(e.ownerDocument||e)!==d&&p(e);var i=r.attrHandle[t.toLowerCase()],o=i&&N.call(r.attrHandle,t.toLowerCase())?i(e,t,!g):void 0;return void 0!==o?o:n.attributes||!g?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},oe.escape=function(e){return(e+"").replace(te,ne)},oe.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},oe.uniqueSort=function(e){var t,r=[],i=0,o=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(D),f){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return c=null,e},i=oe.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else while(t=e[r++])n+=i(t);return n},(r=oe.selectors={cacheLength:50,createPseudo:se,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Z,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(Z,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||oe.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&oe.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return V.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Z,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=E[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&E(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=oe.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace($," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",y=t.parentNode,v=s&&t.nodeName.toLowerCase(),m=!u&&!s,x=!1;if(y){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===v:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?y.firstChild:y.lastChild],a&&m){x=(d=(l=(c=(f=(p=y)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1])&&l[2],p=d&&y.childNodes[d];while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if(1===p.nodeType&&++x&&p===t){c[e]=[T,d,x];break}}else if(m&&(x=d=(l=(c=(f=(p=t)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1]),!1===x)while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===v:1===p.nodeType)&&++x&&(m&&((c=(f=p[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]=[T,x]),p===t))break;return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||oe.error("unsupported pseudo: "+e);return i[b]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?se(function(e,n){var r,o=i(e,t),a=o.length;while(a--)e[r=O(e,o[a])]=!(n[r]=o[a])}):function(e){return i(e,0,n)}):i}},pseudos:{not:se(function(e){var t=[],n=[],r=s(e.replace(B,"$1"));return r[b]?se(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}}),has:se(function(e){return function(t){return oe(e,t).length>0}}),contains:se(function(e){return e=e.replace(Z,ee),function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:se(function(e){return U.test(e||"")||oe.error("unsupported lang: "+e),e=e.replace(Z,ee).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:de(!1),disabled:de(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Y.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:he(function(){return[0]}),last:he(function(e,t){return[t-1]}),eq:he(function(e,t,n){return[n<0?n+t:n]}),even:he(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:he(function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function be(e,t,n){for(var r=0,i=t.length;r-1&&(o[l]=!(a[l]=f))}}else v=we(v===a?v.splice(h,v.length):v),i?i(null,a,v,u):L.apply(a,v)})}function Ce(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],s=a||r.relative[" "],u=a?1:0,c=me(function(e){return e===t},s,!0),f=me(function(e){return O(t,e)>-1},s,!0),p=[function(e,n,r){var i=!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,i}];u1&&xe(p),u>1&&ve(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(B,"$1"),n,u0,i=e.length>0,o=function(o,a,s,u,c){var f,h,y,v=0,m="0",x=o&&[],b=[],w=l,C=o||i&&r.find.TAG("*",c),E=T+=null==w?1:Math.random()||.1,k=C.length;for(c&&(l=a===d||a||c);m!==k&&null!=(f=C[m]);m++){if(i&&f){h=0,a||f.ownerDocument===d||(p(f),s=!g);while(y=e[h++])if(y(f,a||d,s)){u.push(f);break}c&&(T=E)}n&&((f=!y&&f)&&v--,o&&x.push(f))}if(v+=m,n&&m!==v){h=0;while(y=t[h++])y(x,b,a,s);if(o){if(v>0)while(m--)x[m]||b[m]||(b[m]=j.call(u));b=we(b)}L.apply(u,b),c&&!o&&b.length>0&&v+t.length>1&&oe.uniqueSort(u)}return c&&(T=E,l=w),x};return n?se(o):o}return s=oe.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=a(e)),n=t.length;while(n--)(o=Ce(t[n]))[b]?r.push(o):i.push(o);(o=S(e,Ee(i,r))).selector=e}return o},u=oe.select=function(e,t,n,i){var o,u,l,c,f,p="function"==typeof e&&e,d=!i&&a(e=p.selector||e);if(n=n||[],1===d.length){if((u=d[0]=d[0].slice(0)).length>2&&"ID"===(l=u[0]).type&&9===t.nodeType&&g&&r.relative[u[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(Z,ee),t)||[])[0]))return n;p&&(t=t.parentNode),e=e.slice(u.shift().value.length)}o=V.needsContext.test(e)?0:u.length;while(o--){if(l=u[o],r.relative[c=l.type])break;if((f=r.find[c])&&(i=f(l.matches[0].replace(Z,ee),K.test(u[0].type)&&ge(t.parentNode)||t))){if(u.splice(o,1),!(e=i.length&&ve(u)))return L.apply(n,i),n;break}}}return(p||s(e,d))(i,t,!g,n,!t||K.test(e)&&ge(t.parentNode)||t),n},n.sortStable=b.split("").sort(D).join("")===b,n.detectDuplicates=!!f,p(),n.sortDetached=ue(function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))}),ue(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||le("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ue(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||le("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ue(function(e){return null==e.getAttribute("disabled")})||le(P,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),oe}(e);w.find=E,w.expr=E.selectors,w.expr[":"]=w.expr.pseudos,w.uniqueSort=w.unique=E.uniqueSort,w.text=E.getText,w.isXMLDoc=E.isXML,w.contains=E.contains,w.escapeSelector=E.escape;var k=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&w(e).is(n))break;r.push(e)}return r},S=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},D=w.expr.match.needsContext;function N(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var A=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,t,n){return g(t)?w.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?w.grep(e,function(e){return e===t!==n}):"string"!=typeof t?w.grep(e,function(e){return u.call(t,e)>-1!==n}):w.filter(t,e,n)}w.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?w.find.matchesSelector(r,e)?[r]:[]:w.find.matches(e,w.grep(t,function(e){return 1===e.nodeType}))},w.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(w(e).filter(function(){for(t=0;t1?w.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&D.test(e)?w(e):e||[],!1).length}});var q,L=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(w.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:L.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof w?t[0]:t,w.merge(this,w.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:r,!0)),A.test(i[1])&&w.isPlainObject(t))for(i in t)g(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(o=r.getElementById(i[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):g(e)?void 0!==n.ready?n.ready(e):e(w):w.makeArray(e,this)}).prototype=w.fn,q=w(r);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};w.fn.extend({has:function(e){var t=w(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&w.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?w.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?u.call(w(e),this[0]):u.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(w.uniqueSort(w.merge(this.get(),w(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}w.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return k(e,"parentNode")},parentsUntil:function(e,t,n){return k(e,"parentNode",n)},next:function(e){return P(e,"nextSibling")},prev:function(e){return P(e,"previousSibling")},nextAll:function(e){return k(e,"nextSibling")},prevAll:function(e){return k(e,"previousSibling")},nextUntil:function(e,t,n){return k(e,"nextSibling",n)},prevUntil:function(e,t,n){return k(e,"previousSibling",n)},siblings:function(e){return S((e.parentNode||{}).firstChild,e)},children:function(e){return S(e.firstChild)},contents:function(e){return N(e,"iframe")?e.contentDocument:(N(e,"template")&&(e=e.content||e),w.merge([],e.childNodes))}},function(e,t){w.fn[e]=function(n,r){var i=w.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=w.filter(r,i)),this.length>1&&(O[e]||w.uniqueSort(i),H.test(e)&&i.reverse()),this.pushStack(i)}});var M=/[^\x20\t\r\n\f]+/g;function R(e){var t={};return w.each(e.match(M)||[],function(e,n){t[n]=!0}),t}w.Callbacks=function(e){e="string"==typeof e?R(e):w.extend({},e);var t,n,r,i,o=[],a=[],s=-1,u=function(){for(i=i||e.once,r=t=!0;a.length;s=-1){n=a.shift();while(++s-1)o.splice(n,1),n<=s&&s--}),this},has:function(e){return e?w.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l};function I(e){return e}function W(e){throw e}function $(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n):e&&g(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}w.extend({Deferred:function(t){var n=[["notify","progress",w.Callbacks("memory"),w.Callbacks("memory"),2],["resolve","done",w.Callbacks("once memory"),w.Callbacks("once memory"),0,"resolved"],["reject","fail",w.Callbacks("once memory"),w.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},"catch":function(e){return i.then(null,e)},pipe:function(){var e=arguments;return w.Deferred(function(t){w.each(n,function(n,r){var i=g(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&g(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){var o=0;function a(t,n,r,i){return function(){var s=this,u=arguments,l=function(){var e,l;if(!(t=o&&(r!==W&&(s=void 0,u=[e]),n.rejectWith(s,u))}};t?c():(w.Deferred.getStackHook&&(c.stackTrace=w.Deferred.getStackHook()),e.setTimeout(c))}}return w.Deferred(function(e){n[0][3].add(a(0,e,g(i)?i:I,e.notifyWith)),n[1][3].add(a(0,e,g(t)?t:I)),n[2][3].add(a(0,e,g(r)?r:W))}).promise()},promise:function(e){return null!=e?w.extend(e,i):i}},o={};return w.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=o.call(arguments),a=w.Deferred(),s=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?o.call(arguments):n,--t||a.resolveWith(r,i)}};if(t<=1&&($(e,a.done(s(n)).resolve,a.reject,!t),"pending"===a.state()||g(i[n]&&i[n].then)))return a.then();while(n--)$(i[n],s(n),a.reject);return a.promise()}});var B=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;w.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&B.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},w.readyException=function(t){e.setTimeout(function(){throw t})};var F=w.Deferred();w.fn.ready=function(e){return F.then(e)["catch"](function(e){w.readyException(e)}),this},w.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--w.readyWait:w.isReady)||(w.isReady=!0,!0!==e&&--w.readyWait>0||F.resolveWith(r,[w]))}}),w.ready.then=F.then;function _(){r.removeEventListener("DOMContentLoaded",_),e.removeEventListener("load",_),w.ready()}"complete"===r.readyState||"loading"!==r.readyState&&!r.documentElement.doScroll?e.setTimeout(w.ready):(r.addEventListener("DOMContentLoaded",_),e.addEventListener("load",_));var z=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===x(n)){i=!0;for(s in n)z(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,g(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(w(e),n)})),t))for(;s1,null,!0)},removeData:function(e){return this.each(function(){K.remove(this,e)})}}),w.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=J.get(e,t),n&&(!r||Array.isArray(n)?r=J.access(e,t,w.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=w.queue(e,t),r=n.length,i=n.shift(),o=w._queueHooks(e,t),a=function(){w.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return J.get(e,n)||J.access(e,n,{empty:w.Callbacks("once memory").add(function(){J.remove(e,[t+"queue",n])})})}}),w.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]+)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"
diff --git a/dockers/vaultwarden/templates/email/emergency_access_invite_accepted.hbs b/dockers/vaultwarden/templates/email/emergency_access_invite_accepted.hbs new file mode 100644 index 0000000..88382b1 --- /dev/null +++ b/dockers/vaultwarden/templates/email/emergency_access_invite_accepted.hbs @@ -0,0 +1,8 @@ +Emergency access contact {{{grantee_email}}} accepted + +This email is to notify you that {{grantee_email}} has accepted your invitation to become an emergency access contact. + +To confirm this user, log into the web vault ({{url}}), go to settings and confirm the user. + +If you do not wish to confirm this user, you can also remove them on the same page. +{{> email/email_footer_text }} diff --git a/dockers/vaultwarden/templates/email/emergency_access_invite_accepted.html.hbs b/dockers/vaultwarden/templates/email/emergency_access_invite_accepted.html.hbs new file mode 100644 index 0000000..003b0c1 --- /dev/null +++ b/dockers/vaultwarden/templates/email/emergency_access_invite_accepted.html.hbs @@ -0,0 +1,21 @@ +Emergency access contact {{{grantee_email}}} accepted + +{{> email/email_header }} + + + + + + + + + + +
+ This email is to notify you that {{grantee_email}} has accepted your invitation to become an emergency access contact. +
+ To confirm this user, log into the web vault, go to settings and confirm the user. +
+ If you do not wish to confirm this user, you can also remove them on the same page. +
+{{> email/email_footer }} diff --git a/dockers/vaultwarden/templates/email/emergency_access_invite_confirmed.hbs b/dockers/vaultwarden/templates/email/emergency_access_invite_confirmed.hbs new file mode 100644 index 0000000..1417bb6 --- /dev/null +++ b/dockers/vaultwarden/templates/email/emergency_access_invite_confirmed.hbs @@ -0,0 +1,6 @@ +Emergency access contact for {{{grantor_name}}} confirmed + +This email is to notify you that you have been confirmed as an emergency access contact for *{{grantor_name}}*. + +You can now initiate emergency access requests from the web vault ({{url}}). +{{> email/email_footer_text }} diff --git a/dockers/vaultwarden/templates/email/emergency_access_invite_confirmed.html.hbs b/dockers/vaultwarden/templates/email/emergency_access_invite_confirmed.html.hbs new file mode 100644 index 0000000..a8dd6f6 --- /dev/null +++ b/dockers/vaultwarden/templates/email/emergency_access_invite_confirmed.html.hbs @@ -0,0 +1,16 @@ +Emergency access contact for {{{grantor_name}}} confirmed + +{{> email/email_header }} + + + + + + + +
+ This email is to notify you that you have been confirmed as an emergency access contact for {{grantor_name}}. +
+ You can now initiate emergency access requests from the web vault. +
+{{> email/email_footer }} diff --git a/dockers/vaultwarden/templates/email/emergency_access_recovery_approved.hbs b/dockers/vaultwarden/templates/email/emergency_access_recovery_approved.hbs new file mode 100644 index 0000000..8c2a7cc --- /dev/null +++ b/dockers/vaultwarden/templates/email/emergency_access_recovery_approved.hbs @@ -0,0 +1,4 @@ +Emergency access request for {{{grantor_name}}} approved + +{{grantor_name}} has approved your emergency access request. You may now login on the web vault ({{url}}) and access their account. +{{> email/email_footer_text }} diff --git a/dockers/vaultwarden/templates/email/emergency_access_recovery_approved.html.hbs b/dockers/vaultwarden/templates/email/emergency_access_recovery_approved.html.hbs new file mode 100644 index 0000000..90875f0 --- /dev/null +++ b/dockers/vaultwarden/templates/email/emergency_access_recovery_approved.html.hbs @@ -0,0 +1,11 @@ +Emergency access request for {{{grantor_name}}} approved + +{{> email/email_header }} + + + + +
+ {{grantor_name}} has approved your emergency access request. You may now login on the web vault and access their account. +
+{{> email/email_footer }} diff --git a/dockers/vaultwarden/templates/email/emergency_access_recovery_initiated.hbs b/dockers/vaultwarden/templates/email/emergency_access_recovery_initiated.hbs new file mode 100644 index 0000000..072428a --- /dev/null +++ b/dockers/vaultwarden/templates/email/emergency_access_recovery_initiated.hbs @@ -0,0 +1,6 @@ +Emergency access request by {{{grantee_name}}} initiated + +{{grantee_name}} has initiated an emergency access request to {{atype}} your account. You may login on the web vault ({{url}}) and manually approve or reject this request. + +If you do nothing, the request will automatically be approved after {{wait_time_days}} day(s). +{{> email/email_footer_text }} diff --git a/dockers/vaultwarden/templates/email/emergency_access_recovery_initiated.html.hbs b/dockers/vaultwarden/templates/email/emergency_access_recovery_initiated.html.hbs new file mode 100644 index 0000000..3d84779 --- /dev/null +++ b/dockers/vaultwarden/templates/email/emergency_access_recovery_initiated.html.hbs @@ -0,0 +1,16 @@ +Emergency access request by {{{grantee_name}}} initiated + +{{> email/email_header }} + + + + + + + +
+ {{grantee_name}} has initiated an emergency access request to {{atype}} your account. You may login on the web vault and manually approve or reject this request. +
+ If you do nothing, the request will automatically be approved after {{wait_time_days}} day(s). +
+{{> email/email_footer }} diff --git a/dockers/vaultwarden/templates/email/emergency_access_recovery_rejected.hbs b/dockers/vaultwarden/templates/email/emergency_access_recovery_rejected.hbs new file mode 100644 index 0000000..674a2e5 --- /dev/null +++ b/dockers/vaultwarden/templates/email/emergency_access_recovery_rejected.hbs @@ -0,0 +1,4 @@ +Emergency access request to {{{grantor_name}}} rejected + +{{grantor_name}} has rejected your emergency access request. +{{> email/email_footer_text }} diff --git a/dockers/vaultwarden/templates/email/emergency_access_recovery_rejected.html.hbs b/dockers/vaultwarden/templates/email/emergency_access_recovery_rejected.html.hbs new file mode 100644 index 0000000..69f9ca5 --- /dev/null +++ b/dockers/vaultwarden/templates/email/emergency_access_recovery_rejected.html.hbs @@ -0,0 +1,11 @@ +Emergency access request to {{{grantor_name}}} rejected + +{{> email/email_header }} + + + + +
+ {{grantor_name}} has rejected your emergency access request. +
+{{> email/email_footer }} diff --git a/dockers/vaultwarden/templates/email/emergency_access_recovery_reminder.hbs b/dockers/vaultwarden/templates/email/emergency_access_recovery_reminder.hbs new file mode 100644 index 0000000..c2f912f --- /dev/null +++ b/dockers/vaultwarden/templates/email/emergency_access_recovery_reminder.hbs @@ -0,0 +1,6 @@ +Emergency access request by {{{grantee_name}}} is pending + +{{grantee_name}} has a pending emergency access request to {{atype}} your account. You may login on the web vault ({{url}}) and manually approve or reject this request. + +If you do nothing, the request will automatically be approved after {{days_left}} day(s). +{{> email/email_footer_text }} diff --git a/dockers/vaultwarden/templates/email/emergency_access_recovery_reminder.html.hbs b/dockers/vaultwarden/templates/email/emergency_access_recovery_reminder.html.hbs new file mode 100644 index 0000000..ce2d6b3 --- /dev/null +++ b/dockers/vaultwarden/templates/email/emergency_access_recovery_reminder.html.hbs @@ -0,0 +1,16 @@ +Emergency access request by {{{grantee_name}}} is pending + +{{> email/email_header }} + + + + + + + +
+ {{grantee_name}} has a pending emergency access request to {{atype}} your account. You may login on the web vault and manually approve or reject this request. +
+ If you do nothing, the request will automatically be approved after {{days_left}} day(s). +
+{{> email/email_footer }} diff --git a/dockers/vaultwarden/templates/email/emergency_access_recovery_timed_out.hbs b/dockers/vaultwarden/templates/email/emergency_access_recovery_timed_out.hbs new file mode 100644 index 0000000..a4d564c --- /dev/null +++ b/dockers/vaultwarden/templates/email/emergency_access_recovery_timed_out.hbs @@ -0,0 +1,4 @@ +Emergency access request by {{{grantee_name}}} granted + +{{grantee_name}} has been granted emergency access to {{atype}} your account. You may login on the web vault ({{url}}) and manually revoke this request. +{{> email/email_footer_text }} diff --git a/dockers/vaultwarden/templates/email/emergency_access_recovery_timed_out.html.hbs b/dockers/vaultwarden/templates/email/emergency_access_recovery_timed_out.html.hbs new file mode 100644 index 0000000..74918bb --- /dev/null +++ b/dockers/vaultwarden/templates/email/emergency_access_recovery_timed_out.html.hbs @@ -0,0 +1,11 @@ +Emergency access request by {{{grantee_name}}} granted + +{{> email/email_header }} + + + + +
+ {{grantee_name}} has been granted emergency access to {{atype}} your account. You may login on the web vault and manually revoke this request. +
+{{> email/email_footer }} diff --git a/dockers/vaultwarden/templates/email/incomplete_2fa_login.hbs b/dockers/vaultwarden/templates/email/incomplete_2fa_login.hbs new file mode 100644 index 0000000..d9ff395 --- /dev/null +++ b/dockers/vaultwarden/templates/email/incomplete_2fa_login.hbs @@ -0,0 +1,10 @@ +Incomplete Two-Step Login From {{{device}}} + +Someone attempted to log into your account with the correct master password, but did not provide the correct token or action required to complete the two-step login process within {{time_limit}} minutes of the initial login attempt. + +* Date: {{datetime}} +* IP Address: {{ip}} +* Device Type: {{device}} + +If this was not you or someone you authorized, then you should change your master password as soon as possible, as it is likely to be compromised. +{{> email/email_footer_text }} diff --git a/dockers/vaultwarden/templates/email/incomplete_2fa_login.html.hbs b/dockers/vaultwarden/templates/email/incomplete_2fa_login.html.hbs new file mode 100644 index 0000000..8bc1ce2 --- /dev/null +++ b/dockers/vaultwarden/templates/email/incomplete_2fa_login.html.hbs @@ -0,0 +1,31 @@ +Incomplete Two-Step Login From {{{device}}} + +{{> email/email_header }} + + + + + + + + + + + + + + + + +
+ Someone attempted to log into your account with the correct master password, but did not provide the correct token or action required to complete the two-step login process within {{time_limit}} minutes of the initial login attempt. +
+ Date: {{datetime}} +
+ IP Address: {{ip}} +
+ Device Type: {{device}} +
+ If this was not you or someone you authorized, then you should change your master password as soon as possible, as it is likely to be compromised. +
+{{> email/email_footer }} diff --git a/dockers/vaultwarden/templates/email/invite_accepted.hbs b/dockers/vaultwarden/templates/email/invite_accepted.hbs new file mode 100644 index 0000000..e214857 --- /dev/null +++ b/dockers/vaultwarden/templates/email/invite_accepted.hbs @@ -0,0 +1,5 @@ +Invitation to {{{org_name}}} accepted + +Your invitation for *{{email}}* to join *{{org_name}}* was accepted. +Please log in via {{url}} to the vaultwarden server and confirm them from the organization management page. +{{> email/email_footer_text }} \ No newline at end of file diff --git a/dockers/vaultwarden/templates/email/invite_accepted.html.hbs b/dockers/vaultwarden/templates/email/invite_accepted.html.hbs new file mode 100644 index 0000000..c02fd3a --- /dev/null +++ b/dockers/vaultwarden/templates/email/invite_accepted.html.hbs @@ -0,0 +1,21 @@ +Invitation to {{{org_name}}} accepted + +{{> email/email_header }} + + + + + + + + + + +
+ This email is to notify you that {{email}} has accepted your invitation to join {{org_name}}. +
+ Please log in to the vaultwarden server and confirm them from the organization management page. +
+ If you do not wish to confirm this user, you can also remove them from the organization on the same page. +
+{{> email/email_footer }} \ No newline at end of file diff --git a/dockers/vaultwarden/templates/email/invite_confirmed.hbs b/dockers/vaultwarden/templates/email/invite_confirmed.hbs new file mode 100644 index 0000000..c7ea4f9 --- /dev/null +++ b/dockers/vaultwarden/templates/email/invite_confirmed.hbs @@ -0,0 +1,5 @@ +Invitation to {{{org_name}}} confirmed + +Your invitation to join *{{org_name}}* was confirmed. +It will now appear under the Organizations the next time you log in to the web vault at {{url}}. +{{> email/email_footer_text }} \ No newline at end of file diff --git a/dockers/vaultwarden/templates/email/invite_confirmed.html.hbs b/dockers/vaultwarden/templates/email/invite_confirmed.html.hbs new file mode 100644 index 0000000..d6edb9f --- /dev/null +++ b/dockers/vaultwarden/templates/email/invite_confirmed.html.hbs @@ -0,0 +1,17 @@ +Invitation to {{{org_name}}} confirmed + +{{> email/email_header }} + + + + + + + +
+ This email is to notify you that you have been confirmed as a user of {{org_name}}. +
+ Any collections and logins being shared with you by this organization will now appear in your Vaultwarden vault.
+ Log in +
+{{> email/email_footer }} \ No newline at end of file diff --git a/dockers/vaultwarden/templates/email/new_device_logged_in.hbs b/dockers/vaultwarden/templates/email/new_device_logged_in.hbs new file mode 100644 index 0000000..9734dcb --- /dev/null +++ b/dockers/vaultwarden/templates/email/new_device_logged_in.hbs @@ -0,0 +1,10 @@ +New Device Logged In From {{{device}}} + +Your account was just logged into from a new device. + +* Date: {{datetime}} +* IP Address: {{ip}} +* Device Type: {{device}} + +You can deauthorize all devices that have access to your account from the web vault ( {{url}} ) under Settings > My Account > Deauthorize Sessions. +{{> email/email_footer_text }} \ No newline at end of file diff --git a/dockers/vaultwarden/templates/email/new_device_logged_in.html.hbs b/dockers/vaultwarden/templates/email/new_device_logged_in.html.hbs new file mode 100644 index 0000000..4b8340e --- /dev/null +++ b/dockers/vaultwarden/templates/email/new_device_logged_in.html.hbs @@ -0,0 +1,31 @@ +New Device Logged In From {{{device}}} + +{{> email/email_header }} + + + + + + + + + + + + + + + + +
+ Your account was just logged into from a new device. +
+ Date: {{datetime}} +
+ IP Address: {{ip}} +
+ Device Type: {{device}} +
+ You can deauthorize all devices that have access to your account from the web vault under Settings > My Account > Deauthorize Sessions. +
+{{> email/email_footer }} \ No newline at end of file diff --git a/dockers/vaultwarden/templates/email/pw_hint_none.hbs b/dockers/vaultwarden/templates/email/pw_hint_none.hbs new file mode 100644 index 0000000..88c7465 --- /dev/null +++ b/dockers/vaultwarden/templates/email/pw_hint_none.hbs @@ -0,0 +1,8 @@ +Your master password hint + +You (or someone) recently requested your master password hint. Unfortunately, your account does not have a master password hint. + +If you cannot remember your master password, there is no way to recover your data. The only option to gain access to your account again is to delete the account ( {{url}}/#/recover-delete ) so that you can register again and start over. All data associated with your account will be deleted. + +If you did not request your master password hint you can safely ignore this email. +{{> email/email_footer_text }} \ No newline at end of file diff --git a/dockers/vaultwarden/templates/email/pw_hint_none.html.hbs b/dockers/vaultwarden/templates/email/pw_hint_none.html.hbs new file mode 100644 index 0000000..f9f7b85 --- /dev/null +++ b/dockers/vaultwarden/templates/email/pw_hint_none.html.hbs @@ -0,0 +1,21 @@ +Sorry, you have no password hint... + +{{> email/email_header }} + + + + + + + + + + +
+ You (or someone) recently requested your master password hint. Unfortunately, your account does not have a master password hint.
+
+ If you cannot remember your master password, there is no way to recover your data. The only option to gain access to your account again is to delete the account so that you can register again and start over. All data associated with your account will be deleted. +
+ If you did not request your master password hint you can safely ignore this email. +
+{{> email/email_footer }} \ No newline at end of file diff --git a/dockers/vaultwarden/templates/email/pw_hint_some.hbs b/dockers/vaultwarden/templates/email/pw_hint_some.hbs new file mode 100644 index 0000000..514d43c --- /dev/null +++ b/dockers/vaultwarden/templates/email/pw_hint_some.hbs @@ -0,0 +1,11 @@ +Your master password hint + +You (or someone) recently requested your master password hint. + +Your hint is: *{{hint}}* +Log in to the web vault: {{url}} + +If you cannot remember your master password, there is no way to recover your data. The only option to gain access to your account again is to delete the account ( {{url}}/#/recover-delete ) so that you can register again and start over. All data associated with your account will be deleted. + +If you did not request your master password hint you can safely ignore this email. +{{> email/email_footer_text }} \ No newline at end of file diff --git a/dockers/vaultwarden/templates/email/pw_hint_some.html.hbs b/dockers/vaultwarden/templates/email/pw_hint_some.html.hbs new file mode 100644 index 0000000..095b7aa --- /dev/null +++ b/dockers/vaultwarden/templates/email/pw_hint_some.html.hbs @@ -0,0 +1,27 @@ +Your master password hint + +{{> email/email_header }} + + + + + + + + + + + + + +
+ You (or someone) recently requested your master password hint. +
+ Your hint is: "{{hint}}"
+ Log in: Web Vault +
+ If you cannot remember your master password, there is no way to recover your data. The only option to gain access to your account again is to delete the account so that you can register again and start over. All data associated with your account will be deleted. +
+ If you did not request your master password hint you can safely ignore this email. +
+{{> email/email_footer }} \ No newline at end of file diff --git a/dockers/vaultwarden/templates/email/send_2fa_removed_from_org.hbs b/dockers/vaultwarden/templates/email/send_2fa_removed_from_org.hbs new file mode 100644 index 0000000..95d41b9 --- /dev/null +++ b/dockers/vaultwarden/templates/email/send_2fa_removed_from_org.hbs @@ -0,0 +1,9 @@ +Removed from {{{org_name}}} + +You have been removed from organization *{{org_name}}* because your account does not have Two-step Login enabled. + + +You can enable Two-step Login in your account settings. + +=== +Github: https://github.com/dani-garcia/vaultwarden diff --git a/dockers/vaultwarden/templates/email/send_2fa_removed_from_org.html.hbs b/dockers/vaultwarden/templates/email/send_2fa_removed_from_org.html.hbs new file mode 100644 index 0000000..6588a32 --- /dev/null +++ b/dockers/vaultwarden/templates/email/send_2fa_removed_from_org.html.hbs @@ -0,0 +1,16 @@ +Removed from {{{org_name}}} + +{{> email/email_header }} + + + + + + + +
+ You have been removed from organization {{org_name}} because your account does not have Two-step Login enabled. +
+ You can enable Two-step Login in your account settings. +
+{{> email/email_footer }} diff --git a/dockers/vaultwarden/templates/email/send_emergency_access_invite.hbs b/dockers/vaultwarden/templates/email/send_emergency_access_invite.hbs new file mode 100644 index 0000000..9dc114c --- /dev/null +++ b/dockers/vaultwarden/templates/email/send_emergency_access_invite.hbs @@ -0,0 +1,8 @@ +Emergency access for {{{grantor_name}}} + +You have been invited to become an emergency contact for {{grantor_name}}. To accept this invite, click the following link: + +Click here to join: {{url}}/#/accept-emergency/?id={{emer_id}}&name={{grantor_name}}&email={{email}}&token={{token}} + +If you do not wish to become an emergency contact for {{grantor_name}}, you can safely ignore this email. +{{> email/email_footer_text }} diff --git a/dockers/vaultwarden/templates/email/send_emergency_access_invite.html.hbs b/dockers/vaultwarden/templates/email/send_emergency_access_invite.html.hbs new file mode 100644 index 0000000..fd1c040 --- /dev/null +++ b/dockers/vaultwarden/templates/email/send_emergency_access_invite.html.hbs @@ -0,0 +1,24 @@ +Emergency access for {{{grantor_name}}} + +{{> email/email_header }} + + + + + + + + + + +
+ You have been invited to become an emergency contact for {{grantor_name}}. +
+ + Become emergency contact + +
+ If you do not wish to become an emergency contact for {{grantor_name}}, you can safely ignore this email. +
+{{> email/email_footer }} \ No newline at end of file diff --git a/dockers/vaultwarden/templates/email/send_org_invite.hbs b/dockers/vaultwarden/templates/email/send_org_invite.hbs new file mode 100644 index 0000000..be94d0e --- /dev/null +++ b/dockers/vaultwarden/templates/email/send_org_invite.hbs @@ -0,0 +1,10 @@ +Join {{{org_name}}} + +You have been invited to join the *{{org_name}}* organization. + + +Click here to join: {{url}}/#/accept-organization/?organizationId={{org_id}}&organizationUserId={{org_user_id}}&email={{email}}&organizationName={{org_name_encoded}}&token={{token}} + + +If you do not wish to join this organization, you can safely ignore this email. +{{> email/email_footer_text }} \ No newline at end of file diff --git a/dockers/vaultwarden/templates/email/send_org_invite.html.hbs b/dockers/vaultwarden/templates/email/send_org_invite.html.hbs new file mode 100644 index 0000000..55b9814 --- /dev/null +++ b/dockers/vaultwarden/templates/email/send_org_invite.html.hbs @@ -0,0 +1,24 @@ +Join {{{org_name}}} + +{{> email/email_header }} + + + + + + + + + + +
+ You have been invited to join the {{org_name}} organization. +
+ + Join Organization Now + +
+ If you do not wish to join this organization, you can safely ignore this email. +
+{{> email/email_footer }} \ No newline at end of file diff --git a/dockers/vaultwarden/templates/email/send_single_org_removed_from_org.hbs b/dockers/vaultwarden/templates/email/send_single_org_removed_from_org.hbs new file mode 100644 index 0000000..0686d98 --- /dev/null +++ b/dockers/vaultwarden/templates/email/send_single_org_removed_from_org.hbs @@ -0,0 +1,5 @@ +You have been removed from {{{org_name}}} + +Your user account has been removed from the *{{org_name}}* organization because you are a part of another organization. The {{org_name}} organization has enabled a policy that prevents users from being a part of multiple organizations. Before you can re-join this organization you need to leave all other organizations or join with a different account. +=== +Github: https://github.com/dani-garcia/vaultwarden diff --git a/dockers/vaultwarden/templates/email/send_single_org_removed_from_org.html.hbs b/dockers/vaultwarden/templates/email/send_single_org_removed_from_org.html.hbs new file mode 100644 index 0000000..e402662 --- /dev/null +++ b/dockers/vaultwarden/templates/email/send_single_org_removed_from_org.html.hbs @@ -0,0 +1,11 @@ +You have been removed from {{{org_name}}} + +{{> email/email_header }} + + + + +
+ Your user account has been removed from the {{org_name}} organization because you are a part of another organization. The {{org_name}} organization has enabled a policy that prevents users from being a part of multiple organizations. Before you can re-join this organization you need to leave all other organizations or join with a different account. +
+{{> email/email_footer }} diff --git a/dockers/vaultwarden/templates/email/smtp_test.hbs b/dockers/vaultwarden/templates/email/smtp_test.hbs new file mode 100644 index 0000000..f177b09 --- /dev/null +++ b/dockers/vaultwarden/templates/email/smtp_test.hbs @@ -0,0 +1,6 @@ +Vaultwarden SMTP Test + +This is a test email to verify the SMTP configuration for {{url}}. + +When you can read this email it is probably configured correctly. +{{> email/email_footer_text }} \ No newline at end of file diff --git a/dockers/vaultwarden/templates/email/smtp_test.html.hbs b/dockers/vaultwarden/templates/email/smtp_test.html.hbs new file mode 100644 index 0000000..36653f5 --- /dev/null +++ b/dockers/vaultwarden/templates/email/smtp_test.html.hbs @@ -0,0 +1,16 @@ +Vaultwarden SMTP Test + +{{> email/email_header }} + + + + + + + +
+ This is a test email to verify the SMTP configuration for {{url}}. +
+ When you can read this email it is probably configured correctly. +
+{{> email/email_footer }} \ No newline at end of file diff --git a/dockers/vaultwarden/templates/email/twofactor_email.hbs b/dockers/vaultwarden/templates/email/twofactor_email.hbs new file mode 100644 index 0000000..ae24df6 --- /dev/null +++ b/dockers/vaultwarden/templates/email/twofactor_email.hbs @@ -0,0 +1,6 @@ +Your Two-step Login Verification Code + +Your two-step verification code is: {{token}} + +Use this code to complete logging in with Vaultwarden. +{{> email/email_footer_text }} \ No newline at end of file diff --git a/dockers/vaultwarden/templates/email/twofactor_email.html.hbs b/dockers/vaultwarden/templates/email/twofactor_email.html.hbs new file mode 100644 index 0000000..fb21dc6 --- /dev/null +++ b/dockers/vaultwarden/templates/email/twofactor_email.html.hbs @@ -0,0 +1,16 @@ +Your Two-step Login Verification Code + +{{> email/email_header }} + + + + + + + +
+ Your two-step verification code is: {{token}} +
+ Use this code to complete logging in with Vaultwarden. +
+{{> email/email_footer }} \ No newline at end of file diff --git a/dockers/vaultwarden/templates/email/verify_email.hbs b/dockers/vaultwarden/templates/email/verify_email.hbs new file mode 100644 index 0000000..5c8dd55 --- /dev/null +++ b/dockers/vaultwarden/templates/email/verify_email.hbs @@ -0,0 +1,8 @@ +Verify Your Email + +Verify this email address for your account by clicking the link below. + +Verify Email Address Now: {{url}}/#/verify-email/?userId={{user_id}}&token={{token}} + +If you did not request to verify your account, you can safely ignore this email. +{{> email/email_footer_text }} \ No newline at end of file diff --git a/dockers/vaultwarden/templates/email/verify_email.html.hbs b/dockers/vaultwarden/templates/email/verify_email.html.hbs new file mode 100644 index 0000000..c37cf36 --- /dev/null +++ b/dockers/vaultwarden/templates/email/verify_email.html.hbs @@ -0,0 +1,24 @@ +Verify Your Email + +{{> email/email_header }} + + + + + + + + + + +
+ Verify this email address for your account by clicking the link below. +
+ + Verify Email Address Now + +
+ If you did not request to verify your account, you can safely ignore this email. +
+{{> email/email_footer }} \ No newline at end of file diff --git a/dockers/vaultwarden/templates/email/welcome.hbs b/dockers/vaultwarden/templates/email/welcome.hbs new file mode 100644 index 0000000..f08e9be --- /dev/null +++ b/dockers/vaultwarden/templates/email/welcome.hbs @@ -0,0 +1,6 @@ +Welcome + +Thank you for creating an account at {{url}}. You may now log in with your new account. + +If you did not request to create an account, you can safely ignore this email. +{{> email/email_footer_text }} \ No newline at end of file diff --git a/dockers/vaultwarden/templates/email/welcome.html.hbs b/dockers/vaultwarden/templates/email/welcome.html.hbs new file mode 100644 index 0000000..ad7ff2f --- /dev/null +++ b/dockers/vaultwarden/templates/email/welcome.html.hbs @@ -0,0 +1,16 @@ +Welcome + +{{> email/email_header }} + + + + + + + +
+ Thank you for creating an account at {{url}}. You may now log in with your new account. +
+ If you did not request to create an account, you can safely ignore this email. +
+{{> email/email_footer }} \ No newline at end of file diff --git a/dockers/vaultwarden/templates/email/welcome_must_verify.hbs b/dockers/vaultwarden/templates/email/welcome_must_verify.hbs new file mode 100644 index 0000000..7602844 --- /dev/null +++ b/dockers/vaultwarden/templates/email/welcome_must_verify.hbs @@ -0,0 +1,8 @@ +Bienvenue + +Merci d'avoir créé un compte à {{url}}. Avant de pouvoir vous connecter vous devez valider votre adresse mail en cliquant sur le lien ci-dessous. + +Vérifiez votre adresse mail maintenant: {{url}}/#/verify-email/?userId={{user_id}}&token={{token}} + +Si vous n'avez pas fait de demande de création de compte, vous pouvez ignorer cet email. +{{> email/email_footer_text }} diff --git a/dockers/vaultwarden/templates/email/welcome_must_verify.html.hbs b/dockers/vaultwarden/templates/email/welcome_must_verify.html.hbs new file mode 100644 index 0000000..086c69a --- /dev/null +++ b/dockers/vaultwarden/templates/email/welcome_must_verify.html.hbs @@ -0,0 +1,24 @@ +Bienvenue + +{{> email/email_header }} + + + + + + + + + + +
+ Merci d'avoir créé un compte à {{url}}. Avant de pouvoir vous connecter vous devez valider votre adresse mail en cliquant sur le lien ci-dessous. +
+ + Vérifiez votre adresse mail maintenant + +
+ Si vous n'avez pas fait de demande de création de compte, vous pouvez ignorer cet email. +
+{{> email/email_footer }} diff --git a/dockers/web/.env b/dockers/web/.env new file mode 120000 index 0000000..406acd1 --- /dev/null +++ b/dockers/web/.env @@ -0,0 +1 @@ +../../config/dockers.env \ No newline at end of file diff --git a/dockers/web/Readme.txt b/dockers/web/Readme.txt new file mode 100644 index 0000000..5f26c3e --- /dev/null +++ b/dockers/web/Readme.txt @@ -0,0 +1,29 @@ +Pour l'installation d'un serveur web static (avec NGINX) + +0) Verification qu'il n'y a pas deja une image + +# docker images + +# cd /root/install_web + +1) fabrication du docker avec le fichier Dockerfile +Cela utilise + - Dockerfile : le fichier de description + - html : qui contient les pages static + +# docker build -t webkaz . + +2) verification de la creation (webkaz apparait) +# docker images + +3) Lancement du web +Dans docker-compose.yml + - il y a nommage du container + +# docker-compose up -d + +4) Verification +Il y a un container + +# docker ps + diff --git a/dockers/web/docker-compose.yml b/dockers/web/docker-compose.yml new file mode 100644 index 0000000..e27c09f --- /dev/null +++ b/dockers/web/docker-compose.yml @@ -0,0 +1,27 @@ +version: '3' +services: + web: + image: nginx + container_name: ${webServName} + restart: ${restartPolicy} + # ports: + # - 8080:80 + networks: + - webNet + volumes: + - html:/usr/share/nginx/html/ + - config:/etc/nginx/ + - /etc/localtime:/etc/localtime:ro + - /etc/timezone:/etc/timezone:ro + labels: + - "traefik.enable=true" + - "traefik.http.routers.web.rule=Host(`${domain}`,`www.${domain}`)" + +volumes: + html: + config: + +networks: + webNet: + external: true + name: webNet diff --git a/dockers/web/first.sh b/dockers/web/first.sh new file mode 100755 index 0000000..0662e42 --- /dev/null +++ b/dockers/web/first.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +KAZ_ROOT=$(cd $(dirname $0)/../..; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +cd $(dirname $0) +. "${DOCKERS_ENV}" + +WEB_URL="${httpProto}://${domain}" + +checkDockerRunning "${webServName}" "Web" || exit + +if [ $(ls "${DOCK_VOL}/web_html/_data/" 2>/dev/null | wc -l) -le 4 ] ; then + printKazMsg "\n *** Premier lancement de WEB" + + rsync -aAHXh --info=progress2 ./html/ "${DOCK_VOL}/web_html/_data/" + chown -R www-data: "${DOCK_VOL}/web_html/_data/" +fi diff --git a/dockers/web/html/LICENSE.txt b/dockers/web/html/LICENSE.txt new file mode 100644 index 0000000..d447b56 --- /dev/null +++ b/dockers/web/html/LICENSE.txt @@ -0,0 +1,63 @@ +Creative Commons Attribution 3.0 Unported +http://creativecommons.org/licenses/by/3.0/ + +License + +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. + +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. + +1. Definitions + + 1. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License. + 2. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License. + 3. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership. + 4. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License. + 5. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast. + 6. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work. + 7. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. + 8. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images. + 9. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium. + +2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws. + +3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: + + 1. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; + 2. to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified."; + 3. to Distribute and Publicly Perform the Work including as incorporated in Collections; and, + 4. to Distribute and Publicly Perform Adaptations. + 5. + + For the avoidance of doubt: + 1. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; + 2. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and, + 3. Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License. + +The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved. + +4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: + + 1. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(b), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(b), as requested. + 2. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4 (b) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties. + 3. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise. + +5. Representations, Warranties and Disclaimer + +UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. + +6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. Termination + + 1. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. + 2. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. + +8. Miscellaneous + + 1. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. + 2. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License. + 3. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + 4. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. + 5. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You. + 6. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law. diff --git a/dockers/web/html/README.txt b/dockers/web/html/README.txt new file mode 100644 index 0000000..c6d09dc --- /dev/null +++ b/dockers/web/html/README.txt @@ -0,0 +1,38 @@ +Phantom by HTML5 UP +html5up.net | @ajlkn +Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) + + +This is Phantom, a simple design built around a grid of large, colorful, semi-interactive +image tiles (of which you can have as many or as few as you like). Makes use of some +SVG and animation techniques I've been experimenting with on that other project of mine +you may have heard about (https://carrd.co), and includes a handy generic page for whatever. + +Demo images* courtesy of Unsplash, a radtastic collection of CC0 (public domain) images +you can use for pretty much whatever. + +(* = not included) + +AJ +aj@lkn.io | @ajlkn + + +Credits: + + Demo Images: + Unsplash (unsplash.com) + + Icons: + Font Awesome (fontawesome.io) + + Other: + jQuery (jquery.com) + Responsive Tools (github.com/ajlkn/responsive-tools) + + + +********** KAZ ***************** +un peu de js pour inclure les header/footer/menu/... à la volée +https://github.com/Paul-Browne/HTMLInclude + +test du 09/09/2021 diff --git a/dockers/web/html/actualite.html b/dockers/web/html/actualite.html new file mode 100644 index 0000000..febc02a --- /dev/null +++ b/dockers/web/html/actualite.html @@ -0,0 +1,113 @@ + + + + + Cochez la KAZ du libre! + + + + + + + + +
+ + +
+ + + + + +
+
+

Actualités

+ + +
+ +
+

12 Novembre 2021

+
+
Didier, de la collégiale (encore lui ;) ) se fait interviewer par Mooréa Lahalle du Télégramme: cliquez ici +
La collégiale.
+
+
+ +
+

13 Octobre 2021

+
+
Pour lire la seconde newsletter: cliquez ici +
La collégiale.
+
+
+ + + +
+

2 Juillet 2021

+
+
Pour lire notre toute première newsletter: cliquez ici +
La collégiale.
+
+
+ +
+

21 Juin 2021

+
+
Nous sommes +officiellement +membre +du +collectifs +CHATONS! +
+en +avant +la +musique! + +
La collégiale.
+
+
+ +
+

1er Mars 2021

+
+
Nous sommes très heureux d'annoncer l'ouverture officielle des services KAZ!!! +
La collégiale.
+
+
+ +
+ +
+ + +
+ + +
+ +
+ + + + + + + + + + + diff --git a/dockers/web/html/assets/css/fontawesome-all.min.css b/dockers/web/html/assets/css/fontawesome-all.min.css new file mode 100644 index 0000000..b7d052b --- /dev/null +++ b/dockers/web/html/assets/css/fontawesome-all.min.css @@ -0,0 +1,5 @@ +/*! + * Font Awesome Free 5.9.0 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + */ +.fa,.fab,.fal,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.fa-pull-right{margin-left:.3em}.fa-spin{animation:fa-spin 2s infinite linear}.fa-pulse{animation:fa-spin 1s infinite steps(8)}@keyframes fa-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";transform:scaleX(-1)}.fa-flip-vertical{transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical,.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{transform:scale(-1)}:root .fa-flip-both,:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-500px:before{content:"\f26e"}.fa-accessible-icon:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-acquisitions-incorporated:before{content:"\f6af"}.fa-ad:before{content:"\f641"}.fa-address-book:before{content:"\f2b9"}.fa-address-card:before{content:"\f2bb"}.fa-adjust:before{content:"\f042"}.fa-adn:before{content:"\f170"}.fa-adobe:before{content:"\f778"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-air-freshener:before{content:"\f5d0"}.fa-airbnb:before{content:"\f834"}.fa-algolia:before{content:"\f36c"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-alipay:before{content:"\f642"}.fa-allergies:before{content:"\f461"}.fa-amazon:before{content:"\f270"}.fa-amazon-pay:before{content:"\f42c"}.fa-ambulance:before{content:"\f0f9"}.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-amilia:before{content:"\f36d"}.fa-anchor:before{content:"\f13d"}.fa-android:before{content:"\f17b"}.fa-angellist:before{content:"\f209"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-down:before{content:"\f107"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angry:before{content:"\f556"}.fa-angrycreative:before{content:"\f36e"}.fa-angular:before{content:"\f420"}.fa-ankh:before{content:"\f644"}.fa-app-store:before{content:"\f36f"}.fa-app-store-ios:before{content:"\f370"}.fa-apper:before{content:"\f371"}.fa-apple:before{content:"\f179"}.fa-apple-alt:before{content:"\f5d1"}.fa-apple-pay:before{content:"\f415"}.fa-archive:before{content:"\f187"}.fa-archway:before{content:"\f557"}.fa-arrow-alt-circle-down:before{content:"\f358"}.fa-arrow-alt-circle-left:before{content:"\f359"}.fa-arrow-alt-circle-right:before{content:"\f35a"}.fa-arrow-alt-circle-up:before{content:"\f35b"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-down:before{content:"\f063"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrows-alt:before{content:"\f0b2"}.fa-arrows-alt-h:before{content:"\f337"}.fa-arrows-alt-v:before{content:"\f338"}.fa-artstation:before{content:"\f77a"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asterisk:before{content:"\f069"}.fa-asymmetrik:before{content:"\f372"}.fa-at:before{content:"\f1fa"}.fa-atlas:before{content:"\f558"}.fa-atlassian:before{content:"\f77b"}.fa-atom:before{content:"\f5d2"}.fa-audible:before{content:"\f373"}.fa-audio-description:before{content:"\f29e"}.fa-autoprefixer:before{content:"\f41c"}.fa-avianex:before{content:"\f374"}.fa-aviato:before{content:"\f421"}.fa-award:before{content:"\f559"}.fa-aws:before{content:"\f375"}.fa-baby:before{content:"\f77c"}.fa-baby-carriage:before{content:"\f77d"}.fa-backspace:before{content:"\f55a"}.fa-backward:before{content:"\f04a"}.fa-bacon:before{content:"\f7e5"}.fa-balance-scale:before{content:"\f24e"}.fa-balance-scale-left:before{content:"\f515"}.fa-balance-scale-right:before{content:"\f516"}.fa-ban:before{content:"\f05e"}.fa-band-aid:before{content:"\f462"}.fa-bandcamp:before{content:"\f2d5"}.fa-barcode:before{content:"\f02a"}.fa-bars:before{content:"\f0c9"}.fa-baseball-ball:before{content:"\f433"}.fa-basketball-ball:before{content:"\f434"}.fa-bath:before{content:"\f2cd"}.fa-battery-empty:before{content:"\f244"}.fa-battery-full:before{content:"\f240"}.fa-battery-half:before{content:"\f242"}.fa-battery-quarter:before{content:"\f243"}.fa-battery-three-quarters:before{content:"\f241"}.fa-battle-net:before{content:"\f835"}.fa-bed:before{content:"\f236"}.fa-beer:before{content:"\f0fc"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-bell:before{content:"\f0f3"}.fa-bell-slash:before{content:"\f1f6"}.fa-bezier-curve:before{content:"\f55b"}.fa-bible:before{content:"\f647"}.fa-bicycle:before{content:"\f206"}.fa-biking:before{content:"\f84a"}.fa-bimobject:before{content:"\f378"}.fa-binoculars:before{content:"\f1e5"}.fa-biohazard:before{content:"\f780"}.fa-birthday-cake:before{content:"\f1fd"}.fa-bitbucket:before{content:"\f171"}.fa-bitcoin:before{content:"\f379"}.fa-bity:before{content:"\f37a"}.fa-black-tie:before{content:"\f27e"}.fa-blackberry:before{content:"\f37b"}.fa-blender:before{content:"\f517"}.fa-blender-phone:before{content:"\f6b6"}.fa-blind:before{content:"\f29d"}.fa-blog:before{content:"\f781"}.fa-blogger:before{content:"\f37c"}.fa-blogger-b:before{content:"\f37d"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-bold:before{content:"\f032"}.fa-bolt:before{content:"\f0e7"}.fa-bomb:before{content:"\f1e2"}.fa-bone:before{content:"\f5d7"}.fa-bong:before{content:"\f55c"}.fa-book:before{content:"\f02d"}.fa-book-dead:before{content:"\f6b7"}.fa-book-medical:before{content:"\f7e6"}.fa-book-open:before{content:"\f518"}.fa-book-reader:before{content:"\f5da"}.fa-bookmark:before{content:"\f02e"}.fa-bootstrap:before{content:"\f836"}.fa-border-all:before{content:"\f84c"}.fa-border-none:before{content:"\f850"}.fa-border-style:before{content:"\f853"}.fa-bowling-ball:before{content:"\f436"}.fa-box:before{content:"\f466"}.fa-box-open:before{content:"\f49e"}.fa-boxes:before{content:"\f468"}.fa-braille:before{content:"\f2a1"}.fa-brain:before{content:"\f5dc"}.fa-bread-slice:before{content:"\f7ec"}.fa-briefcase:before{content:"\f0b1"}.fa-briefcase-medical:before{content:"\f469"}.fa-broadcast-tower:before{content:"\f519"}.fa-broom:before{content:"\f51a"}.fa-brush:before{content:"\f55d"}.fa-btc:before{content:"\f15a"}.fa-buffer:before{content:"\f837"}.fa-bug:before{content:"\f188"}.fa-building:before{content:"\f1ad"}.fa-bullhorn:before{content:"\f0a1"}.fa-bullseye:before{content:"\f140"}.fa-burn:before{content:"\f46a"}.fa-buromobelexperte:before{content:"\f37f"}.fa-bus:before{content:"\f207"}.fa-bus-alt:before{content:"\f55e"}.fa-business-time:before{content:"\f64a"}.fa-buysellads:before{content:"\f20d"}.fa-calculator:before{content:"\f1ec"}.fa-calendar:before{content:"\f133"}.fa-calendar-alt:before{content:"\f073"}.fa-calendar-check:before{content:"\f274"}.fa-calendar-day:before{content:"\f783"}.fa-calendar-minus:before{content:"\f272"}.fa-calendar-plus:before{content:"\f271"}.fa-calendar-times:before{content:"\f273"}.fa-calendar-week:before{content:"\f784"}.fa-camera:before{content:"\f030"}.fa-camera-retro:before{content:"\f083"}.fa-campground:before{content:"\f6bb"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-candy-cane:before{content:"\f786"}.fa-cannabis:before{content:"\f55f"}.fa-capsules:before{content:"\f46b"}.fa-car:before{content:"\f1b9"}.fa-car-alt:before{content:"\f5de"}.fa-car-battery:before{content:"\f5df"}.fa-car-crash:before{content:"\f5e1"}.fa-car-side:before{content:"\f5e4"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-caret-square-down:before{content:"\f150"}.fa-caret-square-left:before{content:"\f191"}.fa-caret-square-right:before{content:"\f152"}.fa-caret-square-up:before{content:"\f151"}.fa-caret-up:before{content:"\f0d8"}.fa-carrot:before{content:"\f787"}.fa-cart-arrow-down:before{content:"\f218"}.fa-cart-plus:before{content:"\f217"}.fa-cash-register:before{content:"\f788"}.fa-cat:before{content:"\f6be"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-apple-pay:before{content:"\f416"}.fa-cc-diners-club:before{content:"\f24c"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-cc-visa:before{content:"\f1f0"}.fa-centercode:before{content:"\f380"}.fa-centos:before{content:"\f789"}.fa-certificate:before{content:"\f0a3"}.fa-chair:before{content:"\f6c0"}.fa-chalkboard:before{content:"\f51b"}.fa-chalkboard-teacher:before{content:"\f51c"}.fa-charging-station:before{content:"\f5e7"}.fa-chart-area:before{content:"\f1fe"}.fa-chart-bar:before{content:"\f080"}.fa-chart-line:before{content:"\f201"}.fa-chart-pie:before{content:"\f200"}.fa-check:before{content:"\f00c"}.fa-check-circle:before{content:"\f058"}.fa-check-double:before{content:"\f560"}.fa-check-square:before{content:"\f14a"}.fa-cheese:before{content:"\f7ef"}.fa-chess:before{content:"\f439"}.fa-chess-bishop:before{content:"\f43a"}.fa-chess-board:before{content:"\f43c"}.fa-chess-king:before{content:"\f43f"}.fa-chess-knight:before{content:"\f441"}.fa-chess-pawn:before{content:"\f443"}.fa-chess-queen:before{content:"\f445"}.fa-chess-rook:before{content:"\f447"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-down:before{content:"\f078"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-chevron-up:before{content:"\f077"}.fa-child:before{content:"\f1ae"}.fa-chrome:before{content:"\f268"}.fa-chromecast:before{content:"\f838"}.fa-church:before{content:"\f51d"}.fa-circle:before{content:"\f111"}.fa-circle-notch:before{content:"\f1ce"}.fa-city:before{content:"\f64f"}.fa-clinic-medical:before{content:"\f7f2"}.fa-clipboard:before{content:"\f328"}.fa-clipboard-check:before{content:"\f46c"}.fa-clipboard-list:before{content:"\f46d"}.fa-clock:before{content:"\f017"}.fa-clone:before{content:"\f24d"}.fa-closed-captioning:before{content:"\f20a"}.fa-cloud:before{content:"\f0c2"}.fa-cloud-download-alt:before{content:"\f381"}.fa-cloud-meatball:before{content:"\f73b"}.fa-cloud-moon:before{content:"\f6c3"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-cloud-rain:before{content:"\f73d"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-cloud-sun:before{content:"\f6c4"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-cloud-upload-alt:before{content:"\f382"}.fa-cloudscale:before{content:"\f383"}.fa-cloudsmith:before{content:"\f384"}.fa-cloudversify:before{content:"\f385"}.fa-cocktail:before{content:"\f561"}.fa-code:before{content:"\f121"}.fa-code-branch:before{content:"\f126"}.fa-codepen:before{content:"\f1cb"}.fa-codiepie:before{content:"\f284"}.fa-coffee:before{content:"\f0f4"}.fa-cog:before{content:"\f013"}.fa-cogs:before{content:"\f085"}.fa-coins:before{content:"\f51e"}.fa-columns:before{content:"\f0db"}.fa-comment:before{content:"\f075"}.fa-comment-alt:before{content:"\f27a"}.fa-comment-dollar:before{content:"\f651"}.fa-comment-dots:before{content:"\f4ad"}.fa-comment-medical:before{content:"\f7f5"}.fa-comment-slash:before{content:"\f4b3"}.fa-comments:before{content:"\f086"}.fa-comments-dollar:before{content:"\f653"}.fa-compact-disc:before{content:"\f51f"}.fa-compass:before{content:"\f14e"}.fa-compress:before{content:"\f066"}.fa-compress-arrows-alt:before{content:"\f78c"}.fa-concierge-bell:before{content:"\f562"}.fa-confluence:before{content:"\f78d"}.fa-connectdevelop:before{content:"\f20e"}.fa-contao:before{content:"\f26d"}.fa-cookie:before{content:"\f563"}.fa-cookie-bite:before{content:"\f564"}.fa-copy:before{content:"\f0c5"}.fa-copyright:before{content:"\f1f9"}.fa-couch:before{content:"\f4b8"}.fa-cpanel:before{content:"\f388"}.fa-creative-commons:before{content:"\f25e"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-credit-card:before{content:"\f09d"}.fa-critical-role:before{content:"\f6c9"}.fa-crop:before{content:"\f125"}.fa-crop-alt:before{content:"\f565"}.fa-cross:before{content:"\f654"}.fa-crosshairs:before{content:"\f05b"}.fa-crow:before{content:"\f520"}.fa-crown:before{content:"\f521"}.fa-crutch:before{content:"\f7f7"}.fa-css3:before{content:"\f13c"}.fa-css3-alt:before{content:"\f38b"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-cut:before{content:"\f0c4"}.fa-cuttlefish:before{content:"\f38c"}.fa-d-and-d:before{content:"\f38d"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-dashcube:before{content:"\f210"}.fa-database:before{content:"\f1c0"}.fa-deaf:before{content:"\f2a4"}.fa-delicious:before{content:"\f1a5"}.fa-democrat:before{content:"\f747"}.fa-deploydog:before{content:"\f38e"}.fa-deskpro:before{content:"\f38f"}.fa-desktop:before{content:"\f108"}.fa-dev:before{content:"\f6cc"}.fa-deviantart:before{content:"\f1bd"}.fa-dharmachakra:before{content:"\f655"}.fa-dhl:before{content:"\f790"}.fa-diagnoses:before{content:"\f470"}.fa-diaspora:before{content:"\f791"}.fa-dice:before{content:"\f522"}.fa-dice-d20:before{content:"\f6cf"}.fa-dice-d6:before{content:"\f6d1"}.fa-dice-five:before{content:"\f523"}.fa-dice-four:before{content:"\f524"}.fa-dice-one:before{content:"\f525"}.fa-dice-six:before{content:"\f526"}.fa-dice-three:before{content:"\f527"}.fa-dice-two:before{content:"\f528"}.fa-digg:before{content:"\f1a6"}.fa-digital-ocean:before{content:"\f391"}.fa-digital-tachograph:before{content:"\f566"}.fa-directions:before{content:"\f5eb"}.fa-discord:before{content:"\f392"}.fa-discourse:before{content:"\f393"}.fa-divide:before{content:"\f529"}.fa-dizzy:before{content:"\f567"}.fa-dna:before{content:"\f471"}.fa-dochub:before{content:"\f394"}.fa-docker:before{content:"\f395"}.fa-dog:before{content:"\f6d3"}.fa-dollar-sign:before{content:"\f155"}.fa-dolly:before{content:"\f472"}.fa-dolly-flatbed:before{content:"\f474"}.fa-donate:before{content:"\f4b9"}.fa-door-closed:before{content:"\f52a"}.fa-door-open:before{content:"\f52b"}.fa-dot-circle:before{content:"\f192"}.fa-dove:before{content:"\f4ba"}.fa-download:before{content:"\f019"}.fa-draft2digital:before{content:"\f396"}.fa-drafting-compass:before{content:"\f568"}.fa-dragon:before{content:"\f6d5"}.fa-draw-polygon:before{content:"\f5ee"}.fa-dribbble:before{content:"\f17d"}.fa-dribbble-square:before{content:"\f397"}.fa-dropbox:before{content:"\f16b"}.fa-drum:before{content:"\f569"}.fa-drum-steelpan:before{content:"\f56a"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-drupal:before{content:"\f1a9"}.fa-dumbbell:before{content:"\f44b"}.fa-dumpster:before{content:"\f793"}.fa-dumpster-fire:before{content:"\f794"}.fa-dungeon:before{content:"\f6d9"}.fa-dyalog:before{content:"\f399"}.fa-earlybirds:before{content:"\f39a"}.fa-ebay:before{content:"\f4f4"}.fa-edge:before{content:"\f282"}.fa-edit:before{content:"\f044"}.fa-egg:before{content:"\f7fb"}.fa-eject:before{content:"\f052"}.fa-elementor:before{content:"\f430"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-ello:before{content:"\f5f1"}.fa-ember:before{content:"\f423"}.fa-empire:before{content:"\f1d1"}.fa-envelope:before{content:"\f0e0"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-text:before{content:"\f658"}.fa-envelope-square:before{content:"\f199"}.fa-envira:before{content:"\f299"}.fa-equals:before{content:"\f52c"}.fa-eraser:before{content:"\f12d"}.fa-erlang:before{content:"\f39d"}.fa-ethereum:before{content:"\f42e"}.fa-ethernet:before{content:"\f796"}.fa-etsy:before{content:"\f2d7"}.fa-euro-sign:before{content:"\f153"}.fa-evernote:before{content:"\f839"}.fa-exchange-alt:before{content:"\f362"}.fa-exclamation:before{content:"\f12a"}.fa-exclamation-circle:before{content:"\f06a"}.fa-exclamation-triangle:before{content:"\f071"}.fa-expand:before{content:"\f065"}.fa-expand-arrows-alt:before{content:"\f31e"}.fa-expeditedssl:before{content:"\f23e"}.fa-external-link-alt:before{content:"\f35d"}.fa-external-link-square-alt:before{content:"\f360"}.fa-eye:before{content:"\f06e"}.fa-eye-dropper:before{content:"\f1fb"}.fa-eye-slash:before{content:"\f070"}.fa-facebook:before{content:"\f09a"}.fa-facebook-f:before{content:"\f39e"}.fa-facebook-messenger:before{content:"\f39f"}.fa-facebook-square:before{content:"\f082"}.fa-fan:before{content:"\f863"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-fast-backward:before{content:"\f049"}.fa-fast-forward:before{content:"\f050"}.fa-fax:before{content:"\f1ac"}.fa-feather:before{content:"\f52d"}.fa-feather-alt:before{content:"\f56b"}.fa-fedex:before{content:"\f797"}.fa-fedora:before{content:"\f798"}.fa-female:before{content:"\f182"}.fa-fighter-jet:before{content:"\f0fb"}.fa-figma:before{content:"\f799"}.fa-file:before{content:"\f15b"}.fa-file-alt:before{content:"\f15c"}.fa-file-archive:before{content:"\f1c6"}.fa-file-audio:before{content:"\f1c7"}.fa-file-code:before{content:"\f1c9"}.fa-file-contract:before{content:"\f56c"}.fa-file-csv:before{content:"\f6dd"}.fa-file-download:before{content:"\f56d"}.fa-file-excel:before{content:"\f1c3"}.fa-file-export:before{content:"\f56e"}.fa-file-image:before{content:"\f1c5"}.fa-file-import:before{content:"\f56f"}.fa-file-invoice:before{content:"\f570"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-file-medical:before{content:"\f477"}.fa-file-medical-alt:before{content:"\f478"}.fa-file-pdf:before{content:"\f1c1"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-file-prescription:before{content:"\f572"}.fa-file-signature:before{content:"\f573"}.fa-file-upload:before{content:"\f574"}.fa-file-video:before{content:"\f1c8"}.fa-file-word:before{content:"\f1c2"}.fa-fill:before{content:"\f575"}.fa-fill-drip:before{content:"\f576"}.fa-film:before{content:"\f008"}.fa-filter:before{content:"\f0b0"}.fa-fingerprint:before{content:"\f577"}.fa-fire:before{content:"\f06d"}.fa-fire-alt:before{content:"\f7e4"}.fa-fire-extinguisher:before{content:"\f134"}.fa-firefox:before{content:"\f269"}.fa-first-aid:before{content:"\f479"}.fa-first-order:before{content:"\f2b0"}.fa-first-order-alt:before{content:"\f50a"}.fa-firstdraft:before{content:"\f3a1"}.fa-fish:before{content:"\f578"}.fa-fist-raised:before{content:"\f6de"}.fa-flag:before{content:"\f024"}.fa-flag-checkered:before{content:"\f11e"}.fa-flag-usa:before{content:"\f74d"}.fa-flask:before{content:"\f0c3"}.fa-flickr:before{content:"\f16e"}.fa-flipboard:before{content:"\f44d"}.fa-flushed:before{content:"\f579"}.fa-fly:before{content:"\f417"}.fa-folder:before{content:"\f07b"}.fa-folder-minus:before{content:"\f65d"}.fa-folder-open:before{content:"\f07c"}.fa-folder-plus:before{content:"\f65e"}.fa-font:before{content:"\f031"}.fa-font-awesome:before{content:"\f2b4"}.fa-font-awesome-alt:before{content:"\f35c"}.fa-font-awesome-flag:before{content:"\f425"}.fa-font-awesome-logo-full:before{content:"\f4e6"}.fa-fonticons:before{content:"\f280"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-football-ball:before{content:"\f44e"}.fa-fort-awesome:before{content:"\f286"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-forumbee:before{content:"\f211"}.fa-forward:before{content:"\f04e"}.fa-foursquare:before{content:"\f180"}.fa-free-code-camp:before{content:"\f2c5"}.fa-freebsd:before{content:"\f3a4"}.fa-frog:before{content:"\f52e"}.fa-frown:before{content:"\f119"}.fa-frown-open:before{content:"\f57a"}.fa-fulcrum:before{content:"\f50b"}.fa-funnel-dollar:before{content:"\f662"}.fa-futbol:before{content:"\f1e3"}.fa-galactic-republic:before{content:"\f50c"}.fa-galactic-senate:before{content:"\f50d"}.fa-gamepad:before{content:"\f11b"}.fa-gas-pump:before{content:"\f52f"}.fa-gavel:before{content:"\f0e3"}.fa-gem:before{content:"\f3a5"}.fa-genderless:before{content:"\f22d"}.fa-get-pocket:before{content:"\f265"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-ghost:before{content:"\f6e2"}.fa-gift:before{content:"\f06b"}.fa-gifts:before{content:"\f79c"}.fa-git:before{content:"\f1d3"}.fa-git-alt:before{content:"\f841"}.fa-git-square:before{content:"\f1d2"}.fa-github:before{content:"\f09b"}.fa-github-alt:before{content:"\f113"}.fa-github-square:before{content:"\f092"}.fa-gitkraken:before{content:"\f3a6"}.fa-gitlab:before{content:"\f296"}.fa-gitter:before{content:"\f426"}.fa-glass-cheers:before{content:"\f79f"}.fa-glass-martini:before{content:"\f000"}.fa-glass-martini-alt:before{content:"\f57b"}.fa-glass-whiskey:before{content:"\f7a0"}.fa-glasses:before{content:"\f530"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-globe:before{content:"\f0ac"}.fa-globe-africa:before{content:"\f57c"}.fa-globe-americas:before{content:"\f57d"}.fa-globe-asia:before{content:"\f57e"}.fa-globe-europe:before{content:"\f7a2"}.fa-gofore:before{content:"\f3a7"}.fa-golf-ball:before{content:"\f450"}.fa-goodreads:before{content:"\f3a8"}.fa-goodreads-g:before{content:"\f3a9"}.fa-google:before{content:"\f1a0"}.fa-google-drive:before{content:"\f3aa"}.fa-google-play:before{content:"\f3ab"}.fa-google-plus:before{content:"\f2b3"}.fa-google-plus-g:before{content:"\f0d5"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-wallet:before{content:"\f1ee"}.fa-gopuram:before{content:"\f664"}.fa-graduation-cap:before{content:"\f19d"}.fa-gratipay:before{content:"\f184"}.fa-grav:before{content:"\f2d6"}.fa-greater-than:before{content:"\f531"}.fa-greater-than-equal:before{content:"\f532"}.fa-grimace:before{content:"\f57f"}.fa-grin:before{content:"\f580"}.fa-grin-alt:before{content:"\f581"}.fa-grin-beam:before{content:"\f582"}.fa-grin-beam-sweat:before{content:"\f583"}.fa-grin-hearts:before{content:"\f584"}.fa-grin-squint:before{content:"\f585"}.fa-grin-squint-tears:before{content:"\f586"}.fa-grin-stars:before{content:"\f587"}.fa-grin-tears:before{content:"\f588"}.fa-grin-tongue:before{content:"\f589"}.fa-grin-tongue-squint:before{content:"\f58a"}.fa-grin-tongue-wink:before{content:"\f58b"}.fa-grin-wink:before{content:"\f58c"}.fa-grip-horizontal:before{content:"\f58d"}.fa-grip-lines:before{content:"\f7a4"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-grip-vertical:before{content:"\f58e"}.fa-gripfire:before{content:"\f3ac"}.fa-grunt:before{content:"\f3ad"}.fa-guitar:before{content:"\f7a6"}.fa-gulp:before{content:"\f3ae"}.fa-h-square:before{content:"\f0fd"}.fa-hacker-news:before{content:"\f1d4"}.fa-hacker-news-square:before{content:"\f3af"}.fa-hackerrank:before{content:"\f5f7"}.fa-hamburger:before{content:"\f805"}.fa-hammer:before{content:"\f6e3"}.fa-hamsa:before{content:"\f665"}.fa-hand-holding:before{content:"\f4bd"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-hand-holding-usd:before{content:"\f4c0"}.fa-hand-lizard:before{content:"\f258"}.fa-hand-middle-finger:before{content:"\f806"}.fa-hand-paper:before{content:"\f256"}.fa-hand-peace:before{content:"\f25b"}.fa-hand-point-down:before{content:"\f0a7"}.fa-hand-point-left:before{content:"\f0a5"}.fa-hand-point-right:before{content:"\f0a4"}.fa-hand-point-up:before{content:"\f0a6"}.fa-hand-pointer:before{content:"\f25a"}.fa-hand-rock:before{content:"\f255"}.fa-hand-scissors:before{content:"\f257"}.fa-hand-spock:before{content:"\f259"}.fa-hands:before{content:"\f4c2"}.fa-hands-helping:before{content:"\f4c4"}.fa-handshake:before{content:"\f2b5"}.fa-hanukiah:before{content:"\f6e6"}.fa-hard-hat:before{content:"\f807"}.fa-hashtag:before{content:"\f292"}.fa-hat-wizard:before{content:"\f6e8"}.fa-haykal:before{content:"\f666"}.fa-hdd:before{content:"\f0a0"}.fa-heading:before{content:"\f1dc"}.fa-headphones:before{content:"\f025"}.fa-headphones-alt:before{content:"\f58f"}.fa-headset:before{content:"\f590"}.fa-heart:before{content:"\f004"}.fa-heart-broken:before{content:"\f7a9"}.fa-heartbeat:before{content:"\f21e"}.fa-helicopter:before{content:"\f533"}.fa-highlighter:before{content:"\f591"}.fa-hiking:before{content:"\f6ec"}.fa-hippo:before{content:"\f6ed"}.fa-hips:before{content:"\f452"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-history:before{content:"\f1da"}.fa-hockey-puck:before{content:"\f453"}.fa-holly-berry:before{content:"\f7aa"}.fa-home:before{content:"\f015"}.fa-hooli:before{content:"\f427"}.fa-hornbill:before{content:"\f592"}.fa-horse:before{content:"\f6f0"}.fa-horse-head:before{content:"\f7ab"}.fa-hospital:before{content:"\f0f8"}.fa-hospital-alt:before{content:"\f47d"}.fa-hospital-symbol:before{content:"\f47e"}.fa-hot-tub:before{content:"\f593"}.fa-hotdog:before{content:"\f80f"}.fa-hotel:before{content:"\f594"}.fa-hotjar:before{content:"\f3b1"}.fa-hourglass:before{content:"\f254"}.fa-hourglass-end:before{content:"\f253"}.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-start:before{content:"\f251"}.fa-house-damage:before{content:"\f6f1"}.fa-houzz:before{content:"\f27c"}.fa-hryvnia:before{content:"\f6f2"}.fa-html5:before{content:"\f13b"}.fa-hubspot:before{content:"\f3b2"}.fa-i-cursor:before{content:"\f246"}.fa-ice-cream:before{content:"\f810"}.fa-icicles:before{content:"\f7ad"}.fa-icons:before{content:"\f86d"}.fa-id-badge:before{content:"\f2c1"}.fa-id-card:before{content:"\f2c2"}.fa-id-card-alt:before{content:"\f47f"}.fa-igloo:before{content:"\f7ae"}.fa-image:before{content:"\f03e"}.fa-images:before{content:"\f302"}.fa-imdb:before{content:"\f2d8"}.fa-inbox:before{content:"\f01c"}.fa-indent:before{content:"\f03c"}.fa-industry:before{content:"\f275"}.fa-infinity:before{content:"\f534"}.fa-info:before{content:"\f129"}.fa-info-circle:before{content:"\f05a"}.fa-instagram:before{content:"\f16d"}.fa-intercom:before{content:"\f7af"}.fa-internet-explorer:before{content:"\f26b"}.fa-invision:before{content:"\f7b0"}.fa-ioxhost:before{content:"\f208"}.fa-italic:before{content:"\f033"}.fa-itch-io:before{content:"\f83a"}.fa-itunes:before{content:"\f3b4"}.fa-itunes-note:before{content:"\f3b5"}.fa-java:before{content:"\f4e4"}.fa-jedi:before{content:"\f669"}.fa-jedi-order:before{content:"\f50e"}.fa-jenkins:before{content:"\f3b6"}.fa-jira:before{content:"\f7b1"}.fa-joget:before{content:"\f3b7"}.fa-joint:before{content:"\f595"}.fa-joomla:before{content:"\f1aa"}.fa-journal-whills:before{content:"\f66a"}.fa-js:before{content:"\f3b8"}.fa-js-square:before{content:"\f3b9"}.fa-jsfiddle:before{content:"\f1cc"}.fa-kaaba:before{content:"\f66b"}.fa-kaggle:before{content:"\f5fa"}.fa-key:before{content:"\f084"}.fa-keybase:before{content:"\f4f5"}.fa-keyboard:before{content:"\f11c"}.fa-keycdn:before{content:"\f3ba"}.fa-khanda:before{content:"\f66d"}.fa-kickstarter:before{content:"\f3bb"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-kiss:before{content:"\f596"}.fa-kiss-beam:before{content:"\f597"}.fa-kiss-wink-heart:before{content:"\f598"}.fa-kiwi-bird:before{content:"\f535"}.fa-korvue:before{content:"\f42f"}.fa-landmark:before{content:"\f66f"}.fa-language:before{content:"\f1ab"}.fa-laptop:before{content:"\f109"}.fa-laptop-code:before{content:"\f5fc"}.fa-laptop-medical:before{content:"\f812"}.fa-laravel:before{content:"\f3bd"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-laugh:before{content:"\f599"}.fa-laugh-beam:before{content:"\f59a"}.fa-laugh-squint:before{content:"\f59b"}.fa-laugh-wink:before{content:"\f59c"}.fa-layer-group:before{content:"\f5fd"}.fa-leaf:before{content:"\f06c"}.fa-leanpub:before{content:"\f212"}.fa-lemon:before{content:"\f094"}.fa-less:before{content:"\f41d"}.fa-less-than:before{content:"\f536"}.fa-less-than-equal:before{content:"\f537"}.fa-level-down-alt:before{content:"\f3be"}.fa-level-up-alt:before{content:"\f3bf"}.fa-life-ring:before{content:"\f1cd"}.fa-lightbulb:before{content:"\f0eb"}.fa-line:before{content:"\f3c0"}.fa-link:before{content:"\f0c1"}.fa-linkedin:before{content:"\f08c"}.fa-linkedin-in:before{content:"\f0e1"}.fa-linode:before{content:"\f2b8"}.fa-linux:before{content:"\f17c"}.fa-lira-sign:before{content:"\f195"}.fa-list:before{content:"\f03a"}.fa-list-alt:before{content:"\f022"}.fa-list-ol:before{content:"\f0cb"}.fa-list-ul:before{content:"\f0ca"}.fa-location-arrow:before{content:"\f124"}.fa-lock:before{content:"\f023"}.fa-lock-open:before{content:"\f3c1"}.fa-long-arrow-alt-down:before{content:"\f309"}.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-long-arrow-alt-right:before{content:"\f30b"}.fa-long-arrow-alt-up:before{content:"\f30c"}.fa-low-vision:before{content:"\f2a8"}.fa-luggage-cart:before{content:"\f59d"}.fa-lyft:before{content:"\f3c3"}.fa-magento:before{content:"\f3c4"}.fa-magic:before{content:"\f0d0"}.fa-magnet:before{content:"\f076"}.fa-mail-bulk:before{content:"\f674"}.fa-mailchimp:before{content:"\f59e"}.fa-male:before{content:"\f183"}.fa-mandalorian:before{content:"\f50f"}.fa-map:before{content:"\f279"}.fa-map-marked:before{content:"\f59f"}.fa-map-marked-alt:before{content:"\f5a0"}.fa-map-marker:before{content:"\f041"}.fa-map-marker-alt:before{content:"\f3c5"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-markdown:before{content:"\f60f"}.fa-marker:before{content:"\f5a1"}.fa-mars:before{content:"\f222"}.fa-mars-double:before{content:"\f227"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mask:before{content:"\f6fa"}.fa-mastodon:before{content:"\f4f6"}.fa-maxcdn:before{content:"\f136"}.fa-medal:before{content:"\f5a2"}.fa-medapps:before{content:"\f3c6"}.fa-medium:before{content:"\f23a"}.fa-medium-m:before{content:"\f3c7"}.fa-medkit:before{content:"\f0fa"}.fa-medrt:before{content:"\f3c8"}.fa-meetup:before{content:"\f2e0"}.fa-megaport:before{content:"\f5a3"}.fa-meh:before{content:"\f11a"}.fa-meh-blank:before{content:"\f5a4"}.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-memory:before{content:"\f538"}.fa-mendeley:before{content:"\f7b3"}.fa-menorah:before{content:"\f676"}.fa-mercury:before{content:"\f223"}.fa-meteor:before{content:"\f753"}.fa-microchip:before{content:"\f2db"}.fa-microphone:before{content:"\f130"}.fa-microphone-alt:before{content:"\f3c9"}.fa-microphone-alt-slash:before{content:"\f539"}.fa-microphone-slash:before{content:"\f131"}.fa-microscope:before{content:"\f610"}.fa-microsoft:before{content:"\f3ca"}.fa-minus:before{content:"\f068"}.fa-minus-circle:before{content:"\f056"}.fa-minus-square:before{content:"\f146"}.fa-mitten:before{content:"\f7b5"}.fa-mix:before{content:"\f3cb"}.fa-mixcloud:before{content:"\f289"}.fa-mizuni:before{content:"\f3cc"}.fa-mobile:before{content:"\f10b"}.fa-mobile-alt:before{content:"\f3cd"}.fa-modx:before{content:"\f285"}.fa-monero:before{content:"\f3d0"}.fa-money-bill:before{content:"\f0d6"}.fa-money-bill-alt:before{content:"\f3d1"}.fa-money-bill-wave:before{content:"\f53a"}.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-money-check:before{content:"\f53c"}.fa-money-check-alt:before{content:"\f53d"}.fa-monument:before{content:"\f5a6"}.fa-moon:before{content:"\f186"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-mosque:before{content:"\f678"}.fa-motorcycle:before{content:"\f21c"}.fa-mountain:before{content:"\f6fc"}.fa-mouse-pointer:before{content:"\f245"}.fa-mug-hot:before{content:"\f7b6"}.fa-music:before{content:"\f001"}.fa-napster:before{content:"\f3d2"}.fa-neos:before{content:"\f612"}.fa-network-wired:before{content:"\f6ff"}.fa-neuter:before{content:"\f22c"}.fa-newspaper:before{content:"\f1ea"}.fa-nimblr:before{content:"\f5a8"}.fa-node:before{content:"\f419"}.fa-node-js:before{content:"\f3d3"}.fa-not-equal:before{content:"\f53e"}.fa-notes-medical:before{content:"\f481"}.fa-npm:before{content:"\f3d4"}.fa-ns8:before{content:"\f3d5"}.fa-nutritionix:before{content:"\f3d6"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-oil-can:before{content:"\f613"}.fa-old-republic:before{content:"\f510"}.fa-om:before{content:"\f679"}.fa-opencart:before{content:"\f23d"}.fa-openid:before{content:"\f19b"}.fa-opera:before{content:"\f26a"}.fa-optin-monster:before{content:"\f23c"}.fa-osi:before{content:"\f41a"}.fa-otter:before{content:"\f700"}.fa-outdent:before{content:"\f03b"}.fa-page4:before{content:"\f3d7"}.fa-pagelines:before{content:"\f18c"}.fa-pager:before{content:"\f815"}.fa-paint-brush:before{content:"\f1fc"}.fa-paint-roller:before{content:"\f5aa"}.fa-palette:before{content:"\f53f"}.fa-palfed:before{content:"\f3d8"}.fa-pallet:before{content:"\f482"}.fa-paper-plane:before{content:"\f1d8"}.fa-paperclip:before{content:"\f0c6"}.fa-parachute-box:before{content:"\f4cd"}.fa-paragraph:before{content:"\f1dd"}.fa-parking:before{content:"\f540"}.fa-passport:before{content:"\f5ab"}.fa-pastafarianism:before{content:"\f67b"}.fa-paste:before{content:"\f0ea"}.fa-patreon:before{content:"\f3d9"}.fa-pause:before{content:"\f04c"}.fa-pause-circle:before{content:"\f28b"}.fa-paw:before{content:"\f1b0"}.fa-paypal:before{content:"\f1ed"}.fa-peace:before{content:"\f67c"}.fa-pen:before{content:"\f304"}.fa-pen-alt:before{content:"\f305"}.fa-pen-fancy:before{content:"\f5ac"}.fa-pen-nib:before{content:"\f5ad"}.fa-pen-square:before{content:"\f14b"}.fa-pencil-alt:before{content:"\f303"}.fa-pencil-ruler:before{content:"\f5ae"}.fa-penny-arcade:before{content:"\f704"}.fa-people-carry:before{content:"\f4ce"}.fa-pepper-hot:before{content:"\f816"}.fa-percent:before{content:"\f295"}.fa-percentage:before{content:"\f541"}.fa-periscope:before{content:"\f3da"}.fa-person-booth:before{content:"\f756"}.fa-phabricator:before{content:"\f3db"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-phoenix-squadron:before{content:"\f511"}.fa-phone:before{content:"\f095"}.fa-phone-alt:before{content:"\f879"}.fa-phone-slash:before{content:"\f3dd"}.fa-phone-square:before{content:"\f098"}.fa-phone-square-alt:before{content:"\f87b"}.fa-phone-volume:before{content:"\f2a0"}.fa-photo-video:before{content:"\f87c"}.fa-php:before{content:"\f457"}.fa-pied-piper:before{content:"\f2ae"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-piggy-bank:before{content:"\f4d3"}.fa-pills:before{content:"\f484"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-p:before{content:"\f231"}.fa-pinterest-square:before{content:"\f0d3"}.fa-pizza-slice:before{content:"\f818"}.fa-place-of-worship:before{content:"\f67f"}.fa-plane:before{content:"\f072"}.fa-plane-arrival:before{content:"\f5af"}.fa-plane-departure:before{content:"\f5b0"}.fa-play:before{content:"\f04b"}.fa-play-circle:before{content:"\f144"}.fa-playstation:before{content:"\f3df"}.fa-plug:before{content:"\f1e6"}.fa-plus:before{content:"\f067"}.fa-plus-circle:before{content:"\f055"}.fa-plus-square:before{content:"\f0fe"}.fa-podcast:before{content:"\f2ce"}.fa-poll:before{content:"\f681"}.fa-poll-h:before{content:"\f682"}.fa-poo:before{content:"\f2fe"}.fa-poo-storm:before{content:"\f75a"}.fa-poop:before{content:"\f619"}.fa-portrait:before{content:"\f3e0"}.fa-pound-sign:before{content:"\f154"}.fa-power-off:before{content:"\f011"}.fa-pray:before{content:"\f683"}.fa-praying-hands:before{content:"\f684"}.fa-prescription:before{content:"\f5b1"}.fa-prescription-bottle:before{content:"\f485"}.fa-prescription-bottle-alt:before{content:"\f486"}.fa-print:before{content:"\f02f"}.fa-procedures:before{content:"\f487"}.fa-product-hunt:before{content:"\f288"}.fa-project-diagram:before{content:"\f542"}.fa-pushed:before{content:"\f3e1"}.fa-puzzle-piece:before{content:"\f12e"}.fa-python:before{content:"\f3e2"}.fa-qq:before{content:"\f1d6"}.fa-qrcode:before{content:"\f029"}.fa-question:before{content:"\f128"}.fa-question-circle:before{content:"\f059"}.fa-quidditch:before{content:"\f458"}.fa-quinscape:before{content:"\f459"}.fa-quora:before{content:"\f2c4"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-quran:before{content:"\f687"}.fa-r-project:before{content:"\f4f7"}.fa-radiation:before{content:"\f7b9"}.fa-radiation-alt:before{content:"\f7ba"}.fa-rainbow:before{content:"\f75b"}.fa-random:before{content:"\f074"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-ravelry:before{content:"\f2d9"}.fa-react:before{content:"\f41b"}.fa-reacteurope:before{content:"\f75d"}.fa-readme:before{content:"\f4d5"}.fa-rebel:before{content:"\f1d0"}.fa-receipt:before{content:"\f543"}.fa-recycle:before{content:"\f1b8"}.fa-red-river:before{content:"\f3e3"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-alien:before{content:"\f281"}.fa-reddit-square:before{content:"\f1a2"}.fa-redhat:before{content:"\f7bc"}.fa-redo:before{content:"\f01e"}.fa-redo-alt:before{content:"\f2f9"}.fa-registered:before{content:"\f25d"}.fa-remove-format:before{content:"\f87d"}.fa-renren:before{content:"\f18b"}.fa-reply:before{content:"\f3e5"}.fa-reply-all:before{content:"\f122"}.fa-replyd:before{content:"\f3e6"}.fa-republican:before{content:"\f75e"}.fa-researchgate:before{content:"\f4f8"}.fa-resolving:before{content:"\f3e7"}.fa-restroom:before{content:"\f7bd"}.fa-retweet:before{content:"\f079"}.fa-rev:before{content:"\f5b2"}.fa-ribbon:before{content:"\f4d6"}.fa-ring:before{content:"\f70b"}.fa-road:before{content:"\f018"}.fa-robot:before{content:"\f544"}.fa-rocket:before{content:"\f135"}.fa-rocketchat:before{content:"\f3e8"}.fa-rockrms:before{content:"\f3e9"}.fa-route:before{content:"\f4d7"}.fa-rss:before{content:"\f09e"}.fa-rss-square:before{content:"\f143"}.fa-ruble-sign:before{content:"\f158"}.fa-ruler:before{content:"\f545"}.fa-ruler-combined:before{content:"\f546"}.fa-ruler-horizontal:before{content:"\f547"}.fa-ruler-vertical:before{content:"\f548"}.fa-running:before{content:"\f70c"}.fa-rupee-sign:before{content:"\f156"}.fa-sad-cry:before{content:"\f5b3"}.fa-sad-tear:before{content:"\f5b4"}.fa-safari:before{content:"\f267"}.fa-salesforce:before{content:"\f83b"}.fa-sass:before{content:"\f41e"}.fa-satellite:before{content:"\f7bf"}.fa-satellite-dish:before{content:"\f7c0"}.fa-save:before{content:"\f0c7"}.fa-schlix:before{content:"\f3ea"}.fa-school:before{content:"\f549"}.fa-screwdriver:before{content:"\f54a"}.fa-scribd:before{content:"\f28a"}.fa-scroll:before{content:"\f70e"}.fa-sd-card:before{content:"\f7c2"}.fa-search:before{content:"\f002"}.fa-search-dollar:before{content:"\f688"}.fa-search-location:before{content:"\f689"}.fa-search-minus:before{content:"\f010"}.fa-search-plus:before{content:"\f00e"}.fa-searchengin:before{content:"\f3eb"}.fa-seedling:before{content:"\f4d8"}.fa-sellcast:before{content:"\f2da"}.fa-sellsy:before{content:"\f213"}.fa-server:before{content:"\f233"}.fa-servicestack:before{content:"\f3ec"}.fa-shapes:before{content:"\f61f"}.fa-share:before{content:"\f064"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-share-square:before{content:"\f14d"}.fa-shekel-sign:before{content:"\f20b"}.fa-shield-alt:before{content:"\f3ed"}.fa-ship:before{content:"\f21a"}.fa-shipping-fast:before{content:"\f48b"}.fa-shirtsinbulk:before{content:"\f214"}.fa-shoe-prints:before{content:"\f54b"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-shopping-cart:before{content:"\f07a"}.fa-shopware:before{content:"\f5b5"}.fa-shower:before{content:"\f2cc"}.fa-shuttle-van:before{content:"\f5b6"}.fa-sign:before{content:"\f4d9"}.fa-sign-in-alt:before{content:"\f2f6"}.fa-sign-language:before{content:"\f2a7"}.fa-sign-out-alt:before{content:"\f2f5"}.fa-signal:before{content:"\f012"}.fa-signature:before{content:"\f5b7"}.fa-sim-card:before{content:"\f7c4"}.fa-simplybuilt:before{content:"\f215"}.fa-sistrix:before{content:"\f3ee"}.fa-sitemap:before{content:"\f0e8"}.fa-sith:before{content:"\f512"}.fa-skating:before{content:"\f7c5"}.fa-sketch:before{content:"\f7c6"}.fa-skiing:before{content:"\f7c9"}.fa-skiing-nordic:before{content:"\f7ca"}.fa-skull:before{content:"\f54c"}.fa-skull-crossbones:before{content:"\f714"}.fa-skyatlas:before{content:"\f216"}.fa-skype:before{content:"\f17e"}.fa-slack:before{content:"\f198"}.fa-slack-hash:before{content:"\f3ef"}.fa-slash:before{content:"\f715"}.fa-sleigh:before{content:"\f7cc"}.fa-sliders-h:before{content:"\f1de"}.fa-slideshare:before{content:"\f1e7"}.fa-smile:before{content:"\f118"}.fa-smile-beam:before{content:"\f5b8"}.fa-smile-wink:before{content:"\f4da"}.fa-smog:before{content:"\f75f"}.fa-smoking:before{content:"\f48d"}.fa-smoking-ban:before{content:"\f54d"}.fa-sms:before{content:"\f7cd"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-snowboarding:before{content:"\f7ce"}.fa-snowflake:before{content:"\f2dc"}.fa-snowman:before{content:"\f7d0"}.fa-snowplow:before{content:"\f7d2"}.fa-socks:before{content:"\f696"}.fa-solar-panel:before{content:"\f5ba"}.fa-sort:before{content:"\f0dc"}.fa-sort-alpha-down:before{content:"\f15d"}.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-sort-alpha-up:before{content:"\f15e"}.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-sort-amount-down:before{content:"\f160"}.fa-sort-amount-down-alt:before{content:"\f884"}.fa-sort-amount-up:before{content:"\f161"}.fa-sort-amount-up-alt:before{content:"\f885"}.fa-sort-down:before{content:"\f0dd"}.fa-sort-numeric-down:before{content:"\f162"}.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-sort-numeric-up:before{content:"\f163"}.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-sort-up:before{content:"\f0de"}.fa-soundcloud:before{content:"\f1be"}.fa-sourcetree:before{content:"\f7d3"}.fa-spa:before{content:"\f5bb"}.fa-space-shuttle:before{content:"\f197"}.fa-speakap:before{content:"\f3f3"}.fa-speaker-deck:before{content:"\f83c"}.fa-spell-check:before{content:"\f891"}.fa-spider:before{content:"\f717"}.fa-spinner:before{content:"\f110"}.fa-splotch:before{content:"\f5bc"}.fa-spotify:before{content:"\f1bc"}.fa-spray-can:before{content:"\f5bd"}.fa-square:before{content:"\f0c8"}.fa-square-full:before{content:"\f45c"}.fa-square-root-alt:before{content:"\f698"}.fa-squarespace:before{content:"\f5be"}.fa-stack-exchange:before{content:"\f18d"}.fa-stack-overflow:before{content:"\f16c"}.fa-stackpath:before{content:"\f842"}.fa-stamp:before{content:"\f5bf"}.fa-star:before{content:"\f005"}.fa-star-and-crescent:before{content:"\f699"}.fa-star-half:before{content:"\f089"}.fa-star-half-alt:before{content:"\f5c0"}.fa-star-of-david:before{content:"\f69a"}.fa-star-of-life:before{content:"\f621"}.fa-staylinked:before{content:"\f3f5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-steam-symbol:before{content:"\f3f6"}.fa-step-backward:before{content:"\f048"}.fa-step-forward:before{content:"\f051"}.fa-stethoscope:before{content:"\f0f1"}.fa-sticker-mule:before{content:"\f3f7"}.fa-sticky-note:before{content:"\f249"}.fa-stop:before{content:"\f04d"}.fa-stop-circle:before{content:"\f28d"}.fa-stopwatch:before{content:"\f2f2"}.fa-store:before{content:"\f54e"}.fa-store-alt:before{content:"\f54f"}.fa-strava:before{content:"\f428"}.fa-stream:before{content:"\f550"}.fa-street-view:before{content:"\f21d"}.fa-strikethrough:before{content:"\f0cc"}.fa-stripe:before{content:"\f429"}.fa-stripe-s:before{content:"\f42a"}.fa-stroopwafel:before{content:"\f551"}.fa-studiovinari:before{content:"\f3f8"}.fa-stumbleupon:before{content:"\f1a4"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-subscript:before{content:"\f12c"}.fa-subway:before{content:"\f239"}.fa-suitcase:before{content:"\f0f2"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-sun:before{content:"\f185"}.fa-superpowers:before{content:"\f2dd"}.fa-superscript:before{content:"\f12b"}.fa-supple:before{content:"\f3f9"}.fa-surprise:before{content:"\f5c2"}.fa-suse:before{content:"\f7d6"}.fa-swatchbook:before{content:"\f5c3"}.fa-swimmer:before{content:"\f5c4"}.fa-swimming-pool:before{content:"\f5c5"}.fa-symfony:before{content:"\f83d"}.fa-synagogue:before{content:"\f69b"}.fa-sync:before{content:"\f021"}.fa-sync-alt:before{content:"\f2f1"}.fa-syringe:before{content:"\f48e"}.fa-table:before{content:"\f0ce"}.fa-table-tennis:before{content:"\f45d"}.fa-tablet:before{content:"\f10a"}.fa-tablet-alt:before{content:"\f3fa"}.fa-tablets:before{content:"\f490"}.fa-tachometer-alt:before{content:"\f3fd"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-tape:before{content:"\f4db"}.fa-tasks:before{content:"\f0ae"}.fa-taxi:before{content:"\f1ba"}.fa-teamspeak:before{content:"\f4f9"}.fa-teeth:before{content:"\f62e"}.fa-teeth-open:before{content:"\f62f"}.fa-telegram:before{content:"\f2c6"}.fa-telegram-plane:before{content:"\f3fe"}.fa-temperature-high:before{content:"\f769"}.fa-temperature-low:before{content:"\f76b"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-tenge:before{content:"\f7d7"}.fa-terminal:before{content:"\f120"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-th:before{content:"\f00a"}.fa-th-large:before{content:"\f009"}.fa-th-list:before{content:"\f00b"}.fa-the-red-yeti:before{content:"\f69d"}.fa-theater-masks:before{content:"\f630"}.fa-themeco:before{content:"\f5c6"}.fa-themeisle:before{content:"\f2b2"}.fa-thermometer:before{content:"\f491"}.fa-thermometer-empty:before{content:"\f2cb"}.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-think-peaks:before{content:"\f731"}.fa-thumbs-down:before{content:"\f165"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbtack:before{content:"\f08d"}.fa-ticket-alt:before{content:"\f3ff"}.fa-times:before{content:"\f00d"}.fa-times-circle:before{content:"\f057"}.fa-tint:before{content:"\f043"}.fa-tint-slash:before{content:"\f5c7"}.fa-tired:before{content:"\f5c8"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-toilet:before{content:"\f7d8"}.fa-toilet-paper:before{content:"\f71e"}.fa-toolbox:before{content:"\f552"}.fa-tools:before{content:"\f7d9"}.fa-tooth:before{content:"\f5c9"}.fa-torah:before{content:"\f6a0"}.fa-torii-gate:before{content:"\f6a1"}.fa-tractor:before{content:"\f722"}.fa-trade-federation:before{content:"\f513"}.fa-trademark:before{content:"\f25c"}.fa-traffic-light:before{content:"\f637"}.fa-train:before{content:"\f238"}.fa-tram:before{content:"\f7da"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-trash:before{content:"\f1f8"}.fa-trash-alt:before{content:"\f2ed"}.fa-trash-restore:before{content:"\f829"}.fa-trash-restore-alt:before{content:"\f82a"}.fa-tree:before{content:"\f1bb"}.fa-trello:before{content:"\f181"}.fa-tripadvisor:before{content:"\f262"}.fa-trophy:before{content:"\f091"}.fa-truck:before{content:"\f0d1"}.fa-truck-loading:before{content:"\f4de"}.fa-truck-monster:before{content:"\f63b"}.fa-truck-moving:before{content:"\f4df"}.fa-truck-pickup:before{content:"\f63c"}.fa-tshirt:before{content:"\f553"}.fa-tty:before{content:"\f1e4"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-tv:before{content:"\f26c"}.fa-twitch:before{content:"\f1e8"}.fa-twitter:before{content:"\f099"}.fa-twitter-square:before{content:"\f081"}.fa-typo3:before{content:"\f42b"}.fa-uber:before{content:"\f402"}.fa-ubuntu:before{content:"\f7df"}.fa-uikit:before{content:"\f403"}.fa-umbrella:before{content:"\f0e9"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-underline:before{content:"\f0cd"}.fa-undo:before{content:"\f0e2"}.fa-undo-alt:before{content:"\f2ea"}.fa-uniregistry:before{content:"\f404"}.fa-universal-access:before{content:"\f29a"}.fa-university:before{content:"\f19c"}.fa-unlink:before{content:"\f127"}.fa-unlock:before{content:"\f09c"}.fa-unlock-alt:before{content:"\f13e"}.fa-untappd:before{content:"\f405"}.fa-upload:before{content:"\f093"}.fa-ups:before{content:"\f7e0"}.fa-usb:before{content:"\f287"}.fa-user:before{content:"\f007"}.fa-user-alt:before{content:"\f406"}.fa-user-alt-slash:before{content:"\f4fa"}.fa-user-astronaut:before{content:"\f4fb"}.fa-user-check:before{content:"\f4fc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-clock:before{content:"\f4fd"}.fa-user-cog:before{content:"\f4fe"}.fa-user-edit:before{content:"\f4ff"}.fa-user-friends:before{content:"\f500"}.fa-user-graduate:before{content:"\f501"}.fa-user-injured:before{content:"\f728"}.fa-user-lock:before{content:"\f502"}.fa-user-md:before{content:"\f0f0"}.fa-user-minus:before{content:"\f503"}.fa-user-ninja:before{content:"\f504"}.fa-user-nurse:before{content:"\f82f"}.fa-user-plus:before{content:"\f234"}.fa-user-secret:before{content:"\f21b"}.fa-user-shield:before{content:"\f505"}.fa-user-slash:before{content:"\f506"}.fa-user-tag:before{content:"\f507"}.fa-user-tie:before{content:"\f508"}.fa-user-times:before{content:"\f235"}.fa-users:before{content:"\f0c0"}.fa-users-cog:before{content:"\f509"}.fa-usps:before{content:"\f7e1"}.fa-ussunnah:before{content:"\f407"}.fa-utensil-spoon:before{content:"\f2e5"}.fa-utensils:before{content:"\f2e7"}.fa-vaadin:before{content:"\f408"}.fa-vector-square:before{content:"\f5cb"}.fa-venus:before{content:"\f221"}.fa-venus-double:before{content:"\f226"}.fa-venus-mars:before{content:"\f228"}.fa-viacoin:before{content:"\f237"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-vial:before{content:"\f492"}.fa-vials:before{content:"\f493"}.fa-viber:before{content:"\f409"}.fa-video:before{content:"\f03d"}.fa-video-slash:before{content:"\f4e2"}.fa-vihara:before{content:"\f6a7"}.fa-vimeo:before{content:"\f40a"}.fa-vimeo-square:before{content:"\f194"}.fa-vimeo-v:before{content:"\f27d"}.fa-vine:before{content:"\f1ca"}.fa-vk:before{content:"\f189"}.fa-vnv:before{content:"\f40b"}.fa-voicemail:before{content:"\f897"}.fa-volleyball-ball:before{content:"\f45f"}.fa-volume-down:before{content:"\f027"}.fa-volume-mute:before{content:"\f6a9"}.fa-volume-off:before{content:"\f026"}.fa-volume-up:before{content:"\f028"}.fa-vote-yea:before{content:"\f772"}.fa-vr-cardboard:before{content:"\f729"}.fa-vuejs:before{content:"\f41f"}.fa-walking:before{content:"\f554"}.fa-wallet:before{content:"\f555"}.fa-warehouse:before{content:"\f494"}.fa-water:before{content:"\f773"}.fa-wave-square:before{content:"\f83e"}.fa-waze:before{content:"\f83f"}.fa-weebly:before{content:"\f5cc"}.fa-weibo:before{content:"\f18a"}.fa-weight:before{content:"\f496"}.fa-weight-hanging:before{content:"\f5cd"}.fa-weixin:before{content:"\f1d7"}.fa-whatsapp:before{content:"\f232"}.fa-whatsapp-square:before{content:"\f40c"}.fa-wheelchair:before{content:"\f193"}.fa-whmcs:before{content:"\f40d"}.fa-wifi:before{content:"\f1eb"}.fa-wikipedia-w:before{content:"\f266"}.fa-wind:before{content:"\f72e"}.fa-window-close:before{content:"\f410"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-windows:before{content:"\f17a"}.fa-wine-bottle:before{content:"\f72f"}.fa-wine-glass:before{content:"\f4e3"}.fa-wine-glass-alt:before{content:"\f5ce"}.fa-wix:before{content:"\f5cf"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-won-sign:before{content:"\f159"}.fa-wordpress:before{content:"\f19a"}.fa-wordpress-simple:before{content:"\f411"}.fa-wpbeginner:before{content:"\f297"}.fa-wpexplorer:before{content:"\f2de"}.fa-wpforms:before{content:"\f298"}.fa-wpressr:before{content:"\f3e4"}.fa-wrench:before{content:"\f0ad"}.fa-x-ray:before{content:"\f497"}.fa-xbox:before{content:"\f412"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-y-combinator:before{content:"\f23b"}.fa-yahoo:before{content:"\f19e"}.fa-yammer:before{content:"\f840"}.fa-yandex:before{content:"\f413"}.fa-yandex-international:before{content:"\f414"}.fa-yarn:before{content:"\f7e3"}.fa-yelp:before{content:"\f1e9"}.fa-yen-sign:before{content:"\f157"}.fa-yin-yang:before{content:"\f6ad"}.fa-yoast:before{content:"\f2b1"}.fa-youtube:before{content:"\f167"}.fa-youtube-square:before{content:"\f431"}.fa-zhihu:before{content:"\f63f"}.sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}@font-face{font-family:"Font Awesome 5 Brands";font-style:normal;font-weight:normal;font-display:auto;src:url(../webfonts/fa-brands-400.eot);src:url(../webfonts/fa-brands-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.woff) format("woff"),url(../webfonts/fa-brands-400.ttf) format("truetype"),url(../webfonts/fa-brands-400.svg#fontawesome) format("svg")}.fab{font-family:"Font Awesome 5 Brands"}@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:400;font-display:auto;src:url(../webfonts/fa-regular-400.eot);src:url(../webfonts/fa-regular-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.woff) format("woff"),url(../webfonts/fa-regular-400.ttf) format("truetype"),url(../webfonts/fa-regular-400.svg#fontawesome) format("svg")}.far{font-weight:400}@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:900;font-display:auto;src:url(../webfonts/fa-solid-900.eot);src:url(../webfonts/fa-solid-900.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.woff) format("woff"),url(../webfonts/fa-solid-900.ttf) format("truetype"),url(../webfonts/fa-solid-900.svg#fontawesome) format("svg")}.fa,.far,.fas{font-family:"Font Awesome 5 Free"}.fa,.fas{font-weight:900} \ No newline at end of file diff --git a/dockers/web/html/assets/css/fonts.googleapis.css b/dockers/web/html/assets/css/fonts.googleapis.css new file mode 100644 index 0000000..d5c8abd --- /dev/null +++ b/dockers/web/html/assets/css/fonts.googleapis.css @@ -0,0 +1,168 @@ +/* cyrillic-ext */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 300; + src: url(../gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmhduz8A.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 300; + src: url(../gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwkxduz8A.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 300; + src: url(../gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmxduz8A.woff2) format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 300; + src: url(../gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlBduz8A.woff2) format('woff2'); + unicode-range: U+0370-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 300; + src: url(../gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmBduz8A.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 300; + src: url(../gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmRduz8A.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 300; + src: url(../gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdu.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 700; + src: url(../gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmhduz8A.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 700; + src: url(../gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwkxduz8A.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 700; + src: url(../gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmxduz8A.woff2) format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 700; + src: url(../gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlBduz8A.woff2) format('woff2'); + unicode-range: U+0370-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 700; + src: url(../gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmBduz8A.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 700; + src: url(../gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmRduz8A.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 700; + src: url(../gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdu.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 900; + src: url(../gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmhduz8A.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 900; + src: url(../gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwkxduz8A.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 900; + src: url(../gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmxduz8A.woff2) format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 900; + src: url(../gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwlBduz8A.woff2) format('woff2'); + unicode-range: U+0370-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 900; + src: url(../gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmBduz8A.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 900; + src: url(../gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmRduz8A.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 900; + src: url(../gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwlxdu.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} diff --git a/dockers/web/html/assets/css/fonts.googleapis.inutilise.css b/dockers/web/html/assets/css/fonts.googleapis.inutilise.css new file mode 100644 index 0000000..a7c1ad4 --- /dev/null +++ b/dockers/web/html/assets/css/fonts.googleapis.inutilise.css @@ -0,0 +1,168 @@ +/* cyrillic-ext */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 300; + src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmhduz8A.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 300; + src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwkxduz8A.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 300; + src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmxduz8A.woff2) format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 300; + src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlBduz8A.woff2) format('woff2'); + unicode-range: U+0370-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 300; + src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmBduz8A.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 300; + src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmRduz8A.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 300; + src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdu.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 700; + src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmhduz8A.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 700; + src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwkxduz8A.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 700; + src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmxduz8A.woff2) format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 700; + src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlBduz8A.woff2) format('woff2'); + unicode-range: U+0370-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 700; + src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmBduz8A.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 700; + src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmRduz8A.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 700; + src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdu.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 900; + src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmhduz8A.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 900; + src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwkxduz8A.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 900; + src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmxduz8A.woff2) format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 900; + src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwlBduz8A.woff2) format('woff2'); + unicode-range: U+0370-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 900; + src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmBduz8A.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 900; + src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmRduz8A.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 900; + src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwlxdu.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} diff --git a/dockers/web/html/assets/css/main.css b/dockers/web/html/assets/css/main.css new file mode 100644 index 0000000..789026e --- /dev/null +++ b/dockers/web/html/assets/css/main.css @@ -0,0 +1,3346 @@ +@import url(fontawesome-all.min.css); +@import url(fonts.googleapis.css); +/* @import url("https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,700,900"); */ + +/* + Phantom by HTML5 UP + html5up.net | @ajlkn + Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) +*/ + +html, body, div, span, applet, object, +iframe, h1, h2, h3, h4, h5, h6, p, blockquote, +pre, a, abbr, acronym, address, big, cite, +code, del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, b, +u, i, center, dl, dt, dd, ol, ul, li, fieldset, +form, label, legend, table, caption, tbody, +tfoot, thead, tr, th, td, article, aside, +canvas, details, embed, figure, figcaption, +footer, header, hgroup, menu, nav, output, ruby, +section, summary, time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; + } + +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block;} + +body { + line-height: 1; +} + +ol, ul { + list-style: none; +} + +blockquote, q { + quotes: none; +} + + blockquote:before, blockquote:after, q:before, q:after { + content: ''; + content: none; + } + +table { + border-collapse: collapse; + border-spacing: 0; +} + +body { + -webkit-text-size-adjust: none; +} + +mark { + background-color: transparent; + color: inherit; +} + +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +input, select, textarea { + -moz-appearance: none; + -webkit-appearance: none; + -ms-appearance: none; + appearance: none; +} + +video { + width: 100%; + height: auto; +} + +/* Basic */ + + @-ms-viewport { + width: device-width; + } + + body { + -ms-overflow-style: scrollbar; + } + + @media screen and (max-width: 480px) { + + html, body { + min-width: 320px; + } + + } + + html { + box-sizing: border-box; + } + + *, *:before, *:after { + box-sizing: inherit; + } + + body { + background: #ffffff; + } + + body.is-preload *, body.is-preload *:before, body.is-preload *:after { + -moz-animation: none !important; + -webkit-animation: none !important; + -ms-animation: none !important; + animation: none !important; + -moz-transition: none !important; + -webkit-transition: none !important; + -ms-transition: none !important; + transition: none !important; + } + +/* Type */ + + body, input, select, textarea { + color: #585858; + font-family: "Source Sans Pro", Helvetica, sans-serif; + font-size: 16pt; + font-weight: 300; + line-height: 1.75; + } + + @media screen and (max-width: 1680px) { + + body, input, select, textarea { + font-size: 14pt; + } + + } + + @media screen and (max-width: 1280px) { + + body, input, select, textarea { + font-size: 12pt; + } + + } + + a { + -moz-transition: border-bottom-color 0.2s ease, color 0.2s ease; + -webkit-transition: border-bottom-color 0.2s ease, color 0.2s ease; + -ms-transition: border-bottom-color 0.2s ease, color 0.2s ease; + transition: border-bottom-color 0.2s ease, color 0.2s ease; + text-decoration: none; + color: #585858; + border-bottom: dotted 1px rgba(88, 88, 88, 0.5); + } + + a:hover { + border-bottom-color: transparent; + color: #f2849e !important; + } + + strong, b { + font-weight: 900; + } + + em, i { + font-style: italic; + } + + p { + margin: 0 0 2em 0; + } + + h1 { + font-size: 2.75em; + font-weight: 700; + line-height: 1.3; + margin: 0 0 1em 0; + letter-spacing: -0.035em; + } + + h1 a { + color: inherit; + } + + @media screen and (max-width: 736px) { + + h1 { + font-size: 2em; + margin: 0 0 1em 0; + } + + } + + @media screen and (max-width: 360px) { + + h1 { + font-size: 1.75em; + } + + } + + h2, h3, h4, h5, h6 { + font-weight: 900; + line-height: 1.5; + margin: 0 0 2em 0; + text-transform: uppercase; + letter-spacing: 0.35em; + } + + h2 a, h3 a, h4 a, h5 a, h6 a { + color: inherit; + } + + h2 { + font-size: 1.1em; + } + + h3 { + font-size: 1em; + } + + h4 { + font-size: 0.8em; + } + + h5 { + font-size: 0.8em; + } + + h6 { + font-size: 0.8em; + } + + @media screen and (max-width: 980px) { + + h1 br, h2 br, h3 br, h4 br, h5 br, h6 br { + display: none; + } + + } + + @media screen and (max-width: 736px) { + + h2 { + font-size: 1em; + } + + h3 { + font-size: 0.8em; + } + + } + + sub { + font-size: 0.8em; + position: relative; + top: 0.5em; + } + + sup { + font-size: 0.8em; + position: relative; + top: -0.5em; + } + + blockquote { + border-left: solid 4px #c9c9c9; + font-style: italic; + margin: 0 0 2em 0; + padding: 0.5em 0 0.5em 2em; + } + + code { + background: rgba(144, 144, 144, 0.075); + border-radius: 4px; + border: solid 1px #c9c9c9; + font-family: "Courier New", monospace; + font-size: 0.9em; + margin: 0 0.25em; + padding: 0.25em 0.65em; + } + + pre { + -webkit-overflow-scrolling: touch; + font-family: "Courier New", monospace; + font-size: 0.9em; + margin: 0 0 2em 0; + } + + pre code { + display: block; + line-height: 1.75; + padding: 1em 1.5em; + overflow-x: auto; + } + + hr { + border: 0; + border-bottom: solid 1px #c9c9c9; + margin: 2em 0; + } + + hr.major { + margin: 3em 0; + } + + .align-left { + text-align: left; + } + + .align-center { + text-align: center; + } + + .align-right { + text-align: right; + } + +/* Row */ + + .row { + display: flex; + flex-wrap: wrap; + box-sizing: border-box; + align-items: stretch; + } + + .row > * { + box-sizing: border-box; + } + + .row.gtr-uniform > * > :last-child { + margin-bottom: 0; + } + + .row.aln-left { + justify-content: flex-start; + } + + .row.aln-center { + justify-content: center; + } + + .row.aln-right { + justify-content: flex-end; + } + + .row.aln-top { + align-items: flex-start; + } + + .row.aln-middle { + align-items: center; + } + + .row.aln-bottom { + align-items: flex-end; + } + + .row > .imp { + order: -1; + } + + .row > .col-1 { + width: 8.33333%; + } + + .row > .off-1 { + margin-left: 8.33333%; + } + + .row > .col-2 { + width: 16.66667%; + } + + .row > .off-2 { + margin-left: 16.66667%; + } + + .row > .col-3 { + width: 25%; + } + + .row > .off-3 { + margin-left: 25%; + } + + .row > .col-4 { + width: 33.33333%; + } + + .row > .off-4 { + margin-left: 33.33333%; + } + + .row > .col-5 { + width: 41.66667%; + } + + .row > .off-5 { + margin-left: 41.66667%; + } + + .row > .col-6 { + width: 50%; + } + + .row > .off-6 { + margin-left: 50%; + } + + .row > .col-7 { + width: 58.33333%; + } + + .row > .off-7 { + margin-left: 58.33333%; + } + + .row > .col-8 { + width: 66.66667%; + } + + .row > .off-8 { + margin-left: 66.66667%; + } + + .row > .col-9 { + width: 75%; + } + + .row > .off-9 { + margin-left: 75%; + } + + .row > .col-10 { + width: 83.33333%; + } + + .row > .off-10 { + margin-left: 83.33333%; + } + + .row > .col-11 { + width: 91.66667%; + } + + .row > .off-11 { + margin-left: 91.66667%; + } + + .row > .col-12 { + width: 100%; + } + + .row > .off-12 { + margin-left: 100%; + } + + .row.gtr-0 { + margin-top: 0; + margin-left: 0em; + } + + .row.gtr-0 > * { + padding: 0 0 0 0em; + } + + .row.gtr-0.gtr-uniform { + margin-top: 0em; + } + + .row.gtr-0.gtr-uniform > * { + padding-top: 0em; + } + + .row.gtr-25 { + margin-top: 0; + margin-left: -0.5em; + } + + .row.gtr-25 > * { + padding: 0 0 0 0.5em; + } + + .row.gtr-25.gtr-uniform { + margin-top: -0.5em; + } + + .row.gtr-25.gtr-uniform > * { + padding-top: 0.5em; + } + + .row.gtr-50 { + margin-top: 0; + margin-left: -1em; + } + + .row.gtr-50 > * { + padding: 0 0 0 1em; + } + + .row.gtr-50.gtr-uniform { + margin-top: -1em; + } + + .row.gtr-50.gtr-uniform > * { + padding-top: 1em; + } + + .row { + margin-top: 0; + margin-left: -2em; + } + + .row > * { + padding: 0 0 0 2em; + } + + .row.gtr-uniform { + margin-top: -2em; + } + + .row.gtr-uniform > * { + padding-top: 2em; + } + + .row.gtr-150 { + margin-top: 0; + margin-left: -3em; + } + + .row.gtr-150 > * { + padding: 0 0 0 3em; + } + + .row.gtr-150.gtr-uniform { + margin-top: -3em; + } + + .row.gtr-150.gtr-uniform > * { + padding-top: 3em; + } + + .row.gtr-200 { + margin-top: 0; + margin-left: -4em; + } + + .row.gtr-200 > * { + padding: 0 0 0 4em; + } + + .row.gtr-200.gtr-uniform { + margin-top: -4em; + } + + .row.gtr-200.gtr-uniform > * { + padding-top: 4em; + } + + @media screen and (max-width: 1680px) { + + .row { + display: flex; + flex-wrap: wrap; + box-sizing: border-box; + align-items: stretch; + } + + .row > * { + box-sizing: border-box; + } + + .row.gtr-uniform > * > :last-child { + margin-bottom: 0; + } + + .row.aln-left { + justify-content: flex-start; + } + + .row.aln-center { + justify-content: center; + } + + .row.aln-right { + justify-content: flex-end; + } + + .row.aln-top { + align-items: flex-start; + } + + .row.aln-middle { + align-items: center; + } + + .row.aln-bottom { + align-items: flex-end; + } + + .row > .imp-xlarge { + order: -1; + } + + .row > .col-1-xlarge { + width: 8.33333%; + } + + .row > .off-1-xlarge { + margin-left: 8.33333%; + } + + .row > .col-2-xlarge { + width: 16.66667%; + } + + .row > .off-2-xlarge { + margin-left: 16.66667%; + } + + .row > .col-3-xlarge { + width: 25%; + } + + .row > .off-3-xlarge { + margin-left: 25%; + } + + .row > .col-4-xlarge { + width: 33.33333%; + } + + .row > .off-4-xlarge { + margin-left: 33.33333%; + } + + .row > .col-5-xlarge { + width: 41.66667%; + } + + .row > .off-5-xlarge { + margin-left: 41.66667%; + } + + .row > .col-6-xlarge { + width: 50%; + } + + .row > .off-6-xlarge { + margin-left: 50%; + } + + .row > .col-7-xlarge { + width: 58.33333%; + } + + .row > .off-7-xlarge { + margin-left: 58.33333%; + } + + .row > .col-8-xlarge { + width: 66.66667%; + } + + .row > .off-8-xlarge { + margin-left: 66.66667%; + } + + .row > .col-9-xlarge { + width: 75%; + } + + .row > .off-9-xlarge { + margin-left: 75%; + } + + .row > .col-10-xlarge { + width: 83.33333%; + } + + .row > .off-10-xlarge { + margin-left: 83.33333%; + } + + .row > .col-11-xlarge { + width: 91.66667%; + } + + .row > .off-11-xlarge { + margin-left: 91.66667%; + } + + .row > .col-12-xlarge { + width: 100%; + } + + .row > .off-12-xlarge { + margin-left: 100%; + } + + .row.gtr-0 { + margin-top: 0; + margin-left: 0em; + } + + .row.gtr-0 > * { + padding: 0 0 0 0em; + } + + .row.gtr-0.gtr-uniform { + margin-top: 0em; + } + + .row.gtr-0.gtr-uniform > * { + padding-top: 0em; + } + + .row.gtr-25 { + margin-top: 0; + margin-left: -0.5em; + } + + .row.gtr-25 > * { + padding: 0 0 0 0.5em; + } + + .row.gtr-25.gtr-uniform { + margin-top: -0.5em; + } + + .row.gtr-25.gtr-uniform > * { + padding-top: 0.5em; + } + + .row.gtr-50 { + margin-top: 0; + margin-left: -1em; + } + + .row.gtr-50 > * { + padding: 0 0 0 1em; + } + + .row.gtr-50.gtr-uniform { + margin-top: -1em; + } + + .row.gtr-50.gtr-uniform > * { + padding-top: 1em; + } + + .row { + margin-top: 0; + margin-left: -2em; + } + + .row > * { + padding: 0 0 0 2em; + } + + .row.gtr-uniform { + margin-top: -2em; + } + + .row.gtr-uniform > * { + padding-top: 2em; + } + + .row.gtr-150 { + margin-top: 0; + margin-left: -3em; + } + + .row.gtr-150 > * { + padding: 0 0 0 3em; + } + + .row.gtr-150.gtr-uniform { + margin-top: -3em; + } + + .row.gtr-150.gtr-uniform > * { + padding-top: 3em; + } + + .row.gtr-200 { + margin-top: 0; + margin-left: -4em; + } + + .row.gtr-200 > * { + padding: 0 0 0 4em; + } + + .row.gtr-200.gtr-uniform { + margin-top: -4em; + } + + .row.gtr-200.gtr-uniform > * { + padding-top: 4em; + } + + } + + @media screen and (max-width: 1280px) { + + .row { + display: flex; + flex-wrap: wrap; + box-sizing: border-box; + align-items: stretch; + } + + .row > * { + box-sizing: border-box; + } + + .row.gtr-uniform > * > :last-child { + margin-bottom: 0; + } + + .row.aln-left { + justify-content: flex-start; + } + + .row.aln-center { + justify-content: center; + } + + .row.aln-right { + justify-content: flex-end; + } + + .row.aln-top { + align-items: flex-start; + } + + .row.aln-middle { + align-items: center; + } + + .row.aln-bottom { + align-items: flex-end; + } + + .row > .imp-large { + order: -1; + } + + .row > .col-1-large { + width: 8.33333%; + } + + .row > .off-1-large { + margin-left: 8.33333%; + } + + .row > .col-2-large { + width: 16.66667%; + } + + .row > .off-2-large { + margin-left: 16.66667%; + } + + .row > .col-3-large { + width: 25%; + } + + .row > .off-3-large { + margin-left: 25%; + } + + .row > .col-4-large { + width: 33.33333%; + } + + .row > .off-4-large { + margin-left: 33.33333%; + } + + .row > .col-5-large { + width: 41.66667%; + } + + .row > .off-5-large { + margin-left: 41.66667%; + } + + .row > .col-6-large { + width: 50%; + } + + .row > .off-6-large { + margin-left: 50%; + } + + .row > .col-7-large { + width: 58.33333%; + } + + .row > .off-7-large { + margin-left: 58.33333%; + } + + .row > .col-8-large { + width: 66.66667%; + } + + .row > .off-8-large { + margin-left: 66.66667%; + } + + .row > .col-9-large { + width: 75%; + } + + .row > .off-9-large { + margin-left: 75%; + } + + .row > .col-10-large { + width: 83.33333%; + } + + .row > .off-10-large { + margin-left: 83.33333%; + } + + .row > .col-11-large { + width: 91.66667%; + } + + .row > .off-11-large { + margin-left: 91.66667%; + } + + .row > .col-12-large { + width: 100%; + } + + .row > .off-12-large { + margin-left: 100%; + } + + .row.gtr-0 { + margin-top: 0; + margin-left: 0em; + } + + .row.gtr-0 > * { + padding: 0 0 0 0em; + } + + .row.gtr-0.gtr-uniform { + margin-top: 0em; + } + + .row.gtr-0.gtr-uniform > * { + padding-top: 0em; + } + + .row.gtr-25 { + margin-top: 0; + margin-left: -0.5em; + } + + .row.gtr-25 > * { + padding: 0 0 0 0.5em; + } + + .row.gtr-25.gtr-uniform { + margin-top: -0.5em; + } + + .row.gtr-25.gtr-uniform > * { + padding-top: 0.5em; + } + + .row.gtr-50 { + margin-top: 0; + margin-left: -1em; + } + + .row.gtr-50 > * { + padding: 0 0 0 1em; + } + + .row.gtr-50.gtr-uniform { + margin-top: -1em; + } + + .row.gtr-50.gtr-uniform > * { + padding-top: 1em; + } + + .row { + margin-top: 0; + margin-left: -2em; + } + + .row > * { + padding: 0 0 0 2em; + } + + .row.gtr-uniform { + margin-top: -2em; + } + + .row.gtr-uniform > * { + padding-top: 2em; + } + + .row.gtr-150 { + margin-top: 0; + margin-left: -3em; + } + + .row.gtr-150 > * { + padding: 0 0 0 3em; + } + + .row.gtr-150.gtr-uniform { + margin-top: -3em; + } + + .row.gtr-150.gtr-uniform > * { + padding-top: 3em; + } + + .row.gtr-200 { + margin-top: 0; + margin-left: -4em; + } + + .row.gtr-200 > * { + padding: 0 0 0 4em; + } + + .row.gtr-200.gtr-uniform { + margin-top: -4em; + } + + .row.gtr-200.gtr-uniform > * { + padding-top: 4em; + } + + } + + @media screen and (max-width: 980px) { + + .row { + display: flex; + flex-wrap: wrap; + box-sizing: border-box; + align-items: stretch; + } + + .row > * { + box-sizing: border-box; + } + + .row.gtr-uniform > * > :last-child { + margin-bottom: 0; + } + + .row.aln-left { + justify-content: flex-start; + } + + .row.aln-center { + justify-content: center; + } + + .row.aln-right { + justify-content: flex-end; + } + + .row.aln-top { + align-items: flex-start; + } + + .row.aln-middle { + align-items: center; + } + + .row.aln-bottom { + align-items: flex-end; + } + + .row > .imp-medium { + order: -1; + } + + .row > .col-1-medium { + width: 8.33333%; + } + + .row > .off-1-medium { + margin-left: 8.33333%; + } + + .row > .col-2-medium { + width: 16.66667%; + } + + .row > .off-2-medium { + margin-left: 16.66667%; + } + + .row > .col-3-medium { + width: 25%; + } + + .row > .off-3-medium { + margin-left: 25%; + } + + .row > .col-4-medium { + width: 33.33333%; + } + + .row > .off-4-medium { + margin-left: 33.33333%; + } + + .row > .col-5-medium { + width: 41.66667%; + } + + .row > .off-5-medium { + margin-left: 41.66667%; + } + + .row > .col-6-medium { + width: 50%; + } + + .row > .off-6-medium { + margin-left: 50%; + } + + .row > .col-7-medium { + width: 58.33333%; + } + + .row > .off-7-medium { + margin-left: 58.33333%; + } + + .row > .col-8-medium { + width: 66.66667%; + } + + .row > .off-8-medium { + margin-left: 66.66667%; + } + + .row > .col-9-medium { + width: 75%; + } + + .row > .off-9-medium { + margin-left: 75%; + } + + .row > .col-10-medium { + width: 83.33333%; + } + + .row > .off-10-medium { + margin-left: 83.33333%; + } + + .row > .col-11-medium { + width: 91.66667%; + } + + .row > .off-11-medium { + margin-left: 91.66667%; + } + + .row > .col-12-medium { + width: 100%; + } + + .row > .off-12-medium { + margin-left: 100%; + } + + .row.gtr-0 { + margin-top: 0; + margin-left: 0em; + } + + .row.gtr-0 > * { + padding: 0 0 0 0em; + } + + .row.gtr-0.gtr-uniform { + margin-top: 0em; + } + + .row.gtr-0.gtr-uniform > * { + padding-top: 0em; + } + + .row.gtr-25 { + margin-top: 0; + margin-left: -0.375em; + } + + .row.gtr-25 > * { + padding: 0 0 0 0.375em; + } + + .row.gtr-25.gtr-uniform { + margin-top: -0.375em; + } + + .row.gtr-25.gtr-uniform > * { + padding-top: 0.375em; + } + + .row.gtr-50 { + margin-top: 0; + margin-left: -0.75em; + } + + .row.gtr-50 > * { + padding: 0 0 0 0.75em; + } + + .row.gtr-50.gtr-uniform { + margin-top: -0.75em; + } + + .row.gtr-50.gtr-uniform > * { + padding-top: 0.75em; + } + + .row { + margin-top: 0; + margin-left: -1.5em; + } + + .row > * { + padding: 0 0 0 1.5em; + } + + .row.gtr-uniform { + margin-top: -1.5em; + } + + .row.gtr-uniform > * { + padding-top: 1.5em; + } + + .row.gtr-150 { + margin-top: 0; + margin-left: -2.25em; + } + + .row.gtr-150 > * { + padding: 0 0 0 2.25em; + } + + .row.gtr-150.gtr-uniform { + margin-top: -2.25em; + } + + .row.gtr-150.gtr-uniform > * { + padding-top: 2.25em; + } + + .row.gtr-200 { + margin-top: 0; + margin-left: -3em; + } + + .row.gtr-200 > * { + padding: 0 0 0 3em; + } + + .row.gtr-200.gtr-uniform { + margin-top: -3em; + } + + .row.gtr-200.gtr-uniform > * { + padding-top: 3em; + } + + } + + @media screen and (max-width: 736px) { + + .row { + display: flex; + flex-wrap: wrap; + box-sizing: border-box; + align-items: stretch; + } + + .row > * { + box-sizing: border-box; + } + + .row.gtr-uniform > * > :last-child { + margin-bottom: 0; + } + + .row.aln-left { + justify-content: flex-start; + } + + .row.aln-center { + justify-content: center; + } + + .row.aln-right { + justify-content: flex-end; + } + + .row.aln-top { + align-items: flex-start; + } + + .row.aln-middle { + align-items: center; + } + + .row.aln-bottom { + align-items: flex-end; + } + + .row > .imp-small { + order: -1; + } + + .row > .col-1-small { + width: 8.33333%; + } + + .row > .off-1-small { + margin-left: 8.33333%; + } + + .row > .col-2-small { + width: 16.66667%; + } + + .row > .off-2-small { + margin-left: 16.66667%; + } + + .row > .col-3-small { + width: 25%; + } + + .row > .off-3-small { + margin-left: 25%; + } + + .row > .col-4-small { + width: 33.33333%; + } + + .row > .off-4-small { + margin-left: 33.33333%; + } + + .row > .col-5-small { + width: 41.66667%; + } + + .row > .off-5-small { + margin-left: 41.66667%; + } + + .row > .col-6-small { + width: 50%; + } + + .row > .off-6-small { + margin-left: 50%; + } + + .row > .col-7-small { + width: 58.33333%; + } + + .row > .off-7-small { + margin-left: 58.33333%; + } + + .row > .col-8-small { + width: 66.66667%; + } + + .row > .off-8-small { + margin-left: 66.66667%; + } + + .row > .col-9-small { + width: 75%; + } + + .row > .off-9-small { + margin-left: 75%; + } + + .row > .col-10-small { + width: 83.33333%; + } + + .row > .off-10-small { + margin-left: 83.33333%; + } + + .row > .col-11-small { + width: 91.66667%; + } + + .row > .off-11-small { + margin-left: 91.66667%; + } + + .row > .col-12-small { + width: 100%; + } + + .row > .off-12-small { + margin-left: 100%; + } + + .row.gtr-0 { + margin-top: 0; + margin-left: 0em; + } + + .row.gtr-0 > * { + padding: 0 0 0 0em; + } + + .row.gtr-0.gtr-uniform { + margin-top: 0em; + } + + .row.gtr-0.gtr-uniform > * { + padding-top: 0em; + } + + .row.gtr-25 { + margin-top: 0; + margin-left: -0.25em; + } + + .row.gtr-25 > * { + padding: 0 0 0 0.25em; + } + + .row.gtr-25.gtr-uniform { + margin-top: -0.25em; + } + + .row.gtr-25.gtr-uniform > * { + padding-top: 0.25em; + } + + .row.gtr-50 { + margin-top: 0; + margin-left: -0.5em; + } + + .row.gtr-50 > * { + padding: 0 0 0 0.5em; + } + + .row.gtr-50.gtr-uniform { + margin-top: -0.5em; + } + + .row.gtr-50.gtr-uniform > * { + padding-top: 0.5em; + } + + .row { + margin-top: 0; + margin-left: -1em; + } + + .row > * { + padding: 0 0 0 1em; + } + + .row.gtr-uniform { + margin-top: -1em; + } + + .row.gtr-uniform > * { + padding-top: 1em; + } + + .row.gtr-150 { + margin-top: 0; + margin-left: -1.5em; + } + + .row.gtr-150 > * { + padding: 0 0 0 1.5em; + } + + .row.gtr-150.gtr-uniform { + margin-top: -1.5em; + } + + .row.gtr-150.gtr-uniform > * { + padding-top: 1.5em; + } + + .row.gtr-200 { + margin-top: 0; + margin-left: -2em; + } + + .row.gtr-200 > * { + padding: 0 0 0 2em; + } + + .row.gtr-200.gtr-uniform { + margin-top: -2em; + } + + .row.gtr-200.gtr-uniform > * { + padding-top: 2em; + } + + } + + @media screen and (max-width: 480px) { + + .row { + display: flex; + flex-wrap: wrap; + box-sizing: border-box; + align-items: stretch; + } + + .row > * { + box-sizing: border-box; + } + + .row.gtr-uniform > * > :last-child { + margin-bottom: 0; + } + + .row.aln-left { + justify-content: flex-start; + } + + .row.aln-center { + justify-content: center; + } + + .row.aln-right { + justify-content: flex-end; + } + + .row.aln-top { + align-items: flex-start; + } + + .row.aln-middle { + align-items: center; + } + + .row.aln-bottom { + align-items: flex-end; + } + + .row > .imp-xsmall { + order: -1; + } + + .row > .col-1-xsmall { + width: 8.33333%; + } + + .row > .off-1-xsmall { + margin-left: 8.33333%; + } + + .row > .col-2-xsmall { + width: 16.66667%; + } + + .row > .off-2-xsmall { + margin-left: 16.66667%; + } + + .row > .col-3-xsmall { + width: 25%; + } + + .row > .off-3-xsmall { + margin-left: 25%; + } + + .row > .col-4-xsmall { + width: 33.33333%; + } + + .row > .off-4-xsmall { + margin-left: 33.33333%; + } + + .row > .col-5-xsmall { + width: 41.66667%; + } + + .row > .off-5-xsmall { + margin-left: 41.66667%; + } + + .row > .col-6-xsmall { + width: 50%; + } + + .row > .off-6-xsmall { + margin-left: 50%; + } + + .row > .col-7-xsmall { + width: 58.33333%; + } + + .row > .off-7-xsmall { + margin-left: 58.33333%; + } + + .row > .col-8-xsmall { + width: 66.66667%; + } + + .row > .off-8-xsmall { + margin-left: 66.66667%; + } + + .row > .col-9-xsmall { + width: 75%; + } + + .row > .off-9-xsmall { + margin-left: 75%; + } + + .row > .col-10-xsmall { + width: 83.33333%; + } + + .row > .off-10-xsmall { + margin-left: 83.33333%; + } + + .row > .col-11-xsmall { + width: 91.66667%; + } + + .row > .off-11-xsmall { + margin-left: 91.66667%; + } + + .row > .col-12-xsmall { + width: 100%; + } + + .row > .off-12-xsmall { + margin-left: 100%; + } + + .row.gtr-0 { + margin-top: 0; + margin-left: 0em; + } + + .row.gtr-0 > * { + padding: 0 0 0 0em; + } + + .row.gtr-0.gtr-uniform { + margin-top: 0em; + } + + .row.gtr-0.gtr-uniform > * { + padding-top: 0em; + } + + .row.gtr-25 { + margin-top: 0; + margin-left: -0.25em; + } + + .row.gtr-25 > * { + padding: 0 0 0 0.25em; + } + + .row.gtr-25.gtr-uniform { + margin-top: -0.25em; + } + + .row.gtr-25.gtr-uniform > * { + padding-top: 0.25em; + } + + .row.gtr-50 { + margin-top: 0; + margin-left: -0.5em; + } + + .row.gtr-50 > * { + padding: 0 0 0 0.5em; + } + + .row.gtr-50.gtr-uniform { + margin-top: -0.5em; + } + + .row.gtr-50.gtr-uniform > * { + padding-top: 0.5em; + } + + .row { + margin-top: 0; + margin-left: -1em; + } + + .row > * { + padding: 0 0 0 1em; + } + + .row.gtr-uniform { + margin-top: -1em; + } + + .row.gtr-uniform > * { + padding-top: 1em; + } + + .row.gtr-150 { + margin-top: 0; + margin-left: -1.5em; + } + + .row.gtr-150 > * { + padding: 0 0 0 1.5em; + } + + .row.gtr-150.gtr-uniform { + margin-top: -1.5em; + } + + .row.gtr-150.gtr-uniform > * { + padding-top: 1.5em; + } + + .row.gtr-200 { + margin-top: 0; + margin-left: -2em; + } + + .row.gtr-200 > * { + padding: 0 0 0 2em; + } + + .row.gtr-200.gtr-uniform { + margin-top: -2em; + } + + .row.gtr-200.gtr-uniform > * { + padding-top: 2em; + } + + } + +/* Section/Article */ + + section.special, article.special { + text-align: center; + } + + header p { + margin-top: -1em; + } + + @media screen and (max-width: 736px) { + + header p { + margin-top: 0; + } + + } + +/* Icon */ + + .icon { + text-decoration: none; + border-bottom: none; + position: relative; + } + + .icon:before { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + display: inline-block; + font-style: normal; + font-variant: normal; + text-rendering: auto; + line-height: 1; + text-transform: none !important; + font-family: 'Font Awesome 5 Free'; + font-weight: 400; + } + + .icon > .label { + display: none; + } + + .icon:before { + line-height: inherit; + } + + .icon.solid:before { + font-weight: 900; + } + + .icon.brands:before { + font-family: 'Font Awesome 5 Brands'; + } + + .icon.style2 { + -moz-transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out, border-color 0.2s ease-in-out; + -webkit-transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out, border-color 0.2s ease-in-out; + -ms-transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out, border-color 0.2s ease-in-out; + transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out, border-color 0.2s ease-in-out; + background-color: transparent; + border: solid 1px #c9c9c9; + border-radius: 4px; + width: 2.65em; + height: 2.65em; + display: inline-block; + text-align: center; + line-height: 2.65em; + color: inherit; + } + + .icon.style2:before { + font-size: 1.1em; + } + + .icon.style2:hover { + color: #f2849e; + border-color: #f2849e; + } + + .icon.style2:active { + background-color: rgba(242, 132, 158, 0.1); + } + +/* List */ + + ol { + list-style: decimal; + margin: 0 0 2em 0; + padding-left: 1.25em; + } + + ol li { + padding-left: 0.25em; + } + + ul { + list-style: disc; + margin: 0 0 2em 0; + padding-left: 1em; + } + + ul li { + padding-left: 0.5em; + } + + ul.alt { + list-style: none; + padding-left: 0; + } + + ul.alt li { + border-top: solid 1px #c9c9c9; + padding: 0.5em 0; + } + + ul.alt li:first-child { + border-top: 0; + padding-top: 0; + } + + dl { + margin: 0 0 2em 0; + } + + dl dt { + display: block; + font-weight: 900; + margin: 0 0 1em 0; + } + + dl dd { + margin-left: 2em; + } + +/* Actions */ + + ul.actions { + display: -moz-flex; + display: -webkit-flex; + display: -ms-flex; + display: flex; + cursor: default; + list-style: none; + margin-left: -1em; + padding-left: 0; + } + + ul.actions li { + padding: 0 0 0 1em; + vertical-align: middle; + } + + ul.actions.special { + -moz-justify-content: center; + -webkit-justify-content: center; + -ms-justify-content: center; + justify-content: center; + width: 100%; + margin-left: 0; + } + + ul.actions.special li:first-child { + padding-left: 0; + } + + ul.actions.stacked { + -moz-flex-direction: column; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + margin-left: 0; + } + + ul.actions.stacked li { + padding: 1.3em 0 0 0; + } + + ul.actions.stacked li:first-child { + padding-top: 0; + } + + ul.actions.fit { + width: calc(100% + 1em); + } + + ul.actions.fit li { + -moz-flex-grow: 1; + -webkit-flex-grow: 1; + -ms-flex-grow: 1; + flex-grow: 1; + -moz-flex-shrink: 1; + -webkit-flex-shrink: 1; + -ms-flex-shrink: 1; + flex-shrink: 1; + width: 100%; + } + + ul.actions.fit li > * { + width: 100%; + } + + ul.actions.fit.stacked { + width: 100%; + } + + @media screen and (max-width: 480px) { + + ul.actions:not(.fixed) { + -moz-flex-direction: column; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + margin-left: 0; + width: 100% !important; + } + + ul.actions:not(.fixed) li { + -moz-flex-grow: 1; + -webkit-flex-grow: 1; + -ms-flex-grow: 1; + flex-grow: 1; + -moz-flex-shrink: 1; + -webkit-flex-shrink: 1; + -ms-flex-shrink: 1; + flex-shrink: 1; + padding: 1em 0 0 0; + text-align: center; + width: 100%; + } + + ul.actions:not(.fixed) li > * { + width: 100%; + } + + ul.actions:not(.fixed) li:first-child { + padding-top: 0; + } + + ul.actions:not(.fixed) li input[type="submit"], + ul.actions:not(.fixed) li input[type="reset"], + ul.actions:not(.fixed) li input[type="button"], + ul.actions:not(.fixed) li button, + ul.actions:not(.fixed) li .button { + width: 100%; + } + + ul.actions:not(.fixed) li input[type="submit"].icon:before, + ul.actions:not(.fixed) li input[type="reset"].icon:before, + ul.actions:not(.fixed) li input[type="button"].icon:before, + ul.actions:not(.fixed) li button.icon:before, + ul.actions:not(.fixed) li .button.icon:before { + margin-left: -0.5rem; + } + + } + +/* Icons */ + + ul.icons { + cursor: default; + list-style: none; + padding-left: 0; + margin: -1em 0 2em -1em; + } + + ul.icons li { + display: inline-block; + padding: 1em 0 0 1em; + } + +/* Form */ + + form { + margin: 0 0 2em 0; + overflow-x: hidden; + } + + form > :last-child { + margin-bottom: 0; + } + + form > .fields { + display: -moz-flex; + display: -webkit-flex; + display: -ms-flex; + display: flex; + -moz-flex-wrap: wrap; + -webkit-flex-wrap: wrap; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + width: calc(100% + 3em); + margin: -1.5em 0 2em -1.5em; + } + + form > .fields > .field { + -moz-flex-grow: 0; + -webkit-flex-grow: 0; + -ms-flex-grow: 0; + flex-grow: 0; + -moz-flex-shrink: 0; + -webkit-flex-shrink: 0; + -ms-flex-shrink: 0; + flex-shrink: 0; + padding: 1.5em 0 0 1.5em; + width: calc(100% - 1.5em); + } + + form > .fields > .field.half { + width: calc(50% - 0.75em); + } + + form > .fields > .field.third { + width: calc(100%/3 - 0.5em); + } + + form > .fields > .field.quarter { + width: calc(25% - 0.375em); + } + + @media screen and (max-width: 480px) { + + form > .fields { + width: calc(100% + 3em); + margin: -1.5em 0 2em -1.5em; + } + + form > .fields > .field { + padding: 1.5em 0 0 1.5em; + width: calc(100% - 1.5em); + } + + form > .fields > .field.half { + width: calc(100% - 1.5em); + } + + form > .fields > .field.third { + width: calc(100% - 1.5em); + } + + form > .fields > .field.quarter { + width: calc(100% - 1.5em); + } + + } + + label { + display: block; + font-size: 0.9em; + font-weight: 900; + margin: 0 0 1em 0; + } + + input[type="text"], + input[type="password"], + input[type="email"], + input[type="tel"], + select, + textarea { + -moz-appearance: none; + -webkit-appearance: none; + -ms-appearance: none; + appearance: none; + background-color: transparent; + border: none; + border-radius: 0; + border-bottom: solid 1px #c9c9c9; + color: inherit; + display: block; + outline: 0; + padding: 0; + text-decoration: none; + width: 100%; + } + + input[type="text"]:invalid, + input[type="password"]:invalid, + input[type="email"]:invalid, + input[type="tel"]:invalid, + select:invalid, + textarea:invalid { + box-shadow: none; + } + + input[type="text"]:focus, + input[type="password"]:focus, + input[type="email"]:focus, + input[type="tel"]:focus, + select:focus, + textarea:focus { + border-bottom-color: #f2849e; + box-shadow: inset 0 -1px 0 0 #f2849e; + } + + select { + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40' preserveAspectRatio='none' viewBox='0 0 40 40'%3E%3Cpath d='M9.4,12.3l10.4,10.4l10.4-10.4c0.2-0.2,0.5-0.4,0.9-0.4c0.3,0,0.6,0.1,0.9,0.4l3.3,3.3c0.2,0.2,0.4,0.5,0.4,0.9 c0,0.4-0.1,0.6-0.4,0.9L20.7,31.9c-0.2,0.2-0.5,0.4-0.9,0.4c-0.3,0-0.6-0.1-0.9-0.4L4.3,17.3c-0.2-0.2-0.4-0.5-0.4-0.9 c0-0.4,0.1-0.6,0.4-0.9l3.3-3.3c0.2-0.2,0.5-0.4,0.9-0.4S9.1,12.1,9.4,12.3z' fill='%23c9c9c9' /%3E%3C/svg%3E"); + background-size: 1.25rem; + background-repeat: no-repeat; + background-position: calc(100% - 1rem) center; + height: 3em; + padding-right: 3em; + text-overflow: ellipsis; + } + + select option { + background: #ffffff; + } + + select:focus::-ms-value { + background-color: transparent; + } + + select::-ms-expand { + display: none; + } + + input[type="text"], + input[type="password"], + input[type="email"], + select { + height: 3em; + } + + textarea { + padding: 0; + min-height: 3.75em; + } + + input[type="checkbox"], + input[type="radio"] { + -moz-appearance: none; + -webkit-appearance: none; + -ms-appearance: none; + appearance: none; + display: block; + float: left; + margin-right: -2em; + opacity: 0; + width: 1em; + z-index: -1; + } + + input[type="checkbox"] + label, + input[type="radio"] + label { + text-decoration: none; + color: #585858; + cursor: pointer; + display: inline-block; + font-size: 1em; + font-weight: 300; + padding-left: 2.55em; + padding-right: 0.75em; + position: relative; + } + + input[type="checkbox"] + label:before, + input[type="radio"] + label:before { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + display: inline-block; + font-style: normal; + font-variant: normal; + text-rendering: auto; + line-height: 1; + text-transform: none !important; + font-family: 'Font Awesome 5 Free'; + font-weight: 900; + } + + input[type="checkbox"] + label:before, + input[type="radio"] + label:before { + border-radius: 4px; + border: solid 1px #c9c9c9; + content: ''; + display: inline-block; + font-size: 0.8em; + height: 2.25em; + left: 0; + line-height: 2.25em; + position: absolute; + text-align: center; + top: 0; + width: 2.25em; + } + + input[type="checkbox"]:checked + label:before, + input[type="radio"]:checked + label:before { + background: #585858; + border-color: #585858; + color: #ffffff; + content: '\f00c'; + } + + input[type="checkbox"]:focus + label:before, + input[type="radio"]:focus + label:before { + border-color: #f2849e; + box-shadow: 0 0 0 1px #f2849e; + } + + input[type="checkbox"] + label:before { + border-radius: 4px; + } + + input[type="radio"] + label:before { + border-radius: 100%; + } + +/* Box */ + + .box { + border-radius: 4px; + border: solid 1px #c9c9c9; + margin-bottom: 2em; + padding: 1.5em; + } + + .box > :last-child, + .box > :last-child > :last-child, + .box > :last-child > :last-child > :last-child { + margin-bottom: 0; + } + + .box.alt { + border: 0; + border-radius: 0; + padding: 0; + } + +/* Image */ + + .image { + border-radius: 4px; + border: 0; + display: inline-block; + position: relative; + } + + .image img { + border-radius: 4px; + display: block; + } + + .image.left, .image.right { + max-width: 40%; + } + + .image.left img, .image.right img { + width: 100%; + } + + .image.left { + float: left; + padding: 0 1.5em 1em 0; + top: 0.25em; + } + + .image.right { + float: right; + padding: 0 0 1em 1.5em; + top: 0.25em; + } + + .image.fit { + display: block; + margin: 0 0 2em 0; + width: 100%; + } + + .image.fit img { + width: 100%; + } + + .image.main { + display: block; + margin: 0 0 3em 0; + width: 100%; + } + + .image.main img { + width: 100%; + } + + @media screen and (max-width: 736px) { + + .image.main { + margin: 0 0 2em 0; + } + + } + +/* Table */ + + .table-wrapper { + -webkit-overflow-scrolling: touch; + overflow-x: auto; + } + + table { + margin: 0 0 2em 0; + width: 100%; + } + + table tbody tr { + border: solid 1px #c9c9c9; + border-left: 0; + border-right: 0; + } + + table tbody tr:nth-child(2n + 1) { + background-color: rgba(144, 144, 144, 0.075); + } + + table td { + padding: 0.75em 0.75em; + } + + table th { + font-size: 0.9em; + font-weight: 900; + padding: 0 0.75em 0.75em 0.75em; + text-align: left; + } + + table thead { + border-bottom: solid 2px #c9c9c9; + } + + table tfoot { + border-top: solid 2px #c9c9c9; + } + + table.alt { + border-collapse: separate; + } + + table.alt tbody tr td { + border: solid 1px #c9c9c9; + border-left-width: 0; + border-top-width: 0; + } + + table.alt tbody tr td:first-child { + border-left-width: 1px; + } + + table.alt tbody tr:first-child td { + border-top-width: 1px; + } + + table.alt thead { + border-bottom: 0; + } + + table.alt tfoot { + border-top: 0; + } + +/* Button */ + + input[type="submit"], + input[type="reset"], + input[type="button"], + button, + .button { + -moz-appearance: none; + -webkit-appearance: none; + -ms-appearance: none; + appearance: none; + -moz-transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out, box-shadow 0.2s ease-in-out; + -webkit-transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out, box-shadow 0.2s ease-in-out; + -ms-transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out, box-shadow 0.2s ease-in-out; + transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out, box-shadow 0.2s ease-in-out; + background-color: transparent; + border-radius: 4px; + border: 0; + box-shadow: inset 0 0 0 2px #585858; + color: #585858 !important; + cursor: pointer; + display: inline-block; + font-size: 0.8em; + font-weight: 900; + height: 3.5em; + letter-spacing: 0.35em; + line-height: 3.45em; + overflow: hidden; + padding: 0 1.25em 0 1.6em; + text-align: center; + text-decoration: none; + text-overflow: ellipsis; + text-transform: uppercase; + white-space: nowrap; + } + + input[type="submit"].icon:before, + input[type="reset"].icon:before, + input[type="button"].icon:before, + button.icon:before, + .button.icon:before { + margin-right: 0.5em; + } + + input[type="submit"].fit, + input[type="reset"].fit, + input[type="button"].fit, + button.fit, + .button.fit { + width: 100%; + } + + input[type="submit"]:hover, + input[type="reset"]:hover, + input[type="button"]:hover, + button:hover, + .button:hover { + color: #f2849e !important; + box-shadow: inset 0 0 0 2px #f2849e; + } + + input[type="submit"]:active, + input[type="reset"]:active, + input[type="button"]:active, + button:active, + .button:active { + background-color: rgba(242, 132, 158, 0.1); + } + + input[type="submit"].small, + input[type="reset"].small, + input[type="button"].small, + button.small, + .button.small { + font-size: 0.6em; + } + + input[type="submit"].large, + input[type="reset"].large, + input[type="button"].large, + button.large, + .button.large { + font-size: 1em; + } + + input[type="submit"].primary, + input[type="reset"].primary, + input[type="button"].primary, + button.primary, + .button.primary { + box-shadow: none; + background-color: #585858; + color: #ffffff !important; + } + + input[type="submit"].primary:hover, + input[type="reset"].primary:hover, + input[type="button"].primary:hover, + button.primary:hover, + .button.primary:hover { + background-color: #f2849e; + } + + input[type="submit"].primary:active, + input[type="reset"].primary:active, + input[type="button"].primary:active, + button.primary:active, + .button.primary:active { + background-color: #ee5f81; + } + + input[type="submit"].disabled, input[type="submit"]:disabled, + input[type="reset"].disabled, + input[type="reset"]:disabled, + input[type="button"].disabled, + input[type="button"]:disabled, + button.disabled, + button:disabled, + .button.disabled, + .button:disabled { + pointer-events: none; + opacity: 0.25; + } + +/* Tiles */ + + .tiles { + display: -moz-flex; + display: -webkit-flex; + display: -ms-flex; + display: flex; + -moz-flex-wrap: wrap; + -webkit-flex-wrap: wrap; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + postiion: relative; + margin: -2.5em 0 0 -2.5em; + } + + .tiles article { + -moz-transition: -moz-transform 0.5s ease, opacity 0.5s ease; + -webkit-transition: -webkit-transform 0.5s ease, opacity 0.5s ease; + -ms-transition: -ms-transform 0.5s ease, opacity 0.5s ease; + transition: transform 0.5s ease, opacity 0.5s ease; + position: relative; + width: calc(33.33333% - 2.5em); + margin: 2.5em 0 0 2.5em; + } + + .tiles article > .image { + -moz-transition: -moz-transform 0.5s ease; + -webkit-transition: -webkit-transform 0.5s ease; + -ms-transition: -ms-transform 0.5s ease; + transition: transform 0.5s ease; + position: relative; + display: block; + width: 100%; + border-radius: 4px; + overflow: hidden; + } + + .tiles article > .image img { + display: block; + width: 100%; + } + + .tiles article > .image:before { + pointer-events: none; + -moz-transition: background-color 0.5s ease, opacity 0.5s ease; + -webkit-transition: background-color 0.5s ease, opacity 0.5s ease; + -ms-transition: background-color 0.5s ease, opacity 0.5s ease; + transition: background-color 0.5s ease, opacity 0.5s ease; + content: ''; + display: block; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 1.0; + z-index: 1; + opacity: 0.8; + } + + .tiles article > .image:after { + pointer-events: none; + -moz-transition: opacity 0.5s ease; + -webkit-transition: opacity 0.5s ease; + -ms-transition: opacity 0.5s ease; + transition: opacity 0.5s ease; + content: ''; + display: block; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100' preserveAspectRatio='none'%3E%3Cstyle%3Eline %7B stroke-width: 0.25px%3B stroke: %23ffffff%3B %7D%3C/style%3E%3Cline x1='0' y1='0' x2='100' y2='100' /%3E%3Cline x1='100' y1='0' x2='0' y2='100' /%3E%3C/svg%3E"); + background-position: center; + background-repeat: no-repeat; + background-size: 100% 100%; + opacity: 0.25; + z-index: 2; + } + + .tiles article > a { + display: -moz-flex; + display: -webkit-flex; + display: -ms-flex; + display: flex; + -moz-flex-direction: column; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -moz-align-items: center; + -webkit-align-items: center; + -ms-align-items: center; + align-items: center; + -moz-justify-content: center; + -webkit-justify-content: center; + -ms-justify-content: center; + justify-content: center; + -moz-transition: background-color 0.5s ease, -moz-transform 0.5s ease; + -webkit-transition: background-color 0.5s ease, -webkit-transform 0.5s ease; + -ms-transition: background-color 0.5s ease, -ms-transform 0.5s ease; + transition: background-color 0.5s ease, transform 0.5s ease; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + padding: 1em; + border-radius: 4px; + border-bottom: 0; + color: #ffffff; + text-align: center; + text-decoration: none; + z-index: 3; + } + + .tiles article > a > :last-child { + margin: 0; + } + + .tiles article > a:hover { + color: #ffffff !important; + } + + .tiles article > a h2 { + margin: 0; + } + + .tiles article > a .content { + -moz-transition: max-height 0.5s ease, opacity 0.5s ease; + -webkit-transition: max-height 0.5s ease, opacity 0.5s ease; + -ms-transition: max-height 0.5s ease, opacity 0.5s ease; + transition: max-height 0.5s ease, opacity 0.5s ease; + width: 100%; + max-height: 0; + line-height: 1.5; + margin-top: 0.35em; + opacity: 0; + } + + .tiles article > a .content > :last-child { + margin-bottom: 0; + } + + .tiles article.style1 > .image:before { + background-color: #f2849e; + } + + .tiles article.style2 > .image:before { + background-color: #7ecaf6; + } + + .tiles article.style3 > .image:before { + background-color: #7bd0c1; + } + + .tiles article.style4 > .image:before { + background-color: #c75b9b; + } + + .tiles article.style5 > .image:before { + background-color: #ae85ca; + } + + .tiles article.style6 > .image:before { + background-color: #8499e7; + } + + body:not(.is-touch) .tiles article:hover > .image { + -moz-transform: scale(1.1); + -webkit-transform: scale(1.1); + -ms-transform: scale(1.1); + transform: scale(1.1); + } + + body:not(.is-touch) .tiles article:hover > .image:before { + background-color: #333333; + opacity: 0.35; + } + + body:not(.is-touch) .tiles article:hover > .image:after { + opacity: 0; + } + + body:not(.is-touch) .tiles article:hover .content { + max-height: 15em; + opacity: 1; + } + + * + .tiles { + margin-top: 2em; + } + + body.is-preload .tiles article { + -moz-transform: scale(0.9); + -webkit-transform: scale(0.9); + -ms-transform: scale(0.9); + transform: scale(0.9); + opacity: 0; + } + + body.is-touch .tiles article .content { + max-height: 15em; + opacity: 1; + } + + @media screen and (max-width: 1280px) { + + .tiles { + margin: -1.25em 0 0 -1.25em; + } + + .tiles article { + width: calc(33.33333% - 1.25em); + margin: 1.25em 0 0 1.25em; + } + + } + + @media screen and (max-width: 980px) { + + .tiles { + margin: -2.5em 0 0 -2.5em; + } + + .tiles article { + width: calc(50% - 2.5em); + margin: 2.5em 0 0 2.5em; + } + + } + + @media screen and (max-width: 736px) { + + .tiles { + margin: -1.25em 0 0 -1.25em; + } + + .tiles article { + width: calc(50% - 1.25em); + margin: 1.25em 0 0 1.25em; + } + + .tiles article:hover > .image { + -moz-transform: scale(1.0); + -webkit-transform: scale(1.0); + -ms-transform: scale(1.0); + transform: scale(1.0); + } + + } + + @media screen and (max-width: 480px) { + + .tiles { + margin: 0; + } + + .tiles article { + width: 100%; + margin: 1.25em 0 0 0; + } + + } + +/* Header */ + + #header { + padding: 1em 0 0.1em 0 ; + background-color: #f6f6f6; + + } + + #header .logo { + display: block; + border-bottom: 0; + color: inherit; + font-weight: 900; + letter-spacing: 0.35em; + margin: 0 0 2.5em 0; + text-decoration: none; + text-transform: uppercase; + display: inline-block; + } + + #header .logo > * { + display: inline-block; + vertical-align: middle; + } + + #header .logo .symbol { + margin-right: 0.65em; + } + + #header .logo .symbol img { + display: block; + width: 6em; + height: 6em; + } + + #header nav { + position: fixed; + right: 2em; + top: 2em; + z-index: 10000; + } + + #header nav ul { + display: -moz-flex; + display: -webkit-flex; + display: -ms-flex; + display: flex; + -moz-align-items: center; + -webkit-align-items: center; + -ms-align-items: center; + align-items: center; + list-style: none; + margin: 0; + padding: 0; + } + + #header nav ul li { + display: block; + padding: 0; + } + + #header nav ul li a { + display: block; + position: relative; + height: 3em; + line-height: 3em; + padding: 0 1.5em; + background-color: rgba(255, 255, 255, 0.5); + border-radius: 4px; + border: 0; + font-size: 0.8em; + font-weight: 900; + letter-spacing: 0.35em; + text-transform: uppercase; + } + + #header nav ul li a[href="#menu"] { + -webkit-tap-highlight-color: transparent; + width: 4em; + text-indent: 4em; + font-size: 1em; + overflow: hidden; + padding: 0; + white-space: nowrap; + } + + #header nav ul li a[href="#menu"]:before, #header nav ul li a[href="#menu"]:after { + -moz-transition: opacity 0.2s ease; + -webkit-transition: opacity 0.2s ease; + -ms-transition: opacity 0.2s ease; + transition: opacity 0.2s ease; + content: ''; + display: block; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-position: center; + background-repeat: no-repeat; + background-size: 2em 2em; + } + + #header nav ul li a[href="#menu"]:before { + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100' preserveAspectRatio='none'%3E%3Cstyle%3Eline %7B stroke-width: 8px%3B stroke: %23f2849e%3B %7D%3C/style%3E%3Cline x1='0' y1='25' x2='100' y2='25' /%3E%3Cline x1='0' y1='50' x2='100' y2='50' /%3E%3Cline x1='0' y1='75' x2='100' y2='75' /%3E%3C/svg%3E"); + opacity: 0; + } + + #header nav ul li a[href="#menu"]:after { + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100' preserveAspectRatio='none'%3E%3Cstyle%3Eline %7B stroke-width: 8px%3B stroke: %23585858%3B %7D%3C/style%3E%3Cline x1='0' y1='25' x2='100' y2='25' /%3E%3Cline x1='0' y1='50' x2='100' y2='50' /%3E%3Cline x1='0' y1='75' x2='100' y2='75' /%3E%3C/svg%3E"); + opacity: 1; + } + + #header nav ul li a[href="#menu"]:hover:before { + opacity: 1; + } + + #header nav ul li a[href="#menu"]:hover:after { + opacity: 0; + } + + @media screen and (max-width: 736px) { + + #header { + padding: 1em 0 0.1em 0 ; + } + + #header nav { + right: 0.5em; + top: 0.5em; + } + + #header nav ul li a[href="#menu"]:before, #header nav ul li a[href="#menu"]:after { + background-size: 1.5em 1.5em; + } + + } + +/* Menu */ + + #wrapper { + -moz-transition: opacity 0.45s ease; + -webkit-transition: opacity 0.45s ease; + -ms-transition: opacity 0.45s ease; + transition: opacity 0.45s ease; + opacity: 1; + } + + #menu { + -moz-transform: translateX(22em); + -webkit-transform: translateX(22em); + -ms-transform: translateX(22em); + transform: translateX(22em); + -moz-transition: -moz-transform 0.45s ease, visibility 0.45s; + -webkit-transition: -webkit-transform 0.45s ease, visibility 0.45s; + -ms-transition: -ms-transform 0.45s ease, visibility 0.45s; + transition: transform 0.45s ease, visibility 0.45s; + position: fixed; + top: 0; + right: 0; + width: 22em; + max-width: 80%; + height: 100%; + -webkit-overflow-scrolling: touch; + background: #585858; + color: #ffffff; + cursor: default; + visibility: hidden; + z-index: 10002; + } + + #menu > .inner { + -moz-transition: opacity 0.45s ease; + -webkit-transition: opacity 0.45s ease; + -ms-transition: opacity 0.45s ease; + transition: opacity 0.45s ease; + -webkit-overflow-scrolling: touch; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + padding: 2.75em; + opacity: 0; + overflow-y: auto; + } + + #menu > .inner > ul { + list-style: none; + margin: 0 0 1em 0; + padding: 0; + } + + #menu > .inner > ul > li { + padding: 0; + border-top: solid 1px rgba(255, 255, 255, 0.15); + } + + #menu > .inner > ul > li a { + display: block; + padding: 1em 0; + line-height: 1.5; + border: 0; + color: inherit; + } + + #menu > .inner > ul > li:first-child { + border-top: 0; + margin-top: -1em; + } + + #menu > .close { + -moz-transition: opacity 0.45s ease, -moz-transform 0.45s ease; + -webkit-transition: opacity 0.45s ease, -webkit-transform 0.45s ease; + -ms-transition: opacity 0.45s ease, -ms-transform 0.45s ease; + transition: opacity 0.45s ease, transform 0.45s ease; + -moz-transform: scale(0.25) rotate(180deg); + -webkit-transform: scale(0.25) rotate(180deg); + -ms-transform: scale(0.25) rotate(180deg); + transform: scale(0.25) rotate(180deg); + -webkit-tap-highlight-color: transparent; + display: block; + position: absolute; + top: 2em; + left: -6em; + width: 6em; + text-indent: 6em; + height: 3em; + border: 0; + font-size: 1em; + opacity: 0; + overflow: hidden; + padding: 0; + white-space: nowrap; + } + + #menu > .close:before, #menu > .close:after { + -moz-transition: opacity 0.2s ease; + -webkit-transition: opacity 0.2s ease; + -ms-transition: opacity 0.2s ease; + transition: opacity 0.2s ease; + content: ''; + display: block; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-position: center; + background-repeat: no-repeat; + background-size: 2em 2em; + } + + #menu > .close:before { + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100' preserveAspectRatio='none'%3E%3Cstyle%3Eline %7B stroke-width: 8px%3B stroke: %23f2849e%3B %7D%3C/style%3E%3Cline x1='15' y1='15' x2='85' y2='85' /%3E%3Cline x1='85' y1='15' x2='15' y2='85' /%3E%3C/svg%3E"); + opacity: 0; + } + + #menu > .close:after { + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100' preserveAspectRatio='none'%3E%3Cstyle%3Eline %7B stroke-width: 8px%3B stroke: %23585858%3B %7D%3C/style%3E%3Cline x1='15' y1='15' x2='85' y2='85' /%3E%3Cline x1='85' y1='15' x2='15' y2='85' /%3E%3C/svg%3E"); + opacity: 1; + } + + #menu > .close:hover:before { + opacity: 1; + } + + #menu > .close:hover:after { + opacity: 0; + } + + @media screen and (max-width: 736px) { + + #menu { + -moz-transform: translateX(16.5em); + -webkit-transform: translateX(16.5em); + -ms-transform: translateX(16.5em); + transform: translateX(16.5em); + width: 16.5em; + } + + #menu > .inner { + padding: 2.75em 1.5em; + } + + #menu > .close { + top: 0.5em; + left: -4.25em; + width: 4.25em; + text-indent: 4.25em; + } + + #menu > .close:before, #menu > .close:after { + background-size: 1.5em 1.5em; + } + + } + + body.is-menu-visible #wrapper { + pointer-events: none; + cursor: default; + opacity: 0.25; + } + + body.is-menu-visible #menu { + -moz-transform: translateX(0); + -webkit-transform: translateX(0); + -ms-transform: translateX(0); + transform: translateX(0); + visibility: visible; + } + + body.is-menu-visible #menu > * { + opacity: 1; + } + + body.is-menu-visible #menu .close { + -moz-transform: scale(1.0) rotate(0deg); + -webkit-transform: scale(1.0) rotate(0deg); + -ms-transform: scale(1.0) rotate(0deg); + transform: scale(1.0) rotate(0deg); + opacity: 1; + } + +/* Main */ + + #main { + padding: 0em 0 6em 0 ; + } + + @media screen and (max-width: 736px) { + + #main { + padding: 0em 0 4em 0 ; + } + + } + +/* Footer */ + + #footer { + padding: 5em 0 6em 0 ; + background-color: #f6f6f6; + } + + #footer > .inner { + display: -moz-flex; + display: -webkit-flex; + display: -ms-flex; + display: flex; + -moz-flex-wrap: wrap; + -webkit-flex-wrap: wrap; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -moz-flex-direction: row; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + } + + #footer > .inner > * > :last-child { + margin-bottom: 0; + } + + #footer > .inner section:nth-child(1) { + width: calc(66% - 2.5em); + margin-right: 2.5em; + } + + #footer > .inner section:nth-child(2) { + width: calc(33% - 2.5em); + margin-left: 2.5em; + } + + #footer > .inner .copyright { + width: 100%; + padding: 0; + margin-top: 5em; + list-style: none; + font-size: 0.8em; + color: rgba(88, 88, 88, 0.5); + } + + #footer > .inner .copyright a { + color: inherit; + } + + #footer > .inner .copyright li { + display: inline-block; + border-left: solid 1px rgba(88, 88, 88, 0.15); + line-height: 1; + padding: 0 0 0 1em; + margin: 0 0 0 1em; + } + + #footer > .inner .copyright li:first-child { + border-left: 0; + padding-left: 0; + margin-left: 0; + } + + @media screen and (max-width: 1280px) { + + #footer { + padding: 5em 0 3em 0 ; + } + + #footer > .inner section:nth-child(1) { + width: calc(66% - 1.25em); + margin-right: 1.25em; + } + + #footer > .inner section:nth-child(2) { + width: calc(33% - 1.25em); + margin-left: 1.25em; + } + + } + + @media screen and (max-width: 980px) { + + #footer > .inner section:nth-child(1) { + width: 66%; + margin-right: 0; + } + + #footer > .inner section:nth-child(2) { + width: calc(33% - 2.5em); + margin-left: 2.5em; + } + + } + + @media screen and (max-width: 736px) { + + #footer { + padding: 3em 0 1em 0 ; + } + + #footer > .inner { + -moz-flex-direction: column; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + } + + #footer > .inner section:nth-child(1) { + width: 100%; + margin-right: 0; + margin: 3em 0 0 0; + } + + #footer > .inner section:nth-child(2) { + -moz-order: -1; + -webkit-order: -1; + -ms-order: -1; + order: -1; + width: 100%; + margin-left: 0; + } + + #footer > .inner .copyright { + margin-top: 3em; + } + + } + + @media screen and (max-width: 480px) { + + #footer > .inner .copyright { + margin-top: 3em; + } + + #footer > .inner .copyright li { + border-left: 0; + padding-left: 0; + margin: 0.75em 0 0 0; + display: block; + line-height: inherit; + } + + #footer > .inner .copyright li:first-child { + margin-top: 0; + } + + } + +/* Wrapper */ + + #wrapper > * > .inner { + width: 100%; + max-width: 68em; + margin: 0 auto; + padding: 0 2.5em; + } + + @media screen and (max-width: 736px) { + + #wrapper > * > .inner { + padding: 0 1.25em; + } + + } + +/* add by fab */ +.blue { + color: #70cdff; +} +.yello { + color: #ffd683; +} diff --git a/dockers/web/html/assets/css/noscript.css b/dockers/web/html/assets/css/noscript.css new file mode 100644 index 0000000..aff83e6 --- /dev/null +++ b/dockers/web/html/assets/css/noscript.css @@ -0,0 +1,15 @@ +/* + Phantom by HTML5 UP + html5up.net | @ajlkn + Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) +*/ + +/* Tiles */ + + body.is-preload .tiles article { + -moz-transform: none; + -webkit-transform: none; + -ms-transform: none; + transform: none; + opacity: 1; + } \ No newline at end of file diff --git a/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwkxduz8A.woff2 b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwkxduz8A.woff2 new file mode 100644 index 0000000..3e95c64 Binary files /dev/null and b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwkxduz8A.woff2 differ diff --git a/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlBduz8A.woff2 b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlBduz8A.woff2 new file mode 100644 index 0000000..91d0ba4 Binary files /dev/null and b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlBduz8A.woff2 differ diff --git a/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdu.woff2 b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdu.woff2 new file mode 100644 index 0000000..ce34a9f Binary files /dev/null and b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdu.woff2 differ diff --git a/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmBduz8A.woff2 b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmBduz8A.woff2 new file mode 100644 index 0000000..2bd4bb9 Binary files /dev/null and b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmBduz8A.woff2 differ diff --git a/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmRduz8A.woff2 b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmRduz8A.woff2 new file mode 100644 index 0000000..9f9d48a Binary files /dev/null and b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmRduz8A.woff2 differ diff --git a/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmhduz8A.woff2 b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmhduz8A.woff2 new file mode 100644 index 0000000..8a690ae Binary files /dev/null and b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmhduz8A.woff2 differ diff --git a/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmxduz8A.woff2 b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmxduz8A.woff2 new file mode 100644 index 0000000..3498191 Binary files /dev/null and b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmxduz8A.woff2 differ diff --git a/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwkxduz8A.woff2 b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwkxduz8A.woff2 new file mode 100644 index 0000000..9fb8d5d Binary files /dev/null and b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwkxduz8A.woff2 differ diff --git a/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlBduz8A.woff2 b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlBduz8A.woff2 new file mode 100644 index 0000000..b2aaaae Binary files /dev/null and b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlBduz8A.woff2 differ diff --git a/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdu.woff2 b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdu.woff2 new file mode 100644 index 0000000..af998ca Binary files /dev/null and b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdu.woff2 differ diff --git a/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmBduz8A.woff2 b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmBduz8A.woff2 new file mode 100644 index 0000000..eb076e6 Binary files /dev/null and b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmBduz8A.woff2 differ diff --git a/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmRduz8A.woff2 b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmRduz8A.woff2 new file mode 100644 index 0000000..c76022d Binary files /dev/null and b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmRduz8A.woff2 differ diff --git a/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmxduz8A.woff2 b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmxduz8A.woff2 new file mode 100644 index 0000000..26adf1e Binary files /dev/null and b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmxduz8A.woff2 differ diff --git a/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwkxduz8A.woff2 b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwkxduz8A.woff2 new file mode 100644 index 0000000..4e2fc97 Binary files /dev/null and b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwkxduz8A.woff2 differ diff --git a/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwlBduz8A.woff2 b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwlBduz8A.woff2 new file mode 100644 index 0000000..d4fd879 Binary files /dev/null and b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwlBduz8A.woff2 differ diff --git a/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwlxdu.woff2 b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwlxdu.woff2 new file mode 100644 index 0000000..2cd26eb Binary files /dev/null and b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwlxdu.woff2 differ diff --git a/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmBduz8A.woff2 b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmBduz8A.woff2 new file mode 100644 index 0000000..e347571 Binary files /dev/null and b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmBduz8A.woff2 differ diff --git a/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmRduz8A.woff2 b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmRduz8A.woff2 new file mode 100644 index 0000000..85bdf21 Binary files /dev/null and b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmRduz8A.woff2 differ diff --git a/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmhduz8A.woff2 b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmhduz8A.woff2 new file mode 100644 index 0000000..04aefd6 Binary files /dev/null and b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmhduz8A.woff2 differ diff --git a/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmxduz8A.woff2 b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmxduz8A.woff2 new file mode 100644 index 0000000..1b58f91 Binary files /dev/null and b/dockers/web/html/assets/gfonts/6xKydSBYKcSV-LCoeQqfX1RYOo3iu4nwmxduz8A.woff2 differ diff --git a/dockers/web/html/assets/html/footer.html b/dockers/web/html/assets/html/footer.html new file mode 100644 index 0000000..f2175dd --- /dev/null +++ b/dockers/web/html/assets/html/footer.html @@ -0,0 +1,127 @@ + + + + +
+
+ +
+
+ + +
+
+ +
+ +

Nous contacter

+ Vous êtes du Morbihan (ou à coté) et vous souhaitez obtenir un compte gratuit chez kaz? +
+ Rien de plus simple, envoyez un mail à + 9cfff3f2e8fdffe8dcf7fde6b2fee6f4 + + en précisant le mail souhaité, vos nom, prénom et commune ou code postal. Si vous nous laissez un téléphone, c'est encore mieux. +
+ Et si vous connaissez un des membres fondateurs, n'hésitez pas à l'indiquer. C'est plus simple pour valider les inscriptions. +

+ Merci et à très bientôt! + + +
+ + +
+
+ KAZ +
+
+ + +
+
diff --git a/dockers/web/html/assets/html/header.html b/dockers/web/html/assets/html/header.html new file mode 100644 index 0000000..cd5272d --- /dev/null +++ b/dockers/web/html/assets/html/header.html @@ -0,0 +1,15 @@ + diff --git a/dockers/web/html/assets/html/insert_js.html b/dockers/web/html/assets/html/insert_js.html new file mode 100644 index 0000000..3449b79 --- /dev/null +++ b/dockers/web/html/assets/html/insert_js.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/dockers/web/html/assets/html/menu.html b/dockers/web/html/assets/html/menu.html new file mode 100644 index 0000000..82e87e0 --- /dev/null +++ b/dockers/web/html/assets/html/menu.html @@ -0,0 +1,13 @@ +

Menu

+ diff --git a/dockers/web/html/assets/js/HTMLInclude.js b/dockers/web/html/assets/js/HTMLInclude.js new file mode 100644 index 0000000..0147a8f --- /dev/null +++ b/dockers/web/html/assets/js/HTMLInclude.js @@ -0,0 +1,65 @@ +/*! HTMLInclude v1.1.1 | MIT License | github.com/paul-browne/HTMLInclude */ +!function(w, d) { + if (!w.HTMLInclude) { + w.HTMLInclude = function() { + function isInViewport(element, offset) { + return element.getBoundingClientRect().top <= (+offset + w.innerHeight); + } + function ajax(url, elements) { + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function() { + if (xhr.readyState == 4 && xhr.status == 200) { + elements.forEach(function(element) { + var dataReplace = element.getAttribute("data-replace"); + var z = xhr.responseText; + if (dataReplace) { + dataReplace.split(",").forEach(function(el) { + var o = el.trim().split(":"); + z = z.replace(new RegExp(o[0], "g"), o[1]); + }); + } + element.outerHTML = z; + var scripts = new DOMParser().parseFromString(z, 'text/html').querySelectorAll("SCRIPT"); + var i = 0; + var j = scripts.length; + while (i < j) { + var newScript = d.createElement("SCRIPT"); + scripts[i].src ? newScript.src = scripts[i].src : newScript.innerHTML = scripts[i].innerHTML; + d.head.appendChild(newScript); + i++; + } + }); + } + }; + xhr.open("GET", url, true); + xhr.send(); + } + function lazyLoad(element, offset) { + w.addEventListener("scroll", function scrollFunc() { + if (isInViewport(element, offset)) { + w.removeEventListener("scroll", scrollFunc); + ajax(element.getAttribute("data-include"), [element]); + } + }) + } + var store = {}; + var dis = d.querySelectorAll('[data-include]:not([data-in])'); + var i = dis.length; + while (i--) { + var di = dis[i].getAttribute('data-include'); + var laziness = dis[i].getAttribute('data-lazy'); + dis[i].setAttribute("data-in", ""); + if (!laziness || (laziness && isInViewport(dis[i], laziness))) { + store[di] = store[di] || []; + store[di].push(dis[i]); + } else { + lazyLoad(dis[i], laziness); + } + } + for (var key in store) { + ajax(key, store[key]); + } + } + } + w.HTMLInclude(); +}(window, document) \ No newline at end of file diff --git a/dockers/web/html/assets/js/HTMLInclude.min.js b/dockers/web/html/assets/js/HTMLInclude.min.js new file mode 100644 index 0000000..c1c2be3 --- /dev/null +++ b/dockers/web/html/assets/js/HTMLInclude.min.js @@ -0,0 +1,2 @@ +/*! HTMLInclude v1.1.1 | MIT License | github.com/paul-browne/HTMLInclude */ +!function(l,d){l.HTMLInclude||(l.HTMLInclude=function(){function r(t,e){return t.getBoundingClientRect().top<=+e+l.innerHeight}function a(t,e){var o=new XMLHttpRequest;o.onreadystatechange=function(){4==o.readyState&&200==o.status&&e.forEach(function(t){var e=t.getAttribute("data-replace"),n=o.responseText;e&&e.split(",").forEach(function(t){var e=t.trim().split(":");n=n.replace(new RegExp(e[0],"g"),e[1])}),t.outerHTML=n;for(var r=(new DOMParser).parseFromString(n,"text/html").querySelectorAll("SCRIPT"),a=0,i=r.length;a="==e.substr(0,2)?(a="gte",n=e.substr(2)):"<="==e.substr(0,2)?(a="lte",n=e.substr(2)):">"==e.substr(0,1)?(a="gt",n=e.substr(1)):"<"==e.substr(0,1)?(a="lt",n=e.substr(1)):"!"==e.substr(0,1)?(a="not",n=e.substr(1)):(a="eq",n=e),n&&n in t.list)if(i=t.list[n],Array.isArray(i)){if(r=parseInt(i[0]),d=parseInt(i[1]),isNaN(r)){if(isNaN(d))return;c=i[1].substr(String(d).length)}else c=i[0].substr(String(r).length);if(isNaN(r))switch(a){case"gte":s="screen";break;case"lte":s="screen and (max-width: "+d+c+")";break;case"gt":s="screen and (min-width: "+(d+1)+c+")";break;case"lt":s="screen and (max-width: -1px)";break;case"not":s="screen and (min-width: "+(d+1)+c+")";break;default:s="screen and (max-width: "+d+c+")"}else if(isNaN(d))switch(a){case"gte":s="screen and (min-width: "+r+c+")";break;case"lte":s="screen";break;case"gt":s="screen and (max-width: -1px)";break;case"lt":s="screen and (max-width: "+(r-1)+c+")";break;case"not":s="screen and (max-width: "+(r-1)+c+")";break;default:s="screen and (min-width: "+r+c+")"}else switch(a){case"gte":s="screen and (min-width: "+r+c+")";break;case"lte":s="screen and (max-width: "+d+c+")";break;case"gt":s="screen and (min-width: "+(d+1)+c+")";break;case"lt":s="screen and (max-width: "+(r-1)+c+")";break;case"not":s="screen and (max-width: "+(r-1)+c+"), screen and (min-width: "+(d+1)+c+")";break;default:s="screen and (min-width: "+r+c+") and (max-width: "+d+c+")"}}else s="("==i.charAt(0)?"screen and "+i:i;t.media[e]=!!s&&s}return t.media[e]!==!1&&window.matchMedia(t.media[e]).matches},on:function(e,n){t.events.push({query:e,handler:n,state:!1}),t.active(e)&&n()},poll:function(){var e,n;for(e=0;e0:!!("ontouchstart"in window),e.mobile="wp"==e.os||"android"==e.os||"ios"==e.os||"bb"==e.os}};return e.init(),e}();!function(e,n){"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?module.exports=n():e.browser=n()}(this,function(){return browser}); diff --git a/dockers/web/html/assets/js/jquery.min.js b/dockers/web/html/assets/js/jquery.min.js new file mode 100644 index 0000000..a1c07fd --- /dev/null +++ b/dockers/web/html/assets/js/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.4.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],E=C.document,r=Object.getPrototypeOf,s=t.slice,g=t.concat,u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.4.1",k=function(e,t){return new k.fn.init(e,t)},p=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function d(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp($),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+$),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ne=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(m.childNodes),m.childNodes),t[m.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&((e?e.ownerDocument||e:m)!==C&&T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!A[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&U.test(t)){(s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=k),o=(l=h(t)).length;while(o--)l[o]="#"+s+" "+xe(l[o]);c=l.join(","),f=ee.test(t)&&ye(e.parentNode)||e}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){A(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[k]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:m;return r!==C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),m!==C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=k,!C.getElementsByName||!C.getElementsByName(k).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+k+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+k+"+*").length||v.push(".#.+[+~]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",$)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e===C||e.ownerDocument===m&&y(m,e)?-1:t===C||t.ownerDocument===m&&y(m,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===C?-1:t===C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]===m?-1:s[r]===m?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==C&&T(e),d.matchesSelector&&E&&!A[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){A(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=p[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&p(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?k.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?k.grep(e,function(e){return e===n!==r}):"string"!=typeof n?k.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(k.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:L.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof k?t[0]:t,k.merge(this,k.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),D.test(r[1])&&k.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(k):k.makeArray(e,this)}).prototype=k.fn,q=k(E);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}k.fn.extend({has:function(e){var t=k(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?k.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;nx",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ee=/^([^.]*)(?:\.(.+)|)/;function ke(){return!0}function Se(){return!1}function Ne(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Se;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return k().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=k.guid++)),e.each(function(){k.event.add(this,t,i,r,n)})}function De(e,i,o){o?(Q.set(e,i,!1),k.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Q.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(k.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Q.set(this,i,r),t=o(this,i),this[i](),r!==(n=Q.get(this,i))||t?Q.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Q.set(this,i,{value:k.event.trigger(k.extend(r[0],k.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,i)&&k.event.add(e,i,ke)}k.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.get(t);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&k.find.matchesSelector(ie,i),n.guid||(n.guid=k.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof k&&k.event.triggered!==e.type?k.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(R)||[""]).length;while(l--)d=g=(s=Ee.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=k.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=k.event.special[d]||{},c=k.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&k.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),k.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(R)||[""]).length;while(l--)if(d=g=(s=Ee.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=k.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||k.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)k.event.remove(e,d+t[l],n,r,!0);k.isEmptyObject(u)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=k.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,"events")||{})[s.type]||[],c=k.event.special[s.type]||{};for(u[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,qe=/\s*$/g;function Oe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&k(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Re(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Me(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(Q.hasData(e)&&(o=Q.access(e),a=Q.set(t,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n")},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=oe(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||k.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Vt,Gt=[],Yt=/(=)\?(?=&|$)|\?\?/;k.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Gt.pop()||k.expando+"_"+kt++;return this[e]=!0,e}}),k.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Yt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Yt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Yt,"$1"+r):!1!==e.jsonp&&(e.url+=(St.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||k.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?k(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Gt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Vt=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Vt.childNodes.length),k.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=D.exec(e))?[t.createElement(i[1])]:(i=we([e],t,o),o&&o.length&&k(o).remove(),k.merge([],i.childNodes)));var r,i,o},k.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(k.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},k.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){k.fn[t]=function(e){return this.on(t,e)}}),k.expr.pseudos.animated=function(t){return k.grep(k.timers,function(e){return t===e.elem}).length},k.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=k.css(e,"position"),c=k(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=k.css(e,"top"),u=k.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,k.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},k.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){k.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===k.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===k.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=k(e).offset()).top+=k.css(e,"borderTopWidth",!0),i.left+=k.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-k.css(r,"marginTop",!0),left:t.left-i.left-k.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===k.css(e,"position"))e=e.offsetParent;return e||ie})}}),k.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;k.fn[t]=function(e){return _(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),k.each(["top","left"],function(e,n){k.cssHooks[n]=ze(y.pixelPosition,function(e,t){if(t)return t=_e(e,n),$e.test(t)?k(e).position()[n]+"px":t})}),k.each({Height:"height",Width:"width"},function(a,s){k.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){k.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return _(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?k.css(e,t,i):k.style(e,t,n,i)},s,n?e:void 0,n)}})}),k.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){k.fn[n]=function(e,t){return 0'), + $submits = $this.find('input[type="submit"]'); + + $this + .wrap($wrapper) + .attr('rows', 1) + .css('overflow', 'hidden') + .css('resize', 'none') + .on('keydown', function(event) { + + if (event.keyCode == 13 + && event.ctrlKey) { + + event.preventDefault(); + event.stopPropagation(); + + $(this).blur(); + + } + + }) + .on('blur focus', function() { + $this.val($.trim($this.val())); + }) + .on('input blur focus --init', function() { + + $wrapper + .css('height', $this.height()); + + $this + .css('height', 'auto') + .css('height', $this.prop('scrollHeight') + 'px'); + + }) + .on('keyup', function(event) { + + if (event.keyCode == 9) + $this + .select(); + + }) + .triggerHandler('--init'); + + // Fix. + if (browser.name == 'ie' + || browser.mobile) + $this + .css('max-height', '10em') + .css('overflow-y', 'auto'); + + }); + + // Menu. + var $menu = $('#menu'); + + $menu.wrapInner('
'); + + $menu._locked = false; + + $menu._lock = function() { + + if ($menu._locked) + return false; + + $menu._locked = true; + + window.setTimeout(function() { + $menu._locked = false; + }, 350); + + return true; + + }; + + $menu._show = function() { + + if ($menu._lock()) + $body.addClass('is-menu-visible'); + + }; + + $menu._hide = function() { + + if ($menu._lock()) + $body.removeClass('is-menu-visible'); + + }; + + $menu._toggle = function() { + + if ($menu._lock()) + $body.toggleClass('is-menu-visible'); + + }; + + $menu + .appendTo($body) + .on('click', function(event) { + event.stopPropagation(); + }) + .on('click', 'a', function(event) { + + var href = $(this).attr('href'); + + event.preventDefault(); + event.stopPropagation(); + + // Hide. + $menu._hide(); + + // Redirect. + if (href == '#menu') + return; + + window.setTimeout(function() { + window.location.href = href; + }, 350); + + }) + .append('Close'); + + $body + .on('click', 'a[href="#menu"]', function(event) { + + event.stopPropagation(); + event.preventDefault(); + + // Toggle. + $menu._toggle(); + + }) + .on('click', function(event) { + + // Hide. + $menu._hide(); + + }) + .on('keydown', function(event) { + + // Hide on escape. + if (event.keyCode == 27) + $menu._hide(); + + }); + +})(jQuery); \ No newline at end of file diff --git a/dockers/web/html/assets/js/util.js b/dockers/web/html/assets/js/util.js new file mode 100644 index 0000000..bdb8e9f --- /dev/null +++ b/dockers/web/html/assets/js/util.js @@ -0,0 +1,587 @@ +(function($) { + + /** + * Generate an indented list of links from a nav. Meant for use with panel(). + * @return {jQuery} jQuery object. + */ + $.fn.navList = function() { + + var $this = $(this); + $a = $this.find('a'), + b = []; + + $a.each(function() { + + var $this = $(this), + indent = Math.max(0, $this.parents('li').length - 1), + href = $this.attr('href'), + target = $this.attr('target'); + + b.push( + '' + + '' + + $this.text() + + '' + ); + + }); + + return b.join(''); + + }; + + /** + * Panel-ify an element. + * @param {object} userConfig User config. + * @return {jQuery} jQuery object. + */ + $.fn.panel = function(userConfig) { + + // No elements? + if (this.length == 0) + return $this; + + // Multiple elements? + if (this.length > 1) { + + for (var i=0; i < this.length; i++) + $(this[i]).panel(userConfig); + + return $this; + + } + + // Vars. + var $this = $(this), + $body = $('body'), + $window = $(window), + id = $this.attr('id'), + config; + + // Config. + config = $.extend({ + + // Delay. + delay: 0, + + // Hide panel on link click. + hideOnClick: false, + + // Hide panel on escape keypress. + hideOnEscape: false, + + // Hide panel on swipe. + hideOnSwipe: false, + + // Reset scroll position on hide. + resetScroll: false, + + // Reset forms on hide. + resetForms: false, + + // Side of viewport the panel will appear. + side: null, + + // Target element for "class". + target: $this, + + // Class to toggle. + visibleClass: 'visible' + + }, userConfig); + + // Expand "target" if it's not a jQuery object already. + if (typeof config.target != 'jQuery') + config.target = $(config.target); + + // Panel. + + // Methods. + $this._hide = function(event) { + + // Already hidden? Bail. + if (!config.target.hasClass(config.visibleClass)) + return; + + // If an event was provided, cancel it. + if (event) { + + event.preventDefault(); + event.stopPropagation(); + + } + + // Hide. + config.target.removeClass(config.visibleClass); + + // Post-hide stuff. + window.setTimeout(function() { + + // Reset scroll position. + if (config.resetScroll) + $this.scrollTop(0); + + // Reset forms. + if (config.resetForms) + $this.find('form').each(function() { + this.reset(); + }); + + }, config.delay); + + }; + + // Vendor fixes. + $this + .css('-ms-overflow-style', '-ms-autohiding-scrollbar') + .css('-webkit-overflow-scrolling', 'touch'); + + // Hide on click. + if (config.hideOnClick) { + + $this.find('a') + .css('-webkit-tap-highlight-color', 'rgba(0,0,0,0)'); + + $this + .on('click', 'a', function(event) { + + var $a = $(this), + href = $a.attr('href'), + target = $a.attr('target'); + + if (!href || href == '#' || href == '' || href == '#' + id) + return; + + // Cancel original event. + event.preventDefault(); + event.stopPropagation(); + + // Hide panel. + $this._hide(); + + // Redirect to href. + window.setTimeout(function() { + + if (target == '_blank') + window.open(href); + else + window.location.href = href; + + }, config.delay + 10); + + }); + + } + + // Event: Touch stuff. + $this.on('touchstart', function(event) { + + $this.touchPosX = event.originalEvent.touches[0].pageX; + $this.touchPosY = event.originalEvent.touches[0].pageY; + + }) + + $this.on('touchmove', function(event) { + + if ($this.touchPosX === null + || $this.touchPosY === null) + return; + + var diffX = $this.touchPosX - event.originalEvent.touches[0].pageX, + diffY = $this.touchPosY - event.originalEvent.touches[0].pageY, + th = $this.outerHeight(), + ts = ($this.get(0).scrollHeight - $this.scrollTop()); + + // Hide on swipe? + if (config.hideOnSwipe) { + + var result = false, + boundary = 20, + delta = 50; + + switch (config.side) { + + case 'left': + result = (diffY < boundary && diffY > (-1 * boundary)) && (diffX > delta); + break; + + case 'right': + result = (diffY < boundary && diffY > (-1 * boundary)) && (diffX < (-1 * delta)); + break; + + case 'top': + result = (diffX < boundary && diffX > (-1 * boundary)) && (diffY > delta); + break; + + case 'bottom': + result = (diffX < boundary && diffX > (-1 * boundary)) && (diffY < (-1 * delta)); + break; + + default: + break; + + } + + if (result) { + + $this.touchPosX = null; + $this.touchPosY = null; + $this._hide(); + + return false; + + } + + } + + // Prevent vertical scrolling past the top or bottom. + if (($this.scrollTop() < 0 && diffY < 0) + || (ts > (th - 2) && ts < (th + 2) && diffY > 0)) { + + event.preventDefault(); + event.stopPropagation(); + + } + + }); + + // Event: Prevent certain events inside the panel from bubbling. + $this.on('click touchend touchstart touchmove', function(event) { + event.stopPropagation(); + }); + + // Event: Hide panel if a child anchor tag pointing to its ID is clicked. + $this.on('click', 'a[href="#' + id + '"]', function(event) { + + event.preventDefault(); + event.stopPropagation(); + + config.target.removeClass(config.visibleClass); + + }); + + // Body. + + // Event: Hide panel on body click/tap. + $body.on('click touchend', function(event) { + $this._hide(event); + }); + + // Event: Toggle. + $body.on('click', 'a[href="#' + id + '"]', function(event) { + + event.preventDefault(); + event.stopPropagation(); + + config.target.toggleClass(config.visibleClass); + + }); + + // Window. + + // Event: Hide on ESC. + if (config.hideOnEscape) + $window.on('keydown', function(event) { + + if (event.keyCode == 27) + $this._hide(event); + + }); + + return $this; + + }; + + /** + * Apply "placeholder" attribute polyfill to one or more forms. + * @return {jQuery} jQuery object. + */ + $.fn.placeholder = function() { + + // Browser natively supports placeholders? Bail. + if (typeof (document.createElement('input')).placeholder != 'undefined') + return $(this); + + // No elements? + if (this.length == 0) + return $this; + + // Multiple elements? + if (this.length > 1) { + + for (var i=0; i < this.length; i++) + $(this[i]).placeholder(); + + return $this; + + } + + // Vars. + var $this = $(this); + + // Text, TextArea. + $this.find('input[type=text],textarea') + .each(function() { + + var i = $(this); + + if (i.val() == '' + || i.val() == i.attr('placeholder')) + i + .addClass('polyfill-placeholder') + .val(i.attr('placeholder')); + + }) + .on('blur', function() { + + var i = $(this); + + if (i.attr('name').match(/-polyfill-field$/)) + return; + + if (i.val() == '') + i + .addClass('polyfill-placeholder') + .val(i.attr('placeholder')); + + }) + .on('focus', function() { + + var i = $(this); + + if (i.attr('name').match(/-polyfill-field$/)) + return; + + if (i.val() == i.attr('placeholder')) + i + .removeClass('polyfill-placeholder') + .val(''); + + }); + + // Password. + $this.find('input[type=password]') + .each(function() { + + var i = $(this); + var x = $( + $('
') + .append(i.clone()) + .remove() + .html() + .replace(/type="password"/i, 'type="text"') + .replace(/type=password/i, 'type=text') + ); + + if (i.attr('id') != '') + x.attr('id', i.attr('id') + '-polyfill-field'); + + if (i.attr('name') != '') + x.attr('name', i.attr('name') + '-polyfill-field'); + + x.addClass('polyfill-placeholder') + .val(x.attr('placeholder')).insertAfter(i); + + if (i.val() == '') + i.hide(); + else + x.hide(); + + i + .on('blur', function(event) { + + event.preventDefault(); + + var x = i.parent().find('input[name=' + i.attr('name') + '-polyfill-field]'); + + if (i.val() == '') { + + i.hide(); + x.show(); + + } + + }); + + x + .on('focus', function(event) { + + event.preventDefault(); + + var i = x.parent().find('input[name=' + x.attr('name').replace('-polyfill-field', '') + ']'); + + x.hide(); + + i + .show() + .focus(); + + }) + .on('keypress', function(event) { + + event.preventDefault(); + x.val(''); + + }); + + }); + + // Events. + $this + .on('submit', function() { + + $this.find('input[type=text],input[type=password],textarea') + .each(function(event) { + + var i = $(this); + + if (i.attr('name').match(/-polyfill-field$/)) + i.attr('name', ''); + + if (i.val() == i.attr('placeholder')) { + + i.removeClass('polyfill-placeholder'); + i.val(''); + + } + + }); + + }) + .on('reset', function(event) { + + event.preventDefault(); + + $this.find('select') + .val($('option:first').val()); + + $this.find('input,textarea') + .each(function() { + + var i = $(this), + x; + + i.removeClass('polyfill-placeholder'); + + switch (this.type) { + + case 'submit': + case 'reset': + break; + + case 'password': + i.val(i.attr('defaultValue')); + + x = i.parent().find('input[name=' + i.attr('name') + '-polyfill-field]'); + + if (i.val() == '') { + i.hide(); + x.show(); + } + else { + i.show(); + x.hide(); + } + + break; + + case 'checkbox': + case 'radio': + i.attr('checked', i.attr('defaultValue')); + break; + + case 'text': + case 'textarea': + i.val(i.attr('defaultValue')); + + if (i.val() == '') { + i.addClass('polyfill-placeholder'); + i.val(i.attr('placeholder')); + } + + break; + + default: + i.val(i.attr('defaultValue')); + break; + + } + }); + + }); + + return $this; + + }; + + /** + * Moves elements to/from the first positions of their respective parents. + * @param {jQuery} $elements Elements (or selector) to move. + * @param {bool} condition If true, moves elements to the top. Otherwise, moves elements back to their original locations. + */ + $.prioritize = function($elements, condition) { + + var key = '__prioritize'; + + // Expand $elements if it's not already a jQuery object. + if (typeof $elements != 'jQuery') + $elements = $($elements); + + // Step through elements. + $elements.each(function() { + + var $e = $(this), $p, + $parent = $e.parent(); + + // No parent? Bail. + if ($parent.length == 0) + return; + + // Not moved? Move it. + if (!$e.data(key)) { + + // Condition is false? Bail. + if (!condition) + return; + + // Get placeholder (which will serve as our point of reference for when this element needs to move back). + $p = $e.prev(); + + // Couldn't find anything? Means this element's already at the top, so bail. + if ($p.length == 0) + return; + + // Move element to top of parent. + $e.prependTo($parent); + + // Mark element as moved. + $e.data(key, $p); + + } + + // Moved already? + else { + + // Condition is true? Bail. + if (condition) + return; + + $p = $e.data(key); + + // Move element back to its original location (using our placeholder). + $e.insertAfter($p); + + // Unmark element as moved. + $e.removeData(key); + + } + + }); + + }; + +})(jQuery); \ No newline at end of file diff --git a/dockers/web/html/assets/sass/base/_page.scss b/dockers/web/html/assets/sass/base/_page.scss new file mode 100644 index 0000000..1a7db82 --- /dev/null +++ b/dockers/web/html/assets/sass/base/_page.scss @@ -0,0 +1,47 @@ +/// +/// Phantom by HTML5 UP +/// html5up.net | @ajlkn +/// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) +/// + +/* Basic */ + + // MSIE: Required for IEMobile. + @-ms-viewport { + width: device-width; + } + + // MSIE: Prevents scrollbar from overlapping content. + body { + -ms-overflow-style: scrollbar; + } + + // Ensures page width is always >=320px. + @include breakpoint('<=xsmall') { + html, body { + min-width: 320px; + } + } + + // Set box model to border-box. + // Based on css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice + html { + box-sizing: border-box; + } + + *, *:before, *:after { + box-sizing: inherit; + } + + body { + background: _palette(bg); + + // Stops initial animations until page loads. + &.is-preload { + *, *:before, *:after { + @include vendor('animation', 'none !important'); + @include vendor('transition', 'none !important'); + } + } + + } \ No newline at end of file diff --git a/dockers/web/html/assets/sass/base/_reset.scss b/dockers/web/html/assets/sass/base/_reset.scss new file mode 100644 index 0000000..8fa4cff --- /dev/null +++ b/dockers/web/html/assets/sass/base/_reset.scss @@ -0,0 +1,76 @@ +/// +/// Phantom by HTML5 UP +/// html5up.net | @ajlkn +/// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) +/// + +// Reset. +// Based on meyerweb.com/eric/tools/css/reset (v2.0 | 20110126 | License: public domain) + + html, body, div, span, applet, object, + iframe, h1, h2, h3, h4, h5, h6, p, blockquote, + pre, a, abbr, acronym, address, big, cite, + code, del, dfn, em, img, ins, kbd, q, s, samp, + small, strike, strong, sub, sup, tt, var, b, + u, i, center, dl, dt, dd, ol, ul, li, fieldset, + form, label, legend, table, caption, tbody, + tfoot, thead, tr, th, td, article, aside, + canvas, details, embed, figure, figcaption, + footer, header, hgroup, menu, nav, output, ruby, + section, summary, time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; + } + + article, aside, details, figcaption, figure, + footer, header, hgroup, menu, nav, section { + display: block; + } + + body { + line-height: 1; + } + + ol, ul { + list-style:none; + } + + blockquote, q { + quotes: none; + + &:before, + &:after { + content: ''; + content: none; + } + } + + table { + border-collapse: collapse; + border-spacing: 0; + } + + body { + -webkit-text-size-adjust: none; + } + + mark { + background-color: transparent; + color: inherit; + } + + input::-moz-focus-inner { + border: 0; + padding: 0; + } + + input, select, textarea { + -moz-appearance: none; + -webkit-appearance: none; + -ms-appearance: none; + appearance: none; + } \ No newline at end of file diff --git a/dockers/web/html/assets/sass/base/_typography.scss b/dockers/web/html/assets/sass/base/_typography.scss new file mode 100644 index 0000000..2b081f6 --- /dev/null +++ b/dockers/web/html/assets/sass/base/_typography.scss @@ -0,0 +1,189 @@ +/// +/// Phantom by HTML5 UP +/// html5up.net | @ajlkn +/// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) +/// + +/* Type */ + + body, input, select, textarea { + color: _palette(fg); + font-family: _font(family); + font-size: 16pt; + font-weight: _font(weight); + line-height: 1.75; + + @include breakpoint('<=xlarge') { + font-size: 14pt; + } + + @include breakpoint('<=large') { + font-size: 12pt; + } + } + + a { + @include vendor('transition', ( + 'border-bottom-color #{_duration(transition)} ease', + 'color #{_duration(transition)} ease' + )); + text-decoration: none; + color: _palette(fg); + border-bottom: dotted 1px transparentize(_palette(fg), 0.5); + + &:hover { + border-bottom-color: transparent; + color: _palette(accent1) !important; + } + } + + strong, b { + color: _palette(fg-bold); + font-weight: _font(weight-bold); + } + + em, i { + font-style: italic; + } + + p { + margin: 0 0 _size(element-margin) 0; + } + + h1 { + font-size: 2.75em; + color: _palette(fg-bold); + font-weight: _font(weight-bold-alt); + line-height: 1.3; + margin: 0 0 (_size(element-margin) * 0.5) 0; + letter-spacing: _font(letter-spacing-alt); + + a { + color: inherit; + } + + @include breakpoint('<=small') { + font-size: 2em; + margin: 0 0 (_size(element-margin) * 0.5) 0; + } + + @include breakpoint('<=xxsmall') { + font-size: 1.75em; + } + } + + h2, h3, h4, h5, h6 { + color: _palette(fg-bold); + font-weight: _font(weight-bold); + line-height: 1.5; + margin: 0 0 (_size(element-margin) * 1) 0; + text-transform: uppercase; + letter-spacing: _font(letter-spacing); + + a { + color: inherit; + } + } + + h2 { + font-size: 1.1em; + } + + h3 { + font-size: 1em; + } + + h4 { + font-size: 0.8em; + } + + h5 { + font-size: 0.8em; + } + + h6 { + font-size: 0.8em; + } + + @include breakpoint('<=medium') { + h1, h2, h3, h4, h5, h6 { + br { + display: none; + } + } + } + + @include breakpoint('<=small') { + h2 { + font-size: 1em; + } + + h3 { + font-size: 0.8em; + } + } + + sub { + font-size: 0.8em; + position: relative; + top: 0.5em; + } + + sup { + font-size: 0.8em; + position: relative; + top: -0.5em; + } + + blockquote { + border-left: solid (_size(border-width) * 4) _palette(border); + font-style: italic; + margin: 0 0 _size(element-margin) 0; + padding: (_size(element-margin) / 4) 0 (_size(element-margin) / 4) _size(element-margin); + } + + code { + background: _palette(border-bg); + border-radius: _size(border-radius); + border: solid _size(border-width) _palette(border); + font-family: _font(family-fixed); + font-size: 0.9em; + margin: 0 0.25em; + padding: 0.25em 0.65em; + } + + pre { + -webkit-overflow-scrolling: touch; + font-family: _font(family-fixed); + font-size: 0.9em; + margin: 0 0 _size(element-margin) 0; + + code { + display: block; + line-height: 1.75; + padding: 1em 1.5em; + overflow-x: auto; + } + } + + hr { + border: 0; + border-bottom: solid _size(border-width) _palette(border); + margin: _size(element-margin) 0; + + &.major { + margin: (_size(element-margin) * 1.5) 0; + } + } + + .align-left { + text-align: left; + } + + .align-center { + text-align: center; + } + + .align-right { + text-align: right; + } \ No newline at end of file diff --git a/dockers/web/html/assets/sass/components/_actions.scss b/dockers/web/html/assets/sass/components/_actions.scss new file mode 100644 index 0000000..896ebcf --- /dev/null +++ b/dockers/web/html/assets/sass/components/_actions.scss @@ -0,0 +1,101 @@ +/// +/// Phantom by HTML5 UP +/// html5up.net | @ajlkn +/// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) +/// + +/* Actions */ + + ul.actions { + @include vendor('display', 'flex'); + cursor: default; + list-style: none; + margin-left: (_size(element-margin) * -0.5); + padding-left: 0; + + li { + padding: 0 0 0 (_size(element-margin) * 0.5); + vertical-align: middle; + } + + &.special { + @include vendor('justify-content', 'center'); + width: 100%; + margin-left: 0; + + li { + &:first-child { + padding-left: 0; + } + } + } + + &.stacked { + @include vendor('flex-direction', 'column'); + margin-left: 0; + + li { + padding: (_size(element-margin) * 0.65) 0 0 0; + + &:first-child { + padding-top: 0; + } + } + } + + &.fit { + width: calc(100% + #{_size(element-margin) * 0.5}); + + li { + @include vendor('flex-grow', '1'); + @include vendor('flex-shrink', '1'); + width: 100%; + + > * { + width: 100%; + } + } + + &.stacked { + width: 100%; + } + } + + @include breakpoint('<=xsmall') { + &:not(.fixed) { + @include vendor('flex-direction', 'column'); + margin-left: 0; + width: 100% !important; + + li { + @include vendor('flex-grow', '1'); + @include vendor('flex-shrink', '1'); + padding: (_size(element-margin) * 0.5) 0 0 0; + text-align: center; + width: 100%; + + > * { + width: 100%; + } + + &:first-child { + padding-top: 0; + } + + input[type="submit"], + input[type="reset"], + input[type="button"], + button, + .button { + width: 100%; + + &.icon { + &:before { + margin-left: -0.5rem; + } + } + } + } + } + } + } \ No newline at end of file diff --git a/dockers/web/html/assets/sass/components/_box.scss b/dockers/web/html/assets/sass/components/_box.scss new file mode 100644 index 0000000..74d3eba --- /dev/null +++ b/dockers/web/html/assets/sass/components/_box.scss @@ -0,0 +1,26 @@ +/// +/// Phantom by HTML5 UP +/// html5up.net | @ajlkn +/// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) +/// + +/* Box */ + + .box { + border-radius: _size(border-radius); + border: solid _size(border-width) _palette(border); + margin-bottom: _size(element-margin); + padding: 1.5em; + + > :last-child, + > :last-child > :last-child, + > :last-child > :last-child > :last-child { + margin-bottom: 0; + } + + &.alt { + border: 0; + border-radius: 0; + padding: 0; + } + } \ No newline at end of file diff --git a/dockers/web/html/assets/sass/components/_button.scss b/dockers/web/html/assets/sass/components/_button.scss new file mode 100644 index 0000000..04a3ea4 --- /dev/null +++ b/dockers/web/html/assets/sass/components/_button.scss @@ -0,0 +1,86 @@ +/// +/// Phantom by HTML5 UP +/// html5up.net | @ajlkn +/// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) +/// + +/* Button */ + + input[type="submit"], + input[type="reset"], + input[type="button"], + button, + .button { + @include vendor('appearance', 'none'); + @include vendor('transition', ( + 'background-color #{_duration(transition)} ease-in-out', + 'color #{_duration(transition)} ease-in-out', + 'box-shadow #{_duration(transition)} ease-in-out' + )); + background-color: transparent; + border-radius: _size(border-radius); + border: 0; + box-shadow: inset 0 0 0 (_size(border-width) * 2) _palette(fg); + color: _palette(fg) !important; + cursor: pointer; + display: inline-block; + font-size: 0.8em; + font-weight: _font(weight-bold); + height: 3.5em; + letter-spacing: _font(letter-spacing); + line-height: 3.45em; + overflow: hidden; + padding: 0 1.25em 0 #{1.25em + _font(letter-spacing)}; + text-align: center; + text-decoration: none; + text-overflow: ellipsis; + text-transform: uppercase; + white-space: nowrap; + + &.icon { + &:before { + margin-right: 0.5em; + } + } + + &.fit { + width: 100%; + } + + &:hover { + color: _palette(accent1) !important; + box-shadow: inset 0 0 0 (_size(border-width) * 2) _palette(accent1); + } + + &:active { + background-color: transparentize(_palette(accent1), 0.9); + } + + &.small { + font-size: 0.6em; + } + + &.large { + font-size: 1em; + } + + &.primary { + box-shadow: none; + background-color: _palette(fg); + color: _palette(bg) !important; + + &:hover { + background-color: _palette(accent1); + } + + &:active { + background-color: darken(_palette(accent1), 8); + } + } + + &.disabled, + &:disabled { + @include vendor('pointer-events', 'none'); + opacity: 0.25; + } + } \ No newline at end of file diff --git a/dockers/web/html/assets/sass/components/_form.scss b/dockers/web/html/assets/sass/components/_form.scss new file mode 100644 index 0000000..988b1aa --- /dev/null +++ b/dockers/web/html/assets/sass/components/_form.scss @@ -0,0 +1,212 @@ +/// +/// Phantom by HTML5 UP +/// html5up.net | @ajlkn +/// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) +/// + +/* Form */ + + form { + margin: 0 0 _size(element-margin) 0; + overflow-x: hidden; + + > :last-child { + margin-bottom: 0; + } + + > .fields { + $gutter: (_size(element-margin) * 0.75); + + @include vendor('display', 'flex'); + @include vendor('flex-wrap', 'wrap'); + width: calc(100% + #{$gutter * 2}); + margin: ($gutter * -1) 0 _size(element-margin) ($gutter * -1); + + > .field { + @include vendor('flex-grow', '0'); + @include vendor('flex-shrink', '0'); + padding: $gutter 0 0 $gutter; + width: calc(100% - #{$gutter * 1}); + + &.half { + width: calc(50% - #{$gutter * 0.5}); + } + + &.third { + width: calc(#{100% / 3} - #{$gutter * (1 / 3)}); + } + + &.quarter { + width: calc(25% - #{$gutter * 0.25}); + } + } + } + + @include breakpoint('<=xsmall') { + > .fields { + $gutter: (_size(element-margin) * 0.75); + + width: calc(100% + #{$gutter * 2}); + margin: ($gutter * -1) 0 _size(element-margin) ($gutter * -1); + + > .field { + padding: $gutter 0 0 $gutter; + width: calc(100% - #{$gutter * 1}); + + &.half { + width: calc(100% - #{$gutter * 1}); + } + + &.third { + width: calc(100% - #{$gutter * 1}); + } + + &.quarter { + width: calc(100% - #{$gutter * 1}); + } + } + } + } + } + + label { + display: block; + font-size: 0.9em; + font-weight: _font(weight-bold); + margin: 0 0 (_size(element-margin) * 0.5) 0; + } + + input[type="text"], + input[type="password"], + input[type="email"], + input[type="tel"], + select, + textarea { + @include vendor('appearance', 'none'); + background-color: transparent; + border: none; + border-radius: 0; + border-bottom: solid _size(border-width) _palette(border); + color: inherit; + display: block; + outline: 0; + padding: 0; + text-decoration: none; + width: 100%; + + &:invalid { + box-shadow: none; + } + + &:focus { + border-bottom-color: _palette(accent1); + box-shadow: inset 0 -1px 0 0 _palette(accent1); + } + } + + select { + background-image: svg-url(""); + background-size: 1.25rem; + background-repeat: no-repeat; + background-position: calc(100% - 1rem) center; + height: _size(element-height); + padding-right: _size(element-height); + text-overflow: ellipsis; + + option { + color: _palette(fg-bold); + background: _palette(bg); + } + + &:focus { + &::-ms-value { + background-color: transparent; + } + } + + &::-ms-expand { + display: none; + } + } + + input[type="text"], + input[type="password"], + input[type="email"], + select { + height: _size(element-height); + } + + textarea { + padding: 0; + min-height: (_size(element-height) * 1.25); + } + + input[type="checkbox"], + input[type="radio"], { + @include vendor('appearance', 'none'); + display: block; + float: left; + margin-right: -2em; + opacity: 0; + width: 1em; + z-index: -1; + + & + label { + @include icon(false, solid); + color: _palette(fg); + cursor: pointer; + display: inline-block; + font-size: 1em; + font-weight: _font(weight); + padding-left: (_size(element-height) * 0.6) + 0.75em; + padding-right: 0.75em; + position: relative; + + &:before { + border-radius: _size(border-radius); + border: solid _size(border-width) _palette(border); + content: ''; + display: inline-block; + font-size: 0.8em; + height: (_size(element-height) * 0.75); + left: 0; + line-height: (_size(element-height) * 0.75); + position: absolute; + text-align: center; + top: 0; + width: (_size(element-height) * 0.75); + } + } + + &:checked + label { + &:before { + background: _palette(fg); + border-color: _palette(fg); + color: _palette(bg); + content: '\f00c'; + } + } + + &:focus + label { + &:before { + border-color: _palette(accent1); + box-shadow: 0 0 0 _size(border-width) _palette(accent1); + } + } + } + + input[type="checkbox"] { + & + label { + &:before { + border-radius: _size(border-radius); + } + } + } + + input[type="radio"] { + & + label { + &:before { + border-radius: 100%; + } + } + } \ No newline at end of file diff --git a/dockers/web/html/assets/sass/components/_icon.scss b/dockers/web/html/assets/sass/components/_icon.scss new file mode 100644 index 0000000..70d2171 --- /dev/null +++ b/dockers/web/html/assets/sass/components/_icon.scss @@ -0,0 +1,66 @@ +/// +/// Phantom by HTML5 UP +/// html5up.net | @ajlkn +/// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) +/// + +/* Icon */ + + .icon { + @include icon; + border-bottom: none; + position: relative; + + > .label { + display: none; + } + + &:before { + line-height: inherit; + } + + &.solid { + &:before { + font-weight: 900; + } + } + + &.brands { + &:before { + font-family: 'Font Awesome 5 Brands'; + } + } + + &.style1 { + } + + &.style2 { + @include vendor('transition', ( + 'background-color #{_duration(transition)} ease-in-out', + 'color #{_duration(transition)} ease-in-out', + 'border-color #{_duration(transition)} ease-in-out' + )); + background-color: transparent; + border: solid 1px _palette(border); + border-radius: _size(border-radius); + width: 2.65em; + height: 2.65em; + display: inline-block; + text-align: center; + line-height: 2.65em; + color: inherit; + + &:before { + font-size: 1.1em; + } + + &:hover { + color: _palette(accent1); + border-color: _palette(accent1); + } + + &:active { + background-color: transparentize(_palette(accent1), 0.9); + } + } + } \ No newline at end of file diff --git a/dockers/web/html/assets/sass/components/_icons.scss b/dockers/web/html/assets/sass/components/_icons.scss new file mode 100644 index 0000000..d3f3679 --- /dev/null +++ b/dockers/web/html/assets/sass/components/_icons.scss @@ -0,0 +1,19 @@ +/// +/// Phantom by HTML5 UP +/// html5up.net | @ajlkn +/// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) +/// + +/* Icons */ + + ul.icons { + cursor: default; + list-style: none; + padding-left: 0; + margin: -1em 0 _size(element-margin) -1em; + + li { + display: inline-block; + padding: 1em 0 0 1em; + } + } \ No newline at end of file diff --git a/dockers/web/html/assets/sass/components/_image.scss b/dockers/web/html/assets/sass/components/_image.scss new file mode 100644 index 0000000..4cc5e40 --- /dev/null +++ b/dockers/web/html/assets/sass/components/_image.scss @@ -0,0 +1,64 @@ +/// +/// Phantom by HTML5 UP +/// html5up.net | @ajlkn +/// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) +/// + +/* Image */ + + .image { + border-radius: _size(border-radius); + border: 0; + display: inline-block; + position: relative; + + img { + border-radius: _size(border-radius); + display: block; + } + + &.left, + &.right { + max-width: 40%; + + img { + width: 100%; + } + } + + &.left { + float: left; + padding: 0 1.5em 1em 0; + top: 0.25em; + } + + &.right { + float: right; + padding: 0 0 1em 1.5em; + top: 0.25em; + } + + &.fit { + display: block; + margin: 0 0 _size(element-margin) 0; + width: 100%; + + img { + width: 100%; + } + } + + &.main { + display: block; + margin: 0 0 (_size(element-margin) * 1.5) 0; + width: 100%; + + img { + width: 100%; + } + + @include breakpoint('<=small') { + margin: 0 0 _size(element-margin) 0; + } + } + } \ No newline at end of file diff --git a/dockers/web/html/assets/sass/components/_list.scss b/dockers/web/html/assets/sass/components/_list.scss new file mode 100644 index 0000000..c14776b --- /dev/null +++ b/dockers/web/html/assets/sass/components/_list.scss @@ -0,0 +1,56 @@ +/// +/// Phantom by HTML5 UP +/// html5up.net | @ajlkn +/// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) +/// + +/* List */ + + ol { + list-style: decimal; + margin: 0 0 _size(element-margin) 0; + padding-left: 1.25em; + + li { + padding-left: 0.25em; + } + } + + ul { + list-style: disc; + margin: 0 0 _size(element-margin) 0; + padding-left: 1em; + + li { + padding-left: 0.5em; + } + + &.alt { + list-style: none; + padding-left: 0; + + li { + border-top: solid _size(border-width) _palette(border); + padding: 0.5em 0; + + &:first-child { + border-top: 0; + padding-top: 0; + } + } + } + } + + dl { + margin: 0 0 _size(element-margin) 0; + + dt { + display: block; + font-weight: _font(weight-bold); + margin: 0 0 (_size(element-margin) * 0.5) 0; + } + + dd { + margin-left: _size(element-margin); + } + } \ No newline at end of file diff --git a/dockers/web/html/assets/sass/components/_row.scss b/dockers/web/html/assets/sass/components/_row.scss new file mode 100644 index 0000000..ab75432 --- /dev/null +++ b/dockers/web/html/assets/sass/components/_row.scss @@ -0,0 +1,31 @@ +/// +/// Phantom by HTML5 UP +/// html5up.net | @ajlkn +/// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) +/// + +/* Row */ + + .row { + @include html-grid(2em); + + @include breakpoint('<=xlarge') { + @include html-grid(2em, 'xlarge'); + } + + @include breakpoint('<=large') { + @include html-grid(2em, 'large'); + } + + @include breakpoint('<=medium') { + @include html-grid(1.5em, 'medium'); + } + + @include breakpoint('<=small') { + @include html-grid(1em, 'small'); + } + + @include breakpoint('<=xsmall') { + @include html-grid(1em, 'xsmall'); + } + } \ No newline at end of file diff --git a/dockers/web/html/assets/sass/components/_section.scss b/dockers/web/html/assets/sass/components/_section.scss new file mode 100644 index 0000000..3cc4bc8 --- /dev/null +++ b/dockers/web/html/assets/sass/components/_section.scss @@ -0,0 +1,25 @@ +/// +/// Phantom by HTML5 UP +/// html5up.net | @ajlkn +/// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) +/// + +/* Section/Article */ + + section, article { + &.special { + text-align: center; + } + } + + header { + p { + margin-top: _size(element-margin) * -0.5; + } + + @include breakpoint('<=small') { + p { + margin-top: 0; + } + } + } \ No newline at end of file diff --git a/dockers/web/html/assets/sass/components/_table.scss b/dockers/web/html/assets/sass/components/_table.scss new file mode 100644 index 0000000..d6e8728 --- /dev/null +++ b/dockers/web/html/assets/sass/components/_table.scss @@ -0,0 +1,81 @@ +/// +/// Phantom by HTML5 UP +/// html5up.net | @ajlkn +/// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) +/// + +/* Table */ + + .table-wrapper { + -webkit-overflow-scrolling: touch; + overflow-x: auto; + } + + table { + margin: 0 0 _size(element-margin) 0; + width: 100%; + + tbody { + tr { + border: solid _size(border-width) _palette(border); + border-left: 0; + border-right: 0; + + &:nth-child(2n + 1) { + background-color: _palette(border-bg); + } + } + } + + td { + padding: 0.75em 0.75em; + } + + th { + color: _palette(fg-bold); + font-size: 0.9em; + font-weight: _font(weight-bold); + padding: 0 0.75em 0.75em 0.75em; + text-align: left; + } + + thead { + border-bottom: solid (_size(border-width) * 2) _palette(border); + } + + tfoot { + border-top: solid (_size(border-width) * 2) _palette(border); + } + + &.alt { + border-collapse: separate; + + tbody { + tr { + td { + border: solid _size(border-width) _palette(border); + border-left-width: 0; + border-top-width: 0; + + &:first-child { + border-left-width: _size(border-width); + } + } + + &:first-child { + td { + border-top-width: _size(border-width); + } + } + } + } + + thead { + border-bottom: 0; + } + + tfoot { + border-top: 0; + } + } + } \ No newline at end of file diff --git a/dockers/web/html/assets/sass/components/_tiles.scss b/dockers/web/html/assets/sass/components/_tiles.scss new file mode 100644 index 0000000..4c44652 --- /dev/null +++ b/dockers/web/html/assets/sass/components/_tiles.scss @@ -0,0 +1,258 @@ +/// +/// Phantom by HTML5 UP +/// html5up.net | @ajlkn +/// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) +/// + +/* Tiles */ + + .tiles { + $gutter: _size(gutter); + $duration: 0.5s; + $ease: 'ease'; + + @include vendor('display', 'flex'); + @include vendor('flex-wrap', 'wrap'); + postiion: relative; + margin: ($gutter * -1) 0 0 ($gutter * -1); + + article { + @include vendor('transition', ( + 'transform #{$duration} #{$ease}', + 'opacity #{$duration} #{$ease}' + )); + position: relative; + width: calc(#{(100% / 3)} - #{$gutter * 1}); + margin: $gutter 0 0 $gutter; + + > .image { + @include vendor('transition', 'transform #{$duration} #{$ease}'); + position: relative; + display: block; + width: 100%; + border-radius: _size(border-radius); + overflow: hidden; + + img { + display: block; + width: 100%; + } + + &:before { + @include vendor('pointer-events', 'none'); + @include vendor('transition', ( + 'background-color #{$duration} #{$ease}', + 'opacity #{$duration} #{$ease}' + )); + content: ''; + display: block; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 1.0; + z-index: 1; + opacity: 0.8; + } + + &:after { + @include vendor('pointer-events', 'none'); + @include vendor('transition', 'opacity #{$duration} #{$ease}'); + content: ''; + display: block; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-image: svg-url(''); + background-position: center; + background-repeat: no-repeat; + background-size: 100% 100%; + opacity: 0.25; + z-index: 2; + } + } + + > a { + @include vendor('display', 'flex'); + @include vendor('flex-direction', 'column'); + @include vendor('align-items', 'center'); + @include vendor('justify-content', 'center'); + @include vendor('transition', ( + 'background-color #{$duration} #{$ease}', + 'transform #{$duration} #{$ease}' + )); + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + padding: 1em; + border-radius: _size(border-radius); + border-bottom: 0; + color: _palette(fg-accent); + text-align: center; + text-decoration: none; + z-index: 3; + + > :last-child { + margin: 0; + } + + &:hover { + color: _palette(fg-accent) !important; + } + + h2 { + margin: 0; + } + + .content { + @include vendor('transition', ( + 'max-height #{$duration} #{$ease}', + 'opacity #{$duration} #{$ease}' + )); + width: 100%; + max-height: 0; + line-height: 1.5; + margin-top: 0.35em; + opacity: 0; + + > :last-child { + margin-bottom: 0; + } + } + } + + &.style1 { + > .image:before { + background-color: _palette(accent1); + } + } + + &.style2 { + > .image:before { + background-color: _palette(accent2); + } + } + + &.style3 { + > .image:before { + background-color: _palette(accent3); + } + } + + &.style4 { + > .image:before { + background-color: _palette(accent4); + } + } + + &.style5 { + > .image:before { + background-color: _palette(accent5); + } + } + + &.style6 { + > .image:before { + background-color: _palette(accent6); + } + } + + body:not(.is-touch) & { + &:hover { + > .image { + @include vendor('transform', 'scale(1.1)'); + + &:before { + background-color: _palette(bg-accent); + opacity: 0.35; + } + + &:after { + opacity: 0; + } + } + + .content { + max-height: 15em; + opacity: 1; + } + } + } + } + + * + & { + margin-top: _size(element-margin); + } + + body.is-preload & { + article { + @include vendor('transform', 'scale(0.9)'); + opacity: 0; + } + } + + body.is-touch & { + article { + .content { + max-height: 15em; + opacity: 1; + } + } + } + + @include breakpoint('<=large') { + $gutter: _size(gutter) * 0.5; + + margin: ($gutter * -1) 0 0 ($gutter * -1); + + article { + width: calc(#{(100% / 3)} - #{$gutter * 1}); + margin: $gutter 0 0 $gutter; + } + } + + @include breakpoint('<=medium') { + $gutter: _size(gutter); + + margin: ($gutter * -1) 0 0 ($gutter * -1); + + article { + width: calc(#{(100% / 2)} - #{$gutter * 1}); + margin: $gutter 0 0 $gutter; + } + } + + @include breakpoint('<=small') { + $gutter: _size(gutter) * 0.5; + + margin: ($gutter * -1) 0 0 ($gutter * -1); + + article { + width: calc(#{(100% / 2)} - #{$gutter * 1}); + margin: $gutter 0 0 $gutter; + + &:hover { + > .image { + @include vendor('transform', 'scale(1.0)'); + } + } + } + } + + @include breakpoint('<=xsmall') { + $gutter: _size(gutter) * 0.5; + + margin: 0; + + article { + width: 100%; + margin: $gutter 0 0 0; + } + } + } + diff --git a/dockers/web/html/assets/sass/layout/_footer.scss b/dockers/web/html/assets/sass/layout/_footer.scss new file mode 100644 index 0000000..367839e --- /dev/null +++ b/dockers/web/html/assets/sass/layout/_footer.scss @@ -0,0 +1,139 @@ +/// +/// Phantom by HTML5 UP +/// html5up.net | @ajlkn +/// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) +/// + +/* Footer */ + + #footer { + $gutter: _size(gutter); + + @include padding(5em, 0, (0, 0, 3em, 0)); + background-color: _palette(bg-alt); + + > .inner { + @include vendor('display', 'flex'); + @include vendor('flex-wrap', 'wrap'); + @include vendor('flex-direction', 'row'); + + > * > :last-child { + margin-bottom: 0; + } + + section:nth-child(1) { + width: calc(66% - #{$gutter}); + margin-right: $gutter; + } + + section:nth-child(2) { + width: calc(33% - #{$gutter}); + margin-left: $gutter; + } + + .copyright { + width: 100%; + padding: 0; + margin-top: 5em; + list-style: none; + font-size: 0.8em; + color: transparentize(_palette(fg), 0.5); + + a { + color: inherit; + } + + li { + display: inline-block; + border-left: solid 1px transparentize(_palette(fg), 0.85); + line-height: 1; + padding: 0 0 0 1em; + margin: 0 0 0 1em; + + &:first-child { + border-left: 0; + padding-left: 0; + margin-left: 0; + } + } + } + } + + @include breakpoint('<=large') { + $gutter: _size(gutter) * 0.5; + + @include padding(5em, 0); + + > .inner { + section:nth-child(1) { + width: calc(66% - #{$gutter}); + margin-right: $gutter; + } + + section:nth-child(2) { + width: calc(33% - #{$gutter}); + margin-left: $gutter; + } + } + } + + @include breakpoint('<=medium') { + $gutter: _size(gutter); + + > .inner { + section:nth-child(1) { + width: 66%; + margin-right: 0; + } + + section:nth-child(2) { + width: calc(33% - #{$gutter}); + margin-left: $gutter; + } + } + } + + @include breakpoint('<=small') { + @include padding(3em, 0); + + > .inner { + @include vendor('flex-direction', 'column'); + + section:nth-child(1) { + width: 100%; + margin-right: 0; + margin: 3em 0 0 0; + } + + section:nth-child(2) { + @include vendor('order', '-1'); + width: 100%; + margin-left: 0; + } + + .copyright { + margin-top: 3em; + } + } + } + + @include breakpoint('<=xsmall') { + > .inner { + .copyright { + margin-top: 3em; + + li { + border-left: 0; + padding-left: 0; + margin: 0.75em 0 0 0; + display: block; + line-height: inherit; + + &:first-child { + margin-top: 0; + } + } + } + } + } + } \ No newline at end of file diff --git a/dockers/web/html/assets/sass/layout/_header.scss b/dockers/web/html/assets/sass/layout/_header.scss new file mode 100644 index 0000000..47ea10c --- /dev/null +++ b/dockers/web/html/assets/sass/layout/_header.scss @@ -0,0 +1,136 @@ +/// +/// Phantom by HTML5 UP +/// html5up.net | @ajlkn +/// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) +/// + +/* Header */ + + #header { + @include padding(5em, 0, (3em, 0, -5em, 0)); + + .logo { + display: block; + border-bottom: 0; + color: inherit; + font-weight: _font(weight-bold); + letter-spacing: _font(letter-spacing); + margin: 0 0 (_size(element-margin) * 1.25) 0; + text-decoration: none; + text-transform: uppercase; + display: inline-block; + + > * { + display: inline-block; + vertical-align: middle; + } + + .symbol { + margin-right: 0.65em; + + img { + display: block; + width: 2em; + height: 2em; + } + } + } + + nav { + position: fixed; + right: 2em; + top: 2em; + z-index: _misc(z-index-base); + + ul { + @include vendor('display', 'flex'); + @include vendor('align-items', 'center'); + list-style: none; + margin: 0; + padding: 0; + + li { + display: block; + padding: 0; + + a { + display: block; + position: relative; + height: 3em; + line-height: 3em; + padding: 0 1.5em; + background-color: transparentize(_palette(bg), 0.5); + border-radius: _size(border-radius); + border: 0; + font-size: 0.8em; + font-weight: _font(weight-bold); + letter-spacing: _font(letter-spacing); + text-transform: uppercase; + } + + a[href="#menu"] { + -webkit-tap-highlight-color: transparent; + width: 4em; + text-indent: 4em; + font-size: 1em; + overflow: hidden; + padding: 0; + white-space: nowrap; + + &:before, &:after { + @include vendor('transition', 'opacity #{_duration(transition)} ease'); + content: ''; + display: block; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-position: center; + background-repeat: no-repeat; + background-size: 2em 2em; + } + + &:before { + background-image: svg-url(''); + opacity: 0; + } + + &:after { + background-image: svg-url(''); + opacity: 1; + } + + &:hover { + &:before { + opacity: 1; + } + + &:after { + opacity: 0; + } + } + } + } + } + } + + @include breakpoint('<=small') { + @include padding(3em, 0, (1em, 0, -3em, 0)); + + nav { + right: 0.5em; + top: 0.5em; + + ul { + li { + a[href="#menu"] { + &:before, &:after { + background-size: 1.5em 1.5em; + } + } + } + } + } + } + } \ No newline at end of file diff --git a/dockers/web/html/assets/sass/layout/_main.scss b/dockers/web/html/assets/sass/layout/_main.scss new file mode 100644 index 0000000..fd9df62 --- /dev/null +++ b/dockers/web/html/assets/sass/layout/_main.scss @@ -0,0 +1,15 @@ +/// +/// Phantom by HTML5 UP +/// html5up.net | @ajlkn +/// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) +/// + +/* Main */ + + #main { + @include padding(5em, 0, (-5em, 0, 3em, 0)); + + @include breakpoint('<=small') { + @include padding(3em, 0, (-3em, 0, 3em, 0)); + } + } \ No newline at end of file diff --git a/dockers/web/html/assets/sass/layout/_menu.scss b/dockers/web/html/assets/sass/layout/_menu.scss new file mode 100644 index 0000000..91d5bd4 --- /dev/null +++ b/dockers/web/html/assets/sass/layout/_menu.scss @@ -0,0 +1,164 @@ +/// +/// Phantom by HTML5 UP +/// html5up.net | @ajlkn +/// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) +/// + +/* Menu */ + + #wrapper { + @include vendor('transition', 'opacity #{_duration(menu)} ease'); + opacity: 1; + } + + #menu { + @include vendor('transform', 'translateX(#{_size(menu)})'); + @include vendor('transition', ('transform #{_duration(menu)} ease', 'visibility #{_duration(menu)}')); + position: fixed; + top: 0; + right: 0; + width: _size(menu); + max-width: 80%; + height: 100%; + -webkit-overflow-scrolling: touch; + background: _palette(fg); + color: _palette(bg); + cursor: default; + visibility: hidden; + z-index: _misc(z-index-base) + 2; + + > .inner { + @include vendor('transition', 'opacity #{_duration(menu)} ease'); + -webkit-overflow-scrolling: touch; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + padding: 2.75em; + opacity: 0; + overflow-y: auto; + + > ul { + list-style: none; + margin: 0 0 (_size(element-margin) * 0.5) 0; + padding: 0; + + > li { + padding: 0; + border-top: solid 1px transparentize(_palette(bg), 0.85); + + a { + display: block; + padding: 1em 0; + line-height: 1.5; + border: 0; + color: inherit; + } + + &:first-child { + border-top: 0; + margin-top: -1em; + } + } + } + } + + > .close { + @include vendor('transition', ( + 'opacity #{_duration(menu)} ease', + 'transform #{_duration(menu)} ease' + )); + @include vendor('transform', 'scale(0.25) rotate(180deg)'); + -webkit-tap-highlight-color: transparent; + display: block; + position: absolute; + top: 2em; + left: -6em; + width: 6em; + text-indent: 6em; + height: 3em; + border: 0; + font-size: 1em; + opacity: 0; + overflow: hidden; + padding: 0; + white-space: nowrap; + + &:before, &:after { + @include vendor('transition', 'opacity #{_duration(transition)} ease'); + content: ''; + display: block; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-position: center; + background-repeat: no-repeat; + background-size: 2em 2em; + } + + &:before { + background-image: svg-url(''); + opacity: 0; + } + + &:after { + background-image: svg-url(''); + opacity: 1; + } + + &:hover { + &:before { + opacity: 1; + } + + &:after { + opacity: 0; + } + } + } + + @include breakpoint('<=small') { + @include vendor('transform', 'translateX(#{_size(menu) * 0.75})'); + width: (_size(menu) * 0.75); + + > .inner { + padding: 2.75em 1.5em; + } + + > .close { + top: 0.5em; + left: -4.25em; + width: 4.25em; + text-indent: 4.25em; + + &:before, &:after { + background-size: 1.5em 1.5em; + } + } + } + } + + body.is-menu-visible { + #wrapper { + @include vendor('pointer-events', 'none'); + cursor: default; + opacity: 0.25; + } + + #menu { + @include vendor('transform', 'translateX(0)'); + visibility: visible; + + > * { + opacity: 1; + } + + .close { + @include vendor('transform', 'scale(1.0) rotate(0deg)'); + opacity: 1; + } + } + } \ No newline at end of file diff --git a/dockers/web/html/assets/sass/layout/_wrapper.scss b/dockers/web/html/assets/sass/layout/_wrapper.scss new file mode 100644 index 0000000..926a94f --- /dev/null +++ b/dockers/web/html/assets/sass/layout/_wrapper.scss @@ -0,0 +1,26 @@ +/// +/// Phantom by HTML5 UP +/// html5up.net | @ajlkn +/// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) +/// + +/* Wrapper */ + + #wrapper { + > * { + > .inner { + $gutter: _size(gutter); + + width: 100%; + max-width: _size(inner); + margin: 0 auto; + padding: 0 $gutter; + + @include breakpoint('<=small') { + $gutter: _size(gutter) * 0.5; + + padding: 0 $gutter; + } + } + } + } \ No newline at end of file diff --git a/dockers/web/html/assets/sass/libs/_breakpoints.scss b/dockers/web/html/assets/sass/libs/_breakpoints.scss new file mode 100644 index 0000000..c5301d8 --- /dev/null +++ b/dockers/web/html/assets/sass/libs/_breakpoints.scss @@ -0,0 +1,223 @@ +// breakpoints.scss v1.0 | @ajlkn | MIT licensed */ + +// Vars. + + /// Breakpoints. + /// @var {list} + $breakpoints: () !global; + +// Mixins. + + /// Sets breakpoints. + /// @param {map} $x Breakpoints. + @mixin breakpoints($x: ()) { + $breakpoints: $x !global; + } + + /// Wraps @content in a @media block targeting a specific orientation. + /// @param {string} $orientation Orientation. + @mixin orientation($orientation) { + @media screen and (orientation: #{$orientation}) { + @content; + } + } + + /// Wraps @content in a @media block using a given query. + /// @param {string} $query Query. + @mixin breakpoint($query: null) { + + $breakpoint: null; + $op: null; + $media: null; + + // Determine operator, breakpoint. + + // Greater than or equal. + @if (str-slice($query, 0, 2) == '>=') { + + $op: 'gte'; + $breakpoint: str-slice($query, 3); + + } + + // Less than or equal. + @elseif (str-slice($query, 0, 2) == '<=') { + + $op: 'lte'; + $breakpoint: str-slice($query, 3); + + } + + // Greater than. + @elseif (str-slice($query, 0, 1) == '>') { + + $op: 'gt'; + $breakpoint: str-slice($query, 2); + + } + + // Less than. + @elseif (str-slice($query, 0, 1) == '<') { + + $op: 'lt'; + $breakpoint: str-slice($query, 2); + + } + + // Not. + @elseif (str-slice($query, 0, 1) == '!') { + + $op: 'not'; + $breakpoint: str-slice($query, 2); + + } + + // Equal. + @else { + + $op: 'eq'; + $breakpoint: $query; + + } + + // Build media. + @if ($breakpoint and map-has-key($breakpoints, $breakpoint)) { + + $a: map-get($breakpoints, $breakpoint); + + // Range. + @if (type-of($a) == 'list') { + + $x: nth($a, 1); + $y: nth($a, 2); + + // Max only. + @if ($x == null) { + + // Greater than or equal (>= 0 / anything) + @if ($op == 'gte') { + $media: 'screen'; + } + + // Less than or equal (<= y) + @elseif ($op == 'lte') { + $media: 'screen and (max-width: ' + $y + ')'; + } + + // Greater than (> y) + @elseif ($op == 'gt') { + $media: 'screen and (min-width: ' + ($y + 1) + ')'; + } + + // Less than (< 0 / invalid) + @elseif ($op == 'lt') { + $media: 'screen and (max-width: -1px)'; + } + + // Not (> y) + @elseif ($op == 'not') { + $media: 'screen and (min-width: ' + ($y + 1) + ')'; + } + + // Equal (<= y) + @else { + $media: 'screen and (max-width: ' + $y + ')'; + } + + } + + // Min only. + @else if ($y == null) { + + // Greater than or equal (>= x) + @if ($op == 'gte') { + $media: 'screen and (min-width: ' + $x + ')'; + } + + // Less than or equal (<= inf / anything) + @elseif ($op == 'lte') { + $media: 'screen'; + } + + // Greater than (> inf / invalid) + @elseif ($op == 'gt') { + $media: 'screen and (max-width: -1px)'; + } + + // Less than (< x) + @elseif ($op == 'lt') { + $media: 'screen and (max-width: ' + ($x - 1) + ')'; + } + + // Not (< x) + @elseif ($op == 'not') { + $media: 'screen and (max-width: ' + ($x - 1) + ')'; + } + + // Equal (>= x) + @else { + $media: 'screen and (min-width: ' + $x + ')'; + } + + } + + // Min and max. + @else { + + // Greater than or equal (>= x) + @if ($op == 'gte') { + $media: 'screen and (min-width: ' + $x + ')'; + } + + // Less than or equal (<= y) + @elseif ($op == 'lte') { + $media: 'screen and (max-width: ' + $y + ')'; + } + + // Greater than (> y) + @elseif ($op == 'gt') { + $media: 'screen and (min-width: ' + ($y + 1) + ')'; + } + + // Less than (< x) + @elseif ($op == 'lt') { + $media: 'screen and (max-width: ' + ($x - 1) + ')'; + } + + // Not (< x and > y) + @elseif ($op == 'not') { + $media: 'screen and (max-width: ' + ($x - 1) + '), screen and (min-width: ' + ($y + 1) + ')'; + } + + // Equal (>= x and <= y) + @else { + $media: 'screen and (min-width: ' + $x + ') and (max-width: ' + $y + ')'; + } + + } + + } + + // String. + @else { + + // Missing a media type? Prefix with "screen". + @if (str-slice($a, 0, 1) == '(') { + $media: 'screen and ' + $a; + } + + // Otherwise, use as-is. + @else { + $media: $a; + } + + } + + } + + // Output. + @media #{$media} { + @content; + } + + } \ No newline at end of file diff --git a/dockers/web/html/assets/sass/libs/_functions.scss b/dockers/web/html/assets/sass/libs/_functions.scss new file mode 100644 index 0000000..f563aab --- /dev/null +++ b/dockers/web/html/assets/sass/libs/_functions.scss @@ -0,0 +1,90 @@ +/// Removes a specific item from a list. +/// @author Hugo Giraudel +/// @param {list} $list List. +/// @param {integer} $index Index. +/// @return {list} Updated list. +@function remove-nth($list, $index) { + + $result: null; + + @if type-of($index) != number { + @warn "$index: #{quote($index)} is not a number for `remove-nth`."; + } + @else if $index == 0 { + @warn "List index 0 must be a non-zero integer for `remove-nth`."; + } + @else if abs($index) > length($list) { + @warn "List index is #{$index} but list is only #{length($list)} item long for `remove-nth`."; + } + @else { + + $result: (); + $index: if($index < 0, length($list) + $index + 1, $index); + + @for $i from 1 through length($list) { + + @if $i != $index { + $result: append($result, nth($list, $i)); + } + + } + + } + + @return $result; + +} + +/// Gets a value from a map. +/// @author Hugo Giraudel +/// @param {map} $map Map. +/// @param {string} $keys Key(s). +/// @return {string} Value. +@function val($map, $keys...) { + + @if nth($keys, 1) == null { + $keys: remove-nth($keys, 1); + } + + @each $key in $keys { + $map: map-get($map, $key); + } + + @return $map; + +} + +/// Gets a duration value. +/// @param {string} $keys Key(s). +/// @return {string} Value. +@function _duration($keys...) { + @return val($duration, $keys...); +} + +/// Gets a font value. +/// @param {string} $keys Key(s). +/// @return {string} Value. +@function _font($keys...) { + @return val($font, $keys...); +} + +/// Gets a misc value. +/// @param {string} $keys Key(s). +/// @return {string} Value. +@function _misc($keys...) { + @return val($misc, $keys...); +} + +/// Gets a palette value. +/// @param {string} $keys Key(s). +/// @return {string} Value. +@function _palette($keys...) { + @return val($palette, $keys...); +} + +/// Gets a size value. +/// @param {string} $keys Key(s). +/// @return {string} Value. +@function _size($keys...) { + @return val($size, $keys...); +} \ No newline at end of file diff --git a/dockers/web/html/assets/sass/libs/_html-grid.scss b/dockers/web/html/assets/sass/libs/_html-grid.scss new file mode 100644 index 0000000..7438a8c --- /dev/null +++ b/dockers/web/html/assets/sass/libs/_html-grid.scss @@ -0,0 +1,149 @@ +// html-grid.scss v1.0 | @ajlkn | MIT licensed */ + +// Mixins. + + /// Initializes the current element as an HTML grid. + /// @param {mixed} $gutters Gutters (either a single number to set both column/row gutters, or a list to set them individually). + /// @param {mixed} $suffix Column class suffix (optional; either a single suffix or a list). + @mixin html-grid($gutters: 1.5em, $suffix: '') { + + // Initialize. + $cols: 12; + $multipliers: 0, 0.25, 0.5, 1, 1.50, 2.00; + $unit: 100% / $cols; + + // Suffixes. + $suffixes: null; + + @if (type-of($suffix) == 'list') { + $suffixes: $suffix; + } + @else { + $suffixes: ($suffix); + } + + // Gutters. + $guttersCols: null; + $guttersRows: null; + + @if (type-of($gutters) == 'list') { + + $guttersCols: nth($gutters, 1); + $guttersRows: nth($gutters, 2); + + } + @else { + + $guttersCols: $gutters; + $guttersRows: 0; + + } + + // Row. + display: flex; + flex-wrap: wrap; + box-sizing: border-box; + align-items: stretch; + + // Columns. + > * { + box-sizing: border-box; + } + + // Gutters. + &.gtr-uniform { + > * { + > :last-child { + margin-bottom: 0; + } + } + } + + // Alignment. + &.aln-left { + justify-content: flex-start; + } + + &.aln-center { + justify-content: center; + } + + &.aln-right { + justify-content: flex-end; + } + + &.aln-top { + align-items: flex-start; + } + + &.aln-middle { + align-items: center; + } + + &.aln-bottom { + align-items: flex-end; + } + + // Step through suffixes. + @each $suffix in $suffixes { + + // Suffix. + @if ($suffix != '') { + $suffix: '-' + $suffix; + } + @else { + $suffix: ''; + } + + // Row. + + // Important. + > .imp#{$suffix} { + order: -1; + } + + // Columns, offsets. + @for $i from 1 through $cols { + > .col-#{$i}#{$suffix} { + width: $unit * $i; + } + + > .off-#{$i}#{$suffix} { + margin-left: $unit * $i; + } + } + + // Step through multipliers. + @each $multiplier in $multipliers { + + // Gutters. + $class: null; + + @if ($multiplier != 1) { + $class: '.gtr-' + ($multiplier * 100); + } + + &#{$class} { + margin-top: ($guttersRows * $multiplier * -1); + margin-left: ($guttersCols * $multiplier * -1); + + > * { + padding: ($guttersRows * $multiplier) 0 0 ($guttersCols * $multiplier); + } + + // Uniform. + &.gtr-uniform { + margin-top: $guttersCols * $multiplier * -1; + + > * { + padding-top: $guttersCols * $multiplier; + } + } + + } + + } + + } + + } \ No newline at end of file diff --git a/dockers/web/html/assets/sass/libs/_mixins.scss b/dockers/web/html/assets/sass/libs/_mixins.scss new file mode 100644 index 0000000..a331483 --- /dev/null +++ b/dockers/web/html/assets/sass/libs/_mixins.scss @@ -0,0 +1,78 @@ +/// Makes an element's :before pseudoelement a FontAwesome icon. +/// @param {string} $content Optional content value to use. +/// @param {string} $category Optional category to use. +/// @param {string} $where Optional pseudoelement to target (before or after). +@mixin icon($content: false, $category: regular, $where: before) { + + text-decoration: none; + + &:#{$where} { + + @if $content { + content: $content; + } + + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + display: inline-block; + font-style: normal; + font-variant: normal; + text-rendering: auto; + line-height: 1; + text-transform: none !important; + + @if ($category == brands) { + font-family: 'Font Awesome 5 Brands'; + } + @elseif ($category == solid) { + font-family: 'Font Awesome 5 Free'; + font-weight: 900; + } + @else { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; + } + + } + +} + +/// Applies padding to an element, taking the current element-margin value into account. +/// @param {mixed} $tb Top/bottom padding. +/// @param {mixed} $lr Left/right padding. +/// @param {list} $pad Optional extra padding (in the following order top, right, bottom, left) +/// @param {bool} $important If true, adds !important. +@mixin padding($tb, $lr, $pad: (0,0,0,0), $important: null) { + + @if $important { + $important: '!important'; + } + + $x: 0.1em; + + @if unit(_size(element-margin)) == 'rem' { + $x: 0.1rem; + } + + padding: ($tb + nth($pad,1)) ($lr + nth($pad,2)) max($x, $tb - _size(element-margin) + nth($pad,3)) ($lr + nth($pad,4)) #{$important}; + +} + +/// Encodes a SVG data URL so IE doesn't choke (via codepen.io/jakob-e/pen/YXXBrp). +/// @param {string} $svg SVG data URL. +/// @return {string} Encoded SVG data URL. +@function svg-url($svg) { + + $svg: str-replace($svg, '"', '\''); + $svg: str-replace($svg, '%', '%25'); + $svg: str-replace($svg, '<', '%3C'); + $svg: str-replace($svg, '>', '%3E'); + $svg: str-replace($svg, '&', '%26'); + $svg: str-replace($svg, '#', '%23'); + $svg: str-replace($svg, '{', '%7B'); + $svg: str-replace($svg, '}', '%7D'); + $svg: str-replace($svg, ';', '%3B'); + + @return url("data:image/svg+xml;charset=utf8,#{$svg}"); + +} \ No newline at end of file diff --git a/dockers/web/html/assets/sass/libs/_vars.scss b/dockers/web/html/assets/sass/libs/_vars.scss new file mode 100644 index 0000000..9cea5e9 --- /dev/null +++ b/dockers/web/html/assets/sass/libs/_vars.scss @@ -0,0 +1,50 @@ +// Misc. + $misc: ( + z-index-base: 10000 + ); + +// Duration. + $duration: ( + menu: 0.45s, + transition: 0.2s + ); + +// Size. + $size: ( + border-radius: 4px, + border-width: 1px, + element-height: 3em, + element-margin: 2em, + gutter: 2.5em, + field-gutter: 2em, + inner: 68em, + menu: 22em + ); + +// Font. + $font: ( + family: ('Source Sans Pro', Helvetica, sans-serif), + family-fixed: ('Courier New', monospace), + weight: 300, + weight-bold: 900, + weight-bold-alt: 700, + letter-spacing: 0.35em, + letter-spacing-alt: -0.035em + ); + +// Palette. + $palette: ( + bg: #ffffff, + bg-accent: #333333, + bg-alt: #f6f6f6, + fg: #585858, + fg-accent: #ffffff, + border: #c9c9c9, + border-bg: rgba(144,144,144,0.075), + accent1: #f2849e, + accent2: #7ecaf6, + accent3: #7bd0c1, + accent4: #c75b9b, + accent5: #ae85ca, + accent6: #8499e7, + ); \ No newline at end of file diff --git a/dockers/web/html/assets/sass/libs/_vendor.scss b/dockers/web/html/assets/sass/libs/_vendor.scss new file mode 100644 index 0000000..6599a3f --- /dev/null +++ b/dockers/web/html/assets/sass/libs/_vendor.scss @@ -0,0 +1,376 @@ +// vendor.scss v1.0 | @ajlkn | MIT licensed */ + +// Vars. + + /// Vendor prefixes. + /// @var {list} + $vendor-prefixes: ( + '-moz-', + '-webkit-', + '-ms-', + '' + ); + + /// Properties that should be vendorized. + /// Data via caniuse.com, github.com/postcss/autoprefixer, and developer.mozilla.org + /// @var {list} + $vendor-properties: ( + + // Animation. + 'animation', + 'animation-delay', + 'animation-direction', + 'animation-duration', + 'animation-fill-mode', + 'animation-iteration-count', + 'animation-name', + 'animation-play-state', + 'animation-timing-function', + + // Appearance. + 'appearance', + + // Backdrop filter. + 'backdrop-filter', + + // Background image options. + 'background-clip', + 'background-origin', + 'background-size', + + // Box sizing. + 'box-sizing', + + // Clip path. + 'clip-path', + + // Filter effects. + 'filter', + + // Flexbox. + 'align-content', + 'align-items', + 'align-self', + 'flex', + 'flex-basis', + 'flex-direction', + 'flex-flow', + 'flex-grow', + 'flex-shrink', + 'flex-wrap', + 'justify-content', + 'order', + + // Font feature. + 'font-feature-settings', + 'font-language-override', + 'font-variant-ligatures', + + // Font kerning. + 'font-kerning', + + // Fragmented borders and backgrounds. + 'box-decoration-break', + + // Grid layout. + 'grid-column', + 'grid-column-align', + 'grid-column-end', + 'grid-column-start', + 'grid-row', + 'grid-row-align', + 'grid-row-end', + 'grid-row-start', + 'grid-template-columns', + 'grid-template-rows', + + // Hyphens. + 'hyphens', + 'word-break', + + // Masks. + 'mask', + 'mask-border', + 'mask-border-outset', + 'mask-border-repeat', + 'mask-border-slice', + 'mask-border-source', + 'mask-border-width', + 'mask-clip', + 'mask-composite', + 'mask-image', + 'mask-origin', + 'mask-position', + 'mask-repeat', + 'mask-size', + + // Multicolumn. + 'break-after', + 'break-before', + 'break-inside', + 'column-count', + 'column-fill', + 'column-gap', + 'column-rule', + 'column-rule-color', + 'column-rule-style', + 'column-rule-width', + 'column-span', + 'column-width', + 'columns', + + // Object fit. + 'object-fit', + 'object-position', + + // Regions. + 'flow-from', + 'flow-into', + 'region-fragment', + + // Scroll snap points. + 'scroll-snap-coordinate', + 'scroll-snap-destination', + 'scroll-snap-points-x', + 'scroll-snap-points-y', + 'scroll-snap-type', + + // Shapes. + 'shape-image-threshold', + 'shape-margin', + 'shape-outside', + + // Tab size. + 'tab-size', + + // Text align last. + 'text-align-last', + + // Text decoration. + 'text-decoration-color', + 'text-decoration-line', + 'text-decoration-skip', + 'text-decoration-style', + + // Text emphasis. + 'text-emphasis', + 'text-emphasis-color', + 'text-emphasis-position', + 'text-emphasis-style', + + // Text size adjust. + 'text-size-adjust', + + // Text spacing. + 'text-spacing', + + // Transform. + 'transform', + 'transform-origin', + + // Transform 3D. + 'backface-visibility', + 'perspective', + 'perspective-origin', + 'transform-style', + + // Transition. + 'transition', + 'transition-delay', + 'transition-duration', + 'transition-property', + 'transition-timing-function', + + // Unicode bidi. + 'unicode-bidi', + + // User select. + 'user-select', + + // Writing mode. + 'writing-mode', + + ); + + /// Values that should be vendorized. + /// Data via caniuse.com, github.com/postcss/autoprefixer, and developer.mozilla.org + /// @var {list} + $vendor-values: ( + + // Cross fade. + 'cross-fade', + + // Element function. + 'element', + + // Filter function. + 'filter', + + // Flexbox. + 'flex', + 'inline-flex', + + // Grab cursors. + 'grab', + 'grabbing', + + // Gradients. + 'linear-gradient', + 'repeating-linear-gradient', + 'radial-gradient', + 'repeating-radial-gradient', + + // Grid layout. + 'grid', + 'inline-grid', + + // Image set. + 'image-set', + + // Intrinsic width. + 'max-content', + 'min-content', + 'fit-content', + 'fill', + 'fill-available', + 'stretch', + + // Sticky position. + 'sticky', + + // Transform. + 'transform', + + // Zoom cursors. + 'zoom-in', + 'zoom-out', + + ); + +// Functions. + + /// Removes a specific item from a list. + /// @author Hugo Giraudel + /// @param {list} $list List. + /// @param {integer} $index Index. + /// @return {list} Updated list. + @function remove-nth($list, $index) { + + $result: null; + + @if type-of($index) != number { + @warn "$index: #{quote($index)} is not a number for `remove-nth`."; + } + @else if $index == 0 { + @warn "List index 0 must be a non-zero integer for `remove-nth`."; + } + @else if abs($index) > length($list) { + @warn "List index is #{$index} but list is only #{length($list)} item long for `remove-nth`."; + } + @else { + + $result: (); + $index: if($index < 0, length($list) + $index + 1, $index); + + @for $i from 1 through length($list) { + + @if $i != $index { + $result: append($result, nth($list, $i)); + } + + } + + } + + @return $result; + + } + + /// Replaces a substring within another string. + /// @author Hugo Giraudel + /// @param {string} $string String. + /// @param {string} $search Substring. + /// @param {string} $replace Replacement. + /// @return {string} Updated string. + @function str-replace($string, $search, $replace: '') { + + $index: str-index($string, $search); + + @if $index { + @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace); + } + + @return $string; + + } + + /// Replaces a substring within each string in a list. + /// @param {list} $strings List of strings. + /// @param {string} $search Substring. + /// @param {string} $replace Replacement. + /// @return {list} Updated list of strings. + @function str-replace-all($strings, $search, $replace: '') { + + @each $string in $strings { + $strings: set-nth($strings, index($strings, $string), str-replace($string, $search, $replace)); + } + + @return $strings; + + } + +// Mixins. + + /// Wraps @content in vendorized keyframe blocks. + /// @param {string} $name Name. + @mixin keyframes($name) { + + @-moz-keyframes #{$name} { @content; } + @-webkit-keyframes #{$name} { @content; } + @-ms-keyframes #{$name} { @content; } + @keyframes #{$name} { @content; } + + } + + /// Vendorizes a declaration's property and/or value(s). + /// @param {string} $property Property. + /// @param {mixed} $value String/list of value(s). + @mixin vendor($property, $value) { + + // Determine if property should expand. + $expandProperty: index($vendor-properties, $property); + + // Determine if value should expand (and if so, add '-prefix-' placeholder). + $expandValue: false; + + @each $x in $value { + @each $y in $vendor-values { + @if $y == str-slice($x, 1, str-length($y)) { + + $value: set-nth($value, index($value, $x), '-prefix-' + $x); + $expandValue: true; + + } + } + } + + // Expand property? + @if $expandProperty { + @each $vendor in $vendor-prefixes { + #{$vendor}#{$property}: #{str-replace-all($value, '-prefix-', $vendor)}; + } + } + + // Expand just the value? + @elseif $expandValue { + @each $vendor in $vendor-prefixes { + #{$property}: #{str-replace-all($value, '-prefix-', $vendor)}; + } + } + + // Neither? Treat them as a normal declaration. + @else { + #{$property}: #{$value}; + } + + } \ No newline at end of file diff --git a/dockers/web/html/assets/sass/main.scss b/dockers/web/html/assets/sass/main.scss new file mode 100644 index 0000000..2734364 --- /dev/null +++ b/dockers/web/html/assets/sass/main.scss @@ -0,0 +1,54 @@ +@import 'libs/vars'; +@import 'libs/functions'; +@import 'libs/mixins'; +@import 'libs/vendor'; +@import 'libs/breakpoints'; +@import 'libs/html-grid'; +@import 'fontawesome-all.min.css'; +@import url('https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,700,900'); + +/* + Phantom by HTML5 UP + html5up.net | @ajlkn + Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) +*/ + +// Breakpoints. + + @include breakpoints(( + xlarge: ( 1281px, 1680px ), + large: ( 981px, 1280px ), + medium: ( 737px, 980px ), + small: ( 481px, 736px ), + xsmall: ( 361px, 480px ), + xxsmall: ( null, 360px ) + )); + +// Base. + + @import 'base/reset'; + @import 'base/page'; + @import 'base/typography'; + +// Component. + + @import 'components/row'; + @import 'components/section'; + @import 'components/icon'; + @import 'components/list'; + @import 'components/actions'; + @import 'components/icons'; + @import 'components/form'; + @import 'components/box'; + @import 'components/image'; + @import 'components/table'; + @import 'components/button'; + @import 'components/tiles'; + +// Layout. + + @import 'layout/header'; + @import 'layout/menu'; + @import 'layout/main'; + @import 'layout/footer'; + @import 'layout/wrapper'; \ No newline at end of file diff --git a/dockers/web/html/assets/sass/noscript.scss b/dockers/web/html/assets/sass/noscript.scss new file mode 100644 index 0000000..bf3eef9 --- /dev/null +++ b/dockers/web/html/assets/sass/noscript.scss @@ -0,0 +1,23 @@ +@import 'libs/vars'; +@import 'libs/functions'; +@import 'libs/mixins'; +@import 'libs/vendor'; +@import 'libs/breakpoints'; +@import 'libs/html-grid'; + +/* + Phantom by HTML5 UP + html5up.net | @ajlkn + Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) +*/ + +/* Tiles */ + + .tiles { + body.is-preload & { + article { + @include vendor('transform', 'none'); + opacity: 1; + } + } + } \ No newline at end of file diff --git a/dockers/web/html/assets/webfonts/fa-brands-400.eot b/dockers/web/html/assets/webfonts/fa-brands-400.eot new file mode 100644 index 0000000..e79f40f Binary files /dev/null and b/dockers/web/html/assets/webfonts/fa-brands-400.eot differ diff --git a/dockers/web/html/assets/webfonts/fa-brands-400.svg b/dockers/web/html/assets/webfonts/fa-brands-400.svg new file mode 100644 index 0000000..ba0d850 --- /dev/null +++ b/dockers/web/html/assets/webfonts/fa-brands-400.svg @@ -0,0 +1,3442 @@ + + + + + +Created by FontForge 20190112 at Tue Jun 4 15:16:44 2019 + By Robert Madole +Copyright (c) Font Awesome + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dockers/web/html/assets/webfonts/fa-brands-400.ttf b/dockers/web/html/assets/webfonts/fa-brands-400.ttf new file mode 100644 index 0000000..217ffe9 Binary files /dev/null and b/dockers/web/html/assets/webfonts/fa-brands-400.ttf differ diff --git a/dockers/web/html/assets/webfonts/fa-brands-400.woff b/dockers/web/html/assets/webfonts/fa-brands-400.woff new file mode 100644 index 0000000..a2d8025 Binary files /dev/null and b/dockers/web/html/assets/webfonts/fa-brands-400.woff differ diff --git a/dockers/web/html/assets/webfonts/fa-brands-400.woff2 b/dockers/web/html/assets/webfonts/fa-brands-400.woff2 new file mode 100644 index 0000000..e27b0bf Binary files /dev/null and b/dockers/web/html/assets/webfonts/fa-brands-400.woff2 differ diff --git a/dockers/web/html/assets/webfonts/fa-regular-400.eot b/dockers/web/html/assets/webfonts/fa-regular-400.eot new file mode 100644 index 0000000..d62be2f Binary files /dev/null and b/dockers/web/html/assets/webfonts/fa-regular-400.eot differ diff --git a/dockers/web/html/assets/webfonts/fa-regular-400.svg b/dockers/web/html/assets/webfonts/fa-regular-400.svg new file mode 100644 index 0000000..751083e --- /dev/null +++ b/dockers/web/html/assets/webfonts/fa-regular-400.svg @@ -0,0 +1,803 @@ + + + + + +Created by FontForge 20190112 at Tue Jun 4 15:16:44 2019 + By Robert Madole +Copyright (c) Font Awesome + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dockers/web/html/assets/webfonts/fa-regular-400.ttf b/dockers/web/html/assets/webfonts/fa-regular-400.ttf new file mode 100644 index 0000000..eb3cb5e Binary files /dev/null and b/dockers/web/html/assets/webfonts/fa-regular-400.ttf differ diff --git a/dockers/web/html/assets/webfonts/fa-regular-400.woff b/dockers/web/html/assets/webfonts/fa-regular-400.woff new file mode 100644 index 0000000..43b1a9a Binary files /dev/null and b/dockers/web/html/assets/webfonts/fa-regular-400.woff differ diff --git a/dockers/web/html/assets/webfonts/fa-regular-400.woff2 b/dockers/web/html/assets/webfonts/fa-regular-400.woff2 new file mode 100644 index 0000000..b9344a7 Binary files /dev/null and b/dockers/web/html/assets/webfonts/fa-regular-400.woff2 differ diff --git a/dockers/web/html/assets/webfonts/fa-solid-900.eot b/dockers/web/html/assets/webfonts/fa-solid-900.eot new file mode 100644 index 0000000..c77baa8 Binary files /dev/null and b/dockers/web/html/assets/webfonts/fa-solid-900.eot differ diff --git a/dockers/web/html/assets/webfonts/fa-solid-900.svg b/dockers/web/html/assets/webfonts/fa-solid-900.svg new file mode 100644 index 0000000..627128b --- /dev/null +++ b/dockers/web/html/assets/webfonts/fa-solid-900.svg @@ -0,0 +1,4649 @@ + + + + + +Created by FontForge 20190112 at Tue Jun 4 15:16:44 2019 + By Robert Madole +Copyright (c) Font Awesome + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dockers/web/html/assets/webfonts/fa-solid-900.ttf b/dockers/web/html/assets/webfonts/fa-solid-900.ttf new file mode 100644 index 0000000..c6c3dd4 Binary files /dev/null and b/dockers/web/html/assets/webfonts/fa-solid-900.ttf differ diff --git a/dockers/web/html/assets/webfonts/fa-solid-900.woff b/dockers/web/html/assets/webfonts/fa-solid-900.woff new file mode 100644 index 0000000..77c1786 Binary files /dev/null and b/dockers/web/html/assets/webfonts/fa-solid-900.woff differ diff --git a/dockers/web/html/assets/webfonts/fa-solid-900.woff2 b/dockers/web/html/assets/webfonts/fa-solid-900.woff2 new file mode 100644 index 0000000..e30fb67 Binary files /dev/null and b/dockers/web/html/assets/webfonts/fa-solid-900.woff2 differ diff --git a/dockers/web/html/cgi-bin/sendmail b/dockers/web/html/cgi-bin/sendmail new file mode 100755 index 0000000..b25b0b1 --- /dev/null +++ b/dockers/web/html/cgi-bin/sendmail @@ -0,0 +1,3 @@ +#!/bin/bash + +echo toto | msmtp fabrice.regnier@kaz2.ovh diff --git a/dockers/web/html/cgi-bin/test.html b/dockers/web/html/cgi-bin/test.html new file mode 100755 index 0000000..6f61a79 --- /dev/null +++ b/dockers/web/html/cgi-bin/test.html @@ -0,0 +1 @@ +titi diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/21880c0ffe15753a53f8402e9f64e1077e071510/style.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/21880c0ffe15753a53f8402e9f64e1077e071510/style.scssc new file mode 100755 index 0000000..00690b7 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/21880c0ffe15753a53f8402e9f64e1077e071510/style.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/2866be86c578bb583fbf023501bb9fd24cc3bf22/extend.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/2866be86c578bb583fbf023501bb9fd24cc3bf22/extend.scssc new file mode 100755 index 0000000..4320a06 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/2866be86c578bb583fbf023501bb9fd24cc3bf22/extend.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/2866be86c578bb583fbf023501bb9fd24cc3bf22/fonts.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/2866be86c578bb583fbf023501bb9fd24cc3bf22/fonts.scssc new file mode 100755 index 0000000..59832e9 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/2866be86c578bb583fbf023501bb9fd24cc3bf22/fonts.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/2866be86c578bb583fbf023501bb9fd24cc3bf22/global.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/2866be86c578bb583fbf023501bb9fd24cc3bf22/global.scssc new file mode 100755 index 0000000..f5a8481 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/2866be86c578bb583fbf023501bb9fd24cc3bf22/global.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/2866be86c578bb583fbf023501bb9fd24cc3bf22/minxi.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/2866be86c578bb583fbf023501bb9fd24cc3bf22/minxi.scssc new file mode 100755 index 0000000..af9ba2b Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/2866be86c578bb583fbf023501bb9fd24cc3bf22/minxi.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/2866be86c578bb583fbf023501bb9fd24cc3bf22/variables.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/2866be86c578bb583fbf023501bb9fd24cc3bf22/variables.scssc new file mode 100755 index 0000000..a574a90 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/2866be86c578bb583fbf023501bb9fd24cc3bf22/variables.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/3948936f84e1a30385e73a9be140b58b48c82e15/style.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/3948936f84e1a30385e73a9be140b58b48c82e15/style.scssc new file mode 100755 index 0000000..055e518 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/3948936f84e1a30385e73a9be140b58b48c82e15/style.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/41c45612afe01e9f2496d846f6e06c0f4eae7672/extend.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/41c45612afe01e9f2496d846f6e06c0f4eae7672/extend.scssc new file mode 100755 index 0000000..88deb68 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/41c45612afe01e9f2496d846f6e06c0f4eae7672/extend.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/41c45612afe01e9f2496d846f6e06c0f4eae7672/fonts.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/41c45612afe01e9f2496d846f6e06c0f4eae7672/fonts.scssc new file mode 100755 index 0000000..8631175 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/41c45612afe01e9f2496d846f6e06c0f4eae7672/fonts.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/41c45612afe01e9f2496d846f6e06c0f4eae7672/global.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/41c45612afe01e9f2496d846f6e06c0f4eae7672/global.scssc new file mode 100755 index 0000000..e1a34c5 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/41c45612afe01e9f2496d846f6e06c0f4eae7672/global.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/41c45612afe01e9f2496d846f6e06c0f4eae7672/minxi.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/41c45612afe01e9f2496d846f6e06c0f4eae7672/minxi.scssc new file mode 100755 index 0000000..7254d83 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/41c45612afe01e9f2496d846f6e06c0f4eae7672/minxi.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/41c45612afe01e9f2496d846f6e06c0f4eae7672/variables.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/41c45612afe01e9f2496d846f6e06c0f4eae7672/variables.scssc new file mode 100755 index 0000000..28d8539 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/41c45612afe01e9f2496d846f6e06c0f4eae7672/variables.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/5c394fb1b61974d1fcc7e7bce6daf8df739d1747/style.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/5c394fb1b61974d1fcc7e7bce6daf8df739d1747/style.scssc new file mode 100755 index 0000000..ac958a0 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/5c394fb1b61974d1fcc7e7bce6daf8df739d1747/style.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/643489b018602f15119d8f44aeb7fc79468873a9/main.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/643489b018602f15119d8f44aeb7fc79468873a9/main.scssc new file mode 100755 index 0000000..d13352b Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/643489b018602f15119d8f44aeb7fc79468873a9/main.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/643489b018602f15119d8f44aeb7fc79468873a9/responsive.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/643489b018602f15119d8f44aeb7fc79468873a9/responsive.scssc new file mode 100755 index 0000000..637a3c4 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/643489b018602f15119d8f44aeb7fc79468873a9/responsive.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/68358af483fbd41941949162bcf87696e73f5956/extend.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/68358af483fbd41941949162bcf87696e73f5956/extend.scssc new file mode 100755 index 0000000..7d59e1d Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/68358af483fbd41941949162bcf87696e73f5956/extend.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/68358af483fbd41941949162bcf87696e73f5956/fonts.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/68358af483fbd41941949162bcf87696e73f5956/fonts.scssc new file mode 100755 index 0000000..e8af27e Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/68358af483fbd41941949162bcf87696e73f5956/fonts.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/68358af483fbd41941949162bcf87696e73f5956/global.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/68358af483fbd41941949162bcf87696e73f5956/global.scssc new file mode 100755 index 0000000..bd08538 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/68358af483fbd41941949162bcf87696e73f5956/global.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/68358af483fbd41941949162bcf87696e73f5956/minxi.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/68358af483fbd41941949162bcf87696e73f5956/minxi.scssc new file mode 100755 index 0000000..dd37bed Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/68358af483fbd41941949162bcf87696e73f5956/minxi.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/68358af483fbd41941949162bcf87696e73f5956/variables.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/68358af483fbd41941949162bcf87696e73f5956/variables.scssc new file mode 100755 index 0000000..7db6325 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/68358af483fbd41941949162bcf87696e73f5956/variables.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/7643c8aa2e335ff5e104bc47d8dd7f1a0693bb39/extend.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/7643c8aa2e335ff5e104bc47d8dd7f1a0693bb39/extend.scssc new file mode 100755 index 0000000..8826cb9 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/7643c8aa2e335ff5e104bc47d8dd7f1a0693bb39/extend.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/7643c8aa2e335ff5e104bc47d8dd7f1a0693bb39/fonts.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/7643c8aa2e335ff5e104bc47d8dd7f1a0693bb39/fonts.scssc new file mode 100755 index 0000000..9a9d83a Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/7643c8aa2e335ff5e104bc47d8dd7f1a0693bb39/fonts.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/7643c8aa2e335ff5e104bc47d8dd7f1a0693bb39/global.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/7643c8aa2e335ff5e104bc47d8dd7f1a0693bb39/global.scssc new file mode 100755 index 0000000..b10c38e Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/7643c8aa2e335ff5e104bc47d8dd7f1a0693bb39/global.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/7643c8aa2e335ff5e104bc47d8dd7f1a0693bb39/minxi.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/7643c8aa2e335ff5e104bc47d8dd7f1a0693bb39/minxi.scssc new file mode 100755 index 0000000..35a677a Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/7643c8aa2e335ff5e104bc47d8dd7f1a0693bb39/minxi.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/7643c8aa2e335ff5e104bc47d8dd7f1a0693bb39/variables.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/7643c8aa2e335ff5e104bc47d8dd7f1a0693bb39/variables.scssc new file mode 100755 index 0000000..c51fdef Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/7643c8aa2e335ff5e104bc47d8dd7f1a0693bb39/variables.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/82ffc783cd596580cf9adcef3bc24b981935f37f/style.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/82ffc783cd596580cf9adcef3bc24b981935f37f/style.scssc new file mode 100755 index 0000000..01c0dc6 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/82ffc783cd596580cf9adcef3bc24b981935f37f/style.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/9dc767a56f3dc2f8a933300255cd2265f13191d4/main.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/9dc767a56f3dc2f8a933300255cd2265f13191d4/main.scssc new file mode 100755 index 0000000..2132798 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/9dc767a56f3dc2f8a933300255cd2265f13191d4/main.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/9dc767a56f3dc2f8a933300255cd2265f13191d4/responsive.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/9dc767a56f3dc2f8a933300255cd2265f13191d4/responsive.scssc new file mode 100755 index 0000000..0419f12 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/9dc767a56f3dc2f8a933300255cd2265f13191d4/responsive.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/c790b871e732c41046d2d09671dc101cd76728e2/main.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/c790b871e732c41046d2d09671dc101cd76728e2/main.scssc new file mode 100755 index 0000000..b84d310 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/c790b871e732c41046d2d09671dc101cd76728e2/main.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/c790b871e732c41046d2d09671dc101cd76728e2/responsive.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/c790b871e732c41046d2d09671dc101cd76728e2/responsive.scssc new file mode 100755 index 0000000..2aaad48 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/c790b871e732c41046d2d09671dc101cd76728e2/responsive.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/cc2f6914db28d61b7d0c6dff455cf4df6db5b30f/main.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/cc2f6914db28d61b7d0c6dff455cf4df6db5b30f/main.scssc new file mode 100755 index 0000000..092e68e Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/cc2f6914db28d61b7d0c6dff455cf4df6db5b30f/main.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/cc2f6914db28d61b7d0c6dff455cf4df6db5b30f/responsive.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/cc2f6914db28d61b7d0c6dff455cf4df6db5b30f/responsive.scssc new file mode 100755 index 0000000..1e0f271 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/cc2f6914db28d61b7d0c6dff455cf4df6db5b30f/responsive.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/d686493398f83d759b5ae7b15feb395ac4a1f85e/extend.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/d686493398f83d759b5ae7b15feb395ac4a1f85e/extend.scssc new file mode 100755 index 0000000..f48140c Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/d686493398f83d759b5ae7b15feb395ac4a1f85e/extend.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/d686493398f83d759b5ae7b15feb395ac4a1f85e/fonts.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/d686493398f83d759b5ae7b15feb395ac4a1f85e/fonts.scssc new file mode 100755 index 0000000..5463253 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/d686493398f83d759b5ae7b15feb395ac4a1f85e/fonts.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/d686493398f83d759b5ae7b15feb395ac4a1f85e/global.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/d686493398f83d759b5ae7b15feb395ac4a1f85e/global.scssc new file mode 100755 index 0000000..708a60d Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/d686493398f83d759b5ae7b15feb395ac4a1f85e/global.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/d686493398f83d759b5ae7b15feb395ac4a1f85e/minxi.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/d686493398f83d759b5ae7b15feb395ac4a1f85e/minxi.scssc new file mode 100755 index 0000000..09d727b Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/d686493398f83d759b5ae7b15feb395ac4a1f85e/minxi.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/d686493398f83d759b5ae7b15feb395ac4a1f85e/variables.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/d686493398f83d759b5ae7b15feb395ac4a1f85e/variables.scssc new file mode 100755 index 0000000..1d1a770 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/d686493398f83d759b5ae7b15feb395ac4a1f85e/variables.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/dd2deb9c5f01209b6937898f47e048cde30208ce/style.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/dd2deb9c5f01209b6937898f47e048cde30208ce/style.scssc new file mode 100755 index 0000000..33d4358 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/dd2deb9c5f01209b6937898f47e048cde30208ce/style.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/f8f0098c6d87e34d84a0704fb71588a6cc87b962/main.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/f8f0098c6d87e34d84a0704fb71588a6cc87b962/main.scssc new file mode 100755 index 0000000..9a20ff3 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/f8f0098c6d87e34d84a0704fb71588a6cc87b962/main.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/.sass-cache/f8f0098c6d87e34d84a0704fb71588a6cc87b962/responsive.scssc b/dockers/web/html/colorlib-regform-16/.sass-cache/f8f0098c6d87e34d84a0704fb71588a6cc87b962/responsive.scssc new file mode 100755 index 0000000..cfe0f82 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/.sass-cache/f8f0098c6d87e34d84a0704fb71588a6cc87b962/responsive.scssc differ diff --git a/dockers/web/html/colorlib-regform-16/css/style.css b/dockers/web/html/colorlib-regform-16/css/style.css new file mode 100755 index 0000000..ece08a2 --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/css/style.css @@ -0,0 +1,612 @@ +/* @extend display-flex; */ +display-flex, .signup-content, .form-row, .label-flex, .form-radio-group { + display: flex; + display: -webkit-flex; } + +/* @extend list-type-ulli; */ +list-type-ulli, ul { + list-style-type: none; + margin: 0; + padding: 0; } + +/* poppins-300 - latin */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 300; + src: url("../fonts/poppins/poppins-v5-latin-300.eot"); + /* IE9 Compat Modes */ + src: local("Poppins Light"), local("Poppins-Light"), url("../fonts/poppins/poppins-v5-latin-300.eot?#iefix") format("embedded-opentype"), url("../fonts/poppins/poppins-v5-latin-300.woff2") format("woff2"), url("../fonts/poppins/poppins-v5-latin-300.woff") format("woff"), url("../fonts/poppins/poppins-v5-latin-300.ttf") format("truetype"), url("../fonts/poppins/poppins-v5-latin-300.svg#Poppins") format("svg"); + /* Legacy iOS */ } +/* poppins-300italic - latin */ +@font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 300; + src: url("../fonts/poppins/poppins-v5-latin-300italic.eot"); + /* IE9 Compat Modes */ + src: local("Poppins Light Italic"), local("Poppins-LightItalic"), url("../fonts/poppins/poppins-v5-latin-300italic.eot?#iefix") format("embedded-opentype"), url("../fonts/poppins/poppins-v5-latin-300italic.woff2") format("woff2"), url("../fonts/poppins/poppins-v5-latin-300italic.woff") format("woff"), url("../fonts/poppins/poppins-v5-latin-300italic.ttf") format("truetype"), url("../fonts/poppins/poppins-v5-latin-300italic.svg#Poppins") format("svg"); + /* Legacy iOS */ } +/* poppins-regular - latin */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 400; + src: url("../fonts/poppins/poppins-v5-latin-regular.eot"); + /* IE9 Compat Modes */ + src: local("Poppins Regular"), local("Poppins-Regular"), url("../fonts/poppins/poppins-v5-latin-regular.eot?#iefix") format("embedded-opentype"), url("../fonts/poppins/poppins-v5-latin-regular.woff2") format("woff2"), url("../fonts/poppins/poppins-v5-latin-regular.woff") format("woff"), url("../fonts/poppins/poppins-v5-latin-regular.ttf") format("truetype"), url("../fonts/poppins/poppins-v5-latin-regular.svg#Poppins") format("svg"); + /* Legacy iOS */ } +/* poppins-italic - latin */ +@font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 400; + src: url("../fonts/poppins/poppins-v5-latin-italic.eot"); + /* IE9 Compat Modes */ + src: local("Poppins Italic"), local("Poppins-Italic"), url("../fonts/poppins/poppins-v5-latin-italic.eot?#iefix") format("embedded-opentype"), url("../fonts/poppins/poppins-v5-latin-italic.woff2") format("woff2"), url("../fonts/poppins/poppins-v5-latin-italic.woff") format("woff"), url("../fonts/poppins/poppins-v5-latin-italic.ttf") format("truetype"), url("../fonts/poppins/poppins-v5-latin-italic.svg#Poppins") format("svg"); + /* Legacy iOS */ } +/* poppins-500 - latin */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 500; + src: url("../fonts/poppins/poppins-v5-latin-500.eot"); + /* IE9 Compat Modes */ + src: local("Poppins Medium"), local("Poppins-Medium"), url("../fonts/poppins/poppins-v5-latin-500.eot?#iefix") format("embedded-opentype"), url("../fonts/poppins/poppins-v5-latin-500.woff2") format("woff2"), url("../fonts/poppins/poppins-v5-latin-500.woff") format("woff"), url("../fonts/poppins/poppins-v5-latin-500.ttf") format("truetype"), url("../fonts/poppins/poppins-v5-latin-500.svg#Poppins") format("svg"); + /* Legacy iOS */ } +/* poppins-500italic - latin */ +@font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 500; + src: url("../fonts/poppins/poppins-v5-latin-500italic.eot"); + /* IE9 Compat Modes */ + src: local("Poppins Medium Italic"), local("Poppins-MediumItalic"), url("../fonts/poppins/poppins-v5-latin-500italic.eot?#iefix") format("embedded-opentype"), url("../fonts/poppins/poppins-v5-latin-500italic.woff2") format("woff2"), url("../fonts/poppins/poppins-v5-latin-500italic.woff") format("woff"), url("../fonts/poppins/poppins-v5-latin-500italic.ttf") format("truetype"), url("../fonts/poppins/poppins-v5-latin-500italic.svg#Poppins") format("svg"); + /* Legacy iOS */ } +/* poppins-600 - latin */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 600; + src: url("../fonts/poppins/poppins-v5-latin-600.eot"); + /* IE9 Compat Modes */ + src: local("Poppins SemiBold"), local("Poppins-SemiBold"), url("../fonts/poppins/poppins-v5-latin-600.eot?#iefix") format("embedded-opentype"), url("../fonts/poppins/poppins-v5-latin-600.woff2") format("woff2"), url("../fonts/poppins/poppins-v5-latin-600.woff") format("woff"), url("../fonts/poppins/poppins-v5-latin-600.ttf") format("truetype"), url("../fonts/poppins/poppins-v5-latin-600.svg#Poppins") format("svg"); + /* Legacy iOS */ } +/* poppins-700 - latin */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 700; + src: url("../fonts/poppins/poppins-v5-latin-700.eot"); + /* IE9 Compat Modes */ + src: local("Poppins Bold"), local("Poppins-Bold"), url("../fonts/poppins/poppins-v5-latin-700.eot?#iefix") format("embedded-opentype"), url("../fonts/poppins/poppins-v5-latin-700.woff2") format("woff2"), url("../fonts/poppins/poppins-v5-latin-700.woff") format("woff"), url("../fonts/poppins/poppins-v5-latin-700.ttf") format("truetype"), url("../fonts/poppins/poppins-v5-latin-700.svg#Poppins") format("svg"); + /* Legacy iOS */ } +/* poppins-700italic - latin */ +@font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 700; + src: url("../fonts/poppins/poppins-v5-latin-700italic.eot"); + /* IE9 Compat Modes */ + src: local("Poppins Bold Italic"), local("Poppins-BoldItalic"), url("../fonts/poppins/poppins-v5-latin-700italic.eot?#iefix") format("embedded-opentype"), url("../fonts/poppins/poppins-v5-latin-700italic.woff2") format("woff2"), url("../fonts/poppins/poppins-v5-latin-700italic.woff") format("woff"), url("../fonts/poppins/poppins-v5-latin-700italic.ttf") format("truetype"), url("../fonts/poppins/poppins-v5-latin-700italic.svg#Poppins") format("svg"); + /* Legacy iOS */ } +/* poppins-800 - latin */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 800; + src: url("../fonts/poppins/poppins-v5-latin-800.eot"); + /* IE9 Compat Modes */ + src: local("Poppins ExtraBold"), local("Poppins-ExtraBold"), url("../fonts/poppins/poppins-v5-latin-800.eot?#iefix") format("embedded-opentype"), url("../fonts/poppins/poppins-v5-latin-800.woff2") format("woff2"), url("../fonts/poppins/poppins-v5-latin-800.woff") format("woff"), url("../fonts/poppins/poppins-v5-latin-800.ttf") format("truetype"), url("../fonts/poppins/poppins-v5-latin-800.svg#Poppins") format("svg"); + /* Legacy iOS */ } +/* poppins-800italic - latin */ +@font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 800; + src: url("../fonts/poppins/poppins-v5-latin-800italic.eot"); + /* IE9 Compat Modes */ + src: local("Poppins ExtraBold Italic"), local("Poppins-ExtraBoldItalic"), url("../fonts/poppins/poppins-v5-latin-800italic.eot?#iefix") format("embedded-opentype"), url("../fonts/poppins/poppins-v5-latin-800italic.woff2") format("woff2"), url("../fonts/poppins/poppins-v5-latin-800italic.woff") format("woff"), url("../fonts/poppins/poppins-v5-latin-800italic.ttf") format("truetype"), url("../fonts/poppins/poppins-v5-latin-800italic.svg#Poppins") format("svg"); + /* Legacy iOS */ } +/* poppins-900 - latin */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 900; + src: url("../fonts/poppins/poppins-v5-latin-900.eot"); + /* IE9 Compat Modes */ + src: local("Poppins Black"), local("Poppins-Black"), url("../fonts/poppins/poppins-v5-latin-900.eot?#iefix") format("embedded-opentype"), url("../fonts/poppins/poppins-v5-latin-900.woff2") format("woff2"), url("../fonts/poppins/poppins-v5-latin-900.woff") format("woff"), url("../fonts/poppins/poppins-v5-latin-900.ttf") format("truetype"), url("../fonts/poppins/poppins-v5-latin-900.svg#Poppins") format("svg"); + /* Legacy iOS */ } +a:focus, a:active { + text-decoration: none; + outline: none; + transition: all 300ms ease 0s; + -moz-transition: all 300ms ease 0s; + -webkit-transition: all 300ms ease 0s; + -o-transition: all 300ms ease 0s; + -ms-transition: all 300ms ease 0s; } + +input, select, textarea { + outline: none; + appearance: unset !important; + -moz-appearance: unset !important; + -webkit-appearance: unset !important; + -o-appearance: unset !important; + -ms-appearance: unset !important; } + +input::-webkit-outer-spin-button, input::-webkit-inner-spin-button { + appearance: none !important; + -moz-appearance: none !important; + -webkit-appearance: none !important; + -o-appearance: none !important; + -ms-appearance: none !important; + margin: 0; } + +input:focus, select:focus, textarea:focus { + outline: none; + box-shadow: none !important; + -moz-box-shadow: none !important; + -webkit-box-shadow: none !important; + -o-box-shadow: none !important; + -ms-box-shadow: none !important; } + +input[type=checkbox] { + appearance: checkbox !important; + -moz-appearance: checkbox !important; + -webkit-appearance: checkbox !important; + -o-appearance: checkbox !important; + -ms-appearance: checkbox !important; } + +input[type=radio] { + appearance: radio !important; + -moz-appearance: radio !important; + -webkit-appearance: radio !important; + -o-appearance: radio !important; + -ms-appearance: radio !important; } + +input:-webkit-autofill { + box-shadow: 0 0 0 30px transparent inset; + -moz-box-shadow: 0 0 0 30px transparent inset; + -webkit-box-shadow: 0 0 0 30px transparent inset; + -o-box-shadow: 0 0 0 30px transparent inset; + -ms-box-shadow: 0 0 0 30px transparent inset; + background-color: transparent !important; } + +img { + max-width: 100%; + height: auto; } + +figure { + margin: 0; } + +p { + margin: 0px; + font-weight: 600; + color: #fff; } + +h2 { + line-height: 1.2; + margin: 0; + padding: 0; + font-weight: 900; + color: #fff; + font-family: 'Poppins'; + font-size: 26px; + text-transform: uppercase; + margin-bottom: 10px; } + +.clear { + clear: both; } + +body { + font-size: 14px; + line-height: 1.8; + color: #222; + font-weight: 500; + font-family: 'Poppins'; + margin: 0px; + background: #282828; + padding: 50px 0; } + +.main { + position: relative; } + +.container { + width: 1400px; + margin: 0 auto; + background: #fff; } + +.signup-img { + position: relative; + width: 385px; + margin-bottom: -8px; } + +.signup-form { + width: 1015px; + margin-top: -2px; } + +.signup-img-content { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + -moz-transform: translate(-50%, -50%); + -webkit-transform: translate(-50%, -50%); + -o-transform: translate(-50%, -50%); + -ms-transform: translate(-50%, -50%); + text-align: center; + width: 100%; } + +.register-form { + padding: 93px 115px 90px 80px; + margin-bottom: -8px; } + +.form-row { + margin: 0 -30px; } + .form-row .form-group { + width: 50%; + padding: 0 30px; } + +.form-input, .form-select, .form-radio { + margin-bottom: 23px; } + +label, input { + display: block; + width: 100%; } + +label { + font-weight: bold; + text-transform: uppercase; + margin-bottom: 7px; } + +label.required { + position: relative; } + label.required:after { + content: '*'; + margin-left: 2px; + color: #b90000; } + +input { + box-sizing: border-box; + border: 1px solid #ebebeb; + padding: 14px 20px; + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + -o-border-radius: 5px; + -ms-border-radius: 5px; + font-size: 14px; + font-family: 'Poppins'; } + input:focus { + border: 1px solid #329e5e; } + +.label-flex { + justify-content: space-between; + -moz-justify-content: space-between; + -webkit-justify-content: space-between; + -o-justify-content: space-between; + -ms-justify-content: space-between; } + .label-flex label { + width: auto; } + +.form-link { + font-size: 12px; + color: #222; + text-decoration: none; + position: relative; } + .form-link:after { + position: absolute; + content: ""; + width: 100%; + height: 2px; + background: #d7d7d7; + left: 0; + bottom: 12px; } + +.form-radio { + margin-bottom: 18px; } + .form-radio input { + width: auto; + display: inline-block; } + +.radio-label { + padding-right: 72px; } + +.form-radio-group { + padding-bottom: 10px; + padding-top: 12px; } + +.form-radio-item { + position: relative; + margin-right: 30px; } + .form-radio-item label { + font-weight: 500; + padding-left: 30px; + position: relative; + z-index: 9; + display: block; + cursor: pointer; + text-transform: none; } + +.check { + display: inline-block; + position: absolute; + border: 1px solid #ebebeb; + border-radius: 50%; + -moz-border-radius: 50%; + -webkit-border-radius: 50%; + -o-border-radius: 50%; + -ms-border-radius: 50%; + height: 18px; + width: 18px; + top: 2px; + left: 0px; + z-index: 5; + transition: border .25s linear; + -webkit-transition: border .25s linear; } + .check:before { + position: absolute; + display: block; + content: ''; + width: 12px; + height: 12px; + border-radius: 50%; + -moz-border-radius: 50%; + -webkit-border-radius: 50%; + -o-border-radius: 50%; + -ms-border-radius: 50%; + top: 3px; + left: 3px; + margin: auto; + transition: background 0.25s linear; + -webkit-transition: background 0.25s linear; } + +input[type=radio] { + position: absolute; + visibility: hidden; } + input[type=radio]:checked ~ .check { + border: 1px solid #329e5e; } + input[type=radio]:checked ~ .check::before { + background: #329e5e; } + +ul { + background: 0 0; + position: relative; + z-index: 9; + border: 1px solid #ebebeb; + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + -o-border-radius: 5px; + -ms-border-radius: 5px; } + +ul li { + padding: 13px 20px; + z-index: 2; + color: #222; } + +ul li:not(.init) { + display: none; + background: #fff; + color: #222; + padding: 5px 10px; } + +ul li:not(.init):hover, ul li.selected:not(.init) { + background: #329e5e; + color: #fff; } + +li.init { + cursor: pointer; + position: relative; } + li.init:after { + position: absolute; + right: 20px; + top: 50%; + transform: translateY(-50%); + -moz-transform: translateY(-50%); + -webkit-transform: translateY(-50%); + -o-transform: translateY(-50%); + -ms-transform: translateY(-50%); + font-size: 22px; + color: #222; + font-family: 'Material-Design-Iconic-Font'; + content: '\f2f2'; } + +#slider-margin { + height: 5px; + border: none; + box-shadow: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + -o-box-shadow: none; + -ms-box-shadow: none; + background: #f8f8f8; + border-radius: 2.5px; + -moz-border-radius: 2.5px; + -webkit-border-radius: 2.5px; + -o-border-radius: 2.5px; + -ms-border-radius: 2.5px; } + #slider-margin .noUi-connect { + background: #329e5e; } + #slider-margin .noUi-handle { + width: 100px; + height: 30px; + top: -12px; + background: #329e5e; + box-shadow: 0px 3px 2.85px 0.15px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0px 3px 2.85px 0.15px rgba(0, 0, 0, 0.1); + -webkit-box-shadow: 0px 3px 2.85px 0.15px rgba(0, 0, 0, 0.1); + -o-box-shadow: 0px 3px 2.85px 0.15px rgba(0, 0, 0, 0.1); + -ms-box-shadow: 0px 3px 2.85px 0.15px rgba(0, 0, 0, 0.1); + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + -o-border-radius: 5px; + -ms-border-radius: 5px; + outline: none; + border: none; + right: -50px; } + #slider-margin .noUi-handle:after, #slider-margin .noUi-handle:before { + background: transparent; + height: 0px; + width: 20px; + top: -2px; + font-size: 18px; + outline: none; } + #slider-margin .noUi-handle:before { + font-family: 'Material-Design-Iconic-Font'; + color: #fff; + content: '\f2fa'; + left: 10px; } + #slider-margin .noUi-handle:after { + font-family: 'Material-Design-Iconic-Font'; + color: #fff; + content: '\f2fb'; + left: auto; + right: -5px; } + #slider-margin .noUi-handle .noUi-tooltip { + bottom: 2px; + border: none; + background: transparent; + font-weight: bold; + color: #fff; + padding: 0px; } + +.donate-us { + margin-bottom: 93px; + margin-top: 30px; } + .donate-us label { + margin-bottom: 32px; } + +.price_slider { + position: relative; } + +.donate-value { + position: absolute; + top: -9px; + left: 50%; + z-index: 99; + transform: translateX(-50%); + -moz-transform: translateX(-50%); + -webkit-transform: translateX(-50%); + -o-transform: translateX(-50%); + -ms-transform: translateX(-50%); } + +.form-submit { + text-align: right; } + +.submit { + width: 150px; + height: 50px; + display: inline-block; + font-family: 'Poppins'; + font-weight: bold; + font-size: 14px; + padding: 10px; + border: none; + cursor: pointer; + text-transform: uppercase; + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + -o-border-radius: 5px; + -ms-border-radius: 5px; } + +#reset { + background: #fff; + color: #999; + border: 2px solid #ebebeb; } + #reset:hover { + border: 2px solid #329e5e; + background: #329e5e; + color: #fff; } + +#submit { + background: #329e5e; + color: #fff; + margin-right: 25px; } + #submit:hover { + background-color: #267747; } + +.form-input { + position: relative; } + +label.error { + display: block; + position: absolute; + top: 0px; + right: 0; } + label.error:after { + font-family: 'Material-Design-Iconic-Font'; + position: absolute; + content: '\f1f8'; + right: 20px; + top: 37px; + font-size: 23px; + color: #c70000; } + +input.error { + border: 1px solid #c70000; } + +.select-list { + position: relative; + display: inline-block; + width: 100%; + margin-bottom: 47px; } + +.list-item { + position: absolute; + width: 100%; + z-index: 99; } + +@media screen and (max-width: 1024px) { + .container { + width: calc( 100% - 30px); + max-width: 100%; + margin: 0 auto; } + + .signup-img { + display: none; } + + .signup-form { + width: 100%; } + + .register-form { + padding: 93px 80px 90px 80px; } } +@media screen and (max-width: 992px) { + .signup-content { + width: 100%; } + + .form-radio-item { + margin-right: 15px; } + + .register-form { + padding: 93px 30px 90px 30px; } } +@media screen and (max-width: 768px) { + .form-row { + flex-direction: column; + -moz-flex-direction: column; + -webkit-flex-direction: column; + -o-flex-direction: column; + -ms-flex-direction: column; + margin: 0px; } + + .form-row .form-group { + width: 100%; + padding: 0 0px; } } +@media screen and (max-width: 480px) { + .submit { + width: 100%; } + + #submit { + margin-bottom: 20px; + margin-right: 0px; } + + .form-radio-group { + flex-direction: column; + -moz-flex-direction: column; + -webkit-flex-direction: column; + -o-flex-direction: column; + -ms-flex-direction: column; } } + +/*# sourceMappingURL=style.css.map */ diff --git a/dockers/web/html/colorlib-regform-16/css/style.css.map b/dockers/web/html/colorlib-regform-16/css/style.css.map new file mode 100755 index 0000000..e353714 --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/css/style.css.map @@ -0,0 +1,7 @@ +{ +"version": 3, +"mappings": "AAAA,2BAA2B;AAC3B,wEAAa;EACT,OAAO,EAAE,IAAI;EACb,OAAO,EAAE,YAAY;;AAEzB,6BAA6B;AAC7B,kBAAe;EACX,eAAe,EAAE,IAAI;EACrB,MAAM,EAAE,CAAC;EACT,OAAO,EAAE,CAAC;;ACTd,yBAAyB;AACzB,UAWG;EAVC,WAAW,EAAE,SAAS;EACtB,UAAU,EAAE,MAAM;EAClB,WAAW,EAAE,GAAG;EAChB,GAAG,EAAE,gDAAgD;EAAE,sBAAsB;EAC7E,GAAG,EAAE,sZAKsE;EAAE,gBAAgB;AAE/F,+BAA+B;AAC/B,UAWC;EAVC,WAAW,EAAE,SAAS;EACtB,UAAU,EAAE,MAAM;EAClB,WAAW,EAAE,GAAG;EAChB,GAAG,EAAE,sDAAsD;EAAE,sBAAsB;EACnF,GAAG,EAAE,icAK4E;EAAE,gBAAgB;AAErG,6BAA6B;AAC7B,UAWG;EAVC,WAAW,EAAE,SAAS;EACtB,UAAU,EAAE,MAAM;EAClB,WAAW,EAAE,GAAG;EAChB,GAAG,EAAE,oDAAoD;EAAE,sBAAsB;EACjF,GAAG,EAAE,8aAK0E;EAAE,gBAAgB;AAEnG,4BAA4B;AAC5B,UAWC;EAVC,WAAW,EAAE,SAAS;EACtB,UAAU,EAAE,MAAM;EAClB,WAAW,EAAE,GAAG;EAChB,GAAG,EAAE,mDAAmD;EAAE,sBAAsB;EAChF,GAAG,EAAE,uaAKyE;EAAE,gBAAgB;AAElG,yBAAyB;AACzB,UAWC;EAVC,WAAW,EAAE,SAAS;EACtB,UAAU,EAAE,MAAM;EAClB,WAAW,EAAE,GAAG;EAChB,GAAG,EAAE,gDAAgD;EAAE,sBAAsB;EAC7E,GAAG,EAAE,wZAKsE;EAAE,gBAAgB;AAE/F,+BAA+B;AAC/B,UAWC;EAVC,WAAW,EAAE,SAAS;EACtB,UAAU,EAAE,MAAM;EAClB,WAAW,EAAE,GAAG;EAChB,GAAG,EAAE,sDAAsD;EAAE,sBAAsB;EACnF,GAAG,EAAE,mcAK4E;EAAE,gBAAgB;AAErG,yBAAyB;AACzB,UAWC;EAVC,WAAW,EAAE,SAAS;EACtB,UAAU,EAAE,MAAM;EAClB,WAAW,EAAE,GAAG;EAChB,GAAG,EAAE,gDAAgD;EAAE,sBAAsB;EAC7E,GAAG,EAAE,4ZAKsE;EAAE,gBAAgB;AAE/F,yBAAyB;AACzB,UAWC;EAVC,WAAW,EAAE,SAAS;EACtB,UAAU,EAAE,MAAM;EAClB,WAAW,EAAE,GAAG;EAChB,GAAG,EAAE,gDAAgD;EAAE,sBAAsB;EAC7E,GAAG,EAAE,oZAKsE;EAAE,gBAAgB;AAE/F,+BAA+B;AAC/B,UAWC;EAVC,WAAW,EAAE,SAAS;EACtB,UAAU,EAAE,MAAM;EAClB,WAAW,EAAE,GAAG;EAChB,GAAG,EAAE,sDAAsD;EAAE,sBAAsB;EACnF,GAAG,EAAE,+bAK4E;EAAE,gBAAgB;AAErG,yBAAyB;AACzB,UAWC;EAVC,WAAW,EAAE,SAAS;EACtB,UAAU,EAAE,MAAM;EAClB,WAAW,EAAE,GAAG;EAChB,GAAG,EAAE,gDAAgD;EAAE,sBAAsB;EAC7E,GAAG,EAAE,8ZAKsE;EAAE,gBAAgB;AAE/F,+BAA+B;AAC/B,UAWC;EAVC,WAAW,EAAE,SAAS;EACtB,UAAU,EAAE,MAAM;EAClB,WAAW,EAAE,GAAG;EAChB,GAAG,EAAE,sDAAsD;EAAE,sBAAsB;EACnF,GAAG,EAAE,ycAK4E;EAAE,gBAAgB;AAErG,yBAAyB;AACzB,UAWC;EAVC,WAAW,EAAE,SAAS;EACtB,UAAU,EAAE,MAAM;EAClB,WAAW,EAAE,GAAG;EAChB,GAAG,EAAE,gDAAgD;EAAE,sBAAsB;EAC7E,GAAG,EAAE,sZAKsE;EAAE,gBAAgB;AC1JlG,iBAAkB;EAAE,eAAe,EAAE,IAAI;EAAE,OAAO,EAAE,IAAI;ECmDxD,UAAU,EDnDoE,iBAAiB;ECoD/F,eAAe,EDpD+D,iBAAiB;ECqD/F,kBAAkB,EDrD4D,iBAAiB;ECsD/F,aAAa,EDtDiE,iBAAiB;ECuD/F,cAAc,EDvDgE,iBAAiB;;AAChG,uBAAwB;EAAE,OAAO,EAAE,IAAI;ECuEtC,UAAU,EAAE,gBAAiB;EAC7B,eAAe,EAAE,gBAAiB;EAClC,kBAAkB,EAAE,gBAAiB;EACrC,aAAa,EAAE,gBAAiB;EAChC,cAAc,EAAE,gBAAiB;;AD1ElC,kEAAmE;ECsElE,UAAU,EAAE,eAAiB;EAC7B,eAAe,EAAE,eAAiB;EAClC,kBAAkB,EAAE,eAAiB;EACrC,aAAa,EAAE,eAAiB;EAChC,cAAc,EAAE,eAAiB;EDxE9B,MAAM,EAAE,CAAC;;AAEb,yCAA0C;EAAE,OAAO,EAAE,IAAI;ECyExD,UAAU,EAAE,eAAiB;EAC7B,eAAe,EAAE,eAAiB;EAClC,kBAAkB,EAAE,eAAiB;EACrC,aAAa,EAAE,eAAiB;EAChC,cAAc,EAAE,eAAiB;;AD5ElC,oBAAqB;ECiEpB,UAAU,EAAE,mBAAiB;EAC7B,eAAe,EAAE,mBAAiB;EAClC,kBAAkB,EAAE,mBAAiB;EACrC,aAAa,EAAE,mBAAiB;EAChC,cAAc,EAAE,mBAAiB;;ADpElC,iBAAkB;ECgEjB,UAAU,EAAE,gBAAiB;EAC7B,eAAe,EAAE,gBAAiB;EAClC,kBAAkB,EAAE,gBAAiB;EACrC,aAAa,EAAE,gBAAiB;EAChC,cAAc,EAAE,gBAAiB;;ADnElC,sBAAuB;EC4BtB,UAAU,ED3Ba,4BAA4B;EC4BnD,eAAe,ED5BQ,4BAA4B;EC6BnD,kBAAkB,ED7BK,4BAA4B;EC8BnD,aAAa,ED9BU,4BAA4B;EC+BnD,cAAc,ED/BS,4BAA4B;EAChD,gBAAgB,EAAE,sBAAsB;;AAE5C,GAAI;EAAE,SAAS,EAAE,IAAI;EAAE,MAAM,EAAE,IAAI;;AACnC,MAAO;EAAE,MAAM,EAAE,CAAC;;AAClB,CAAE;EACE,MAAM,EAAE,GAAG;EACX,WAAW,EAAE,GAAG;EAChB,KAAK,EAAE,IAAI;;AAEf,EAAG;EACC,WAAW,EAAE,GAAG;EAChB,MAAM,EAAE,CAAC;EACT,OAAO,EAAE,CAAC;EACV,WAAW,EAAE,GAAG;EAChB,KAAK,EAAE,IAAI;EACX,WAAW,EAAE,SAAS;EACtB,SAAS,EAAE,IAAI;EACf,cAAc,EAAE,SAAS;EACzB,aAAa,EAAE,IAAI;;AAEvB,MAAO;EAAE,KAAK,EAAE,IAAI;;AACpB,IAAK;EACJ,SAAS,EAAE,IAAI;EACZ,WAAW,EAAE,GAAG;EAChB,KAAK,EEjCK,IAAI;EFkCd,WAAW,EAAE,GAAG;EAChB,WAAW,EAAE,SAAS;EACtB,MAAM,EAAE,GAAG;EACX,UAAU,EAAE,OAAO;EACnB,OAAO,EAAE,MAAM;;AAGnB,KAAM;EACF,QAAQ,EAAE,QAAQ;;AAEtB,UAAW;EACP,KAAK,EAAE,MAAM;EACb,MAAM,EAAE,MAAM;EACd,UAAU,EAAE,IAAI;;AGjDpB,WAAY;EACR,QAAQ,EAAE,QAAQ;EAClB,KAAK,EAAE,KAAK;EACZ,aAAa,EAAE,IAAI;;AAEvB,YAAa;EACT,KAAK,EAAE,MAAM;EACb,UAAU,EAAE,IAAI;;AAEpB,mBAAoB;EAChB,QAAQ,EAAE,QAAQ;EAClB,GAAG,EAAE,GAAG;EACR,IAAI,EAAE,GAAG;EFkBZ,SAAS,EAAE,qBAAM;EACjB,cAAc,EAAE,qBAAM;EACtB,iBAAiB,EAAE,qBAAM;EACzB,YAAY,EAAE,qBAAM;EACpB,aAAa,EAAE,qBAAM;EEpBlB,UAAU,EAAE,MAAM;EAClB,KAAK,EAAE,IAAI;;AAMf,cAAe;EACX,OAAO,EAAE,oBAAoB;EAC7B,aAAa,EAAE,IAAI;;AAEvB,SAAU;EAEN,MAAM,EAAE,OAAO;EACf,qBAAY;IACR,KAAK,EAAE,GAAG;IACV,OAAO,EAAE,MAAM;;AAGvB,sCAAuC;EACnC,aAAa,EAAE,IAAI;;AAEvB,YAAa;EACT,OAAO,EAAE,KAAK;EACd,KAAK,EAAE,IAAI;;AAEf,KAAM;EACF,WAAW,EAAE,IAAI;EACjB,cAAc,EAAE,SAAS;EACzB,aAAa,EAAE,GAAG;;AAEtB,cAAe;EACX,QAAQ,EAAE,QAAQ;EAClB,oBAAQ;IACJ,OAAO,EAAE,GAAG;IACZ,WAAW,EAAE,GAAG;IAChB,KAAK,EAAE,OAAO;;AAItB,KAAM;EACF,UAAU,EAAE,UAAU;EACtB,MAAM,EAAE,iBAAuB;EAC/B,OAAO,EAAE,SAAS;EFvDlB,aAAa,EEwDU,GAAG;EFvD1B,kBAAkB,EEuDK,GAAG;EFtD1B,qBAAqB,EEsDE,GAAG;EFrD1B,gBAAgB,EEqDO,GAAG;EFpD1B,iBAAiB,EEoDM,GAAG;EAC1B,SAAS,EAAE,IAAI;EACf,WAAW,EAAE,SAAS;EACtB,WAAQ;IACJ,MAAM,EAAE,iBAAsB;;AAItC,WAAY;EF2CX,eAAe,EEzCa,aAAa;EF0CzC,oBAAoB,EE1CQ,aAAa;EF2CzC,uBAAuB,EE3CK,aAAa;EF4CzC,kBAAkB,EE5CU,aAAa;EF6CzC,mBAAmB,EE7CS,aAAa;EAEtC,iBAAM;IACF,KAAK,EAAE,IAAI;;AAGnB,UAAW;EACP,SAAS,EAAE,IAAI;EACf,KAAK,ED1EK,IAAI;EC2Ed,eAAe,EAAE,IAAI;EACrB,QAAQ,EAAE,QAAQ;EAClB,gBAAQ;IACJ,QAAQ,EAAE,QAAQ;IAClB,OAAO,EAAE,EAAE;IACX,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,GAAG;IACX,UAAU,EAAE,OAAO;IACnB,IAAI,EAAE,CAAC;IACP,MAAM,EAAE,IAAI;;AAIpB,WAAY;EAER,aAAa,EAAE,IAAI;EACnB,iBAAM;IACF,KAAK,EAAE,IAAI;IACX,OAAO,EAAE,YAAY;;AAG7B,YAAa;EACT,aAAa,EAAE,IAAI;;AAEvB,iBAAkB;EAEd,cAAc,EAAE,IAAI;EACpB,WAAW,EAAE,IAAI;;AAErB,gBAAiB;EACb,QAAQ,EAAE,QAAQ;EAClB,YAAY,EAAE,IAAI;EAClB,sBAAM;IACF,WAAW,EAAE,GAAG;IAChB,YAAY,EAAE,IAAI;IAClB,QAAQ,EAAE,QAAQ;IAClB,OAAO,EAAE,CAAC;IACV,OAAO,EAAE,KAAK;IACd,MAAM,EAAE,OAAO;IACf,cAAc,EAAE,IAAI;;AAI5B,MAAM;EACF,OAAO,EAAE,YAAY;EACrB,QAAQ,EAAE,QAAQ;EAClB,MAAM,EAAE,iBAAuB;EFzH/B,aAAa,EE0HU,GAAG;EFzH1B,kBAAkB,EEyHK,GAAG;EFxH1B,qBAAqB,EEwHE,GAAG;EFvH1B,gBAAgB,EEuHO,GAAG;EFtH1B,iBAAiB,EEsHM,GAAG;EAC1B,MAAM,EAAE,IAAI;EACZ,KAAK,EAAE,IAAI;EACX,GAAG,EAAE,GAAG;EACR,IAAI,EAAE,GAAG;EACP,OAAO,EAAE,CAAC;EACV,UAAU,EAAE,kBAAkB;EAC9B,kBAAkB,EAAE,kBAAkB;EACxC,aAAS;IACL,QAAQ,EAAE,QAAQ;IAClB,OAAO,EAAE,KAAK;IACd,OAAO,EAAE,EAAE;IACX,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,IAAI;IFvIhB,aAAa,EEwIc,GAAG;IFvI9B,kBAAkB,EEuIS,GAAG;IFtI9B,qBAAqB,EEsIM,GAAG;IFrI9B,gBAAgB,EEqIW,GAAG;IFpI9B,iBAAiB,EEoIU,GAAG;IAC1B,GAAG,EAAE,GAAG;IACR,IAAI,EAAE,GAAG;IACT,MAAM,EAAE,IAAI;IACZ,UAAU,EAAE,uBAAuB;IACnC,kBAAkB,EAAE,uBAAuB;;AAGnD,iBAAkB;EACd,QAAQ,EAAE,QAAQ;EAClB,UAAU,EAAE,MAAM;EAClB,kCAAmB;IACf,MAAM,EAAE,iBAAsB;EAElC,0CAA2B;IACvB,UAAU,EDzJH,OAAO;;AC4JtB,EAAG;EAEC,UAAU,EAAE,GAAG;EACf,QAAQ,EAAE,QAAQ;EAClB,OAAO,EAAE,CAAC;EACV,MAAM,EAAE,iBAAuB;EF/J/B,aAAa,EEgKU,GAAG;EF/J1B,kBAAkB,EE+JK,GAAG;EF9J1B,qBAAqB,EE8JE,GAAG;EF7J1B,gBAAgB,EE6JO,GAAG;EF5J1B,iBAAiB,EE4JM,GAAG;;AAE9B,KAAM;EACF,OAAO,EAAE,SAAS;EAClB,OAAO,EAAE,CAAC;EACV,KAAK,EDrKK,IAAI;;ACuKlB,gBAAiB;EACb,OAAO,EAAE,IAAI;EACb,UAAU,EAAE,IAAI;EAChB,KAAK,ED1KK,IAAI;EC2Kd,OAAO,EAAE,QAAQ;;AAErB,iDAAkD;EAC9C,UAAU,EDhLC,OAAO;ECiLlB,KAAK,EAAE,IAAI;;AAEf,OAAQ;EACJ,MAAM,EAAE,OAAO;EACf,QAAQ,EAAE,QAAQ;EAClB,aAAQ;IACJ,QAAQ,EAAE,QAAQ;IAClB,KAAK,EAAE,IAAI;IACX,GAAG,EAAE,GAAG;IF3Jf,SAAS,EAAE,gBAAM;IACjB,cAAc,EAAE,gBAAM;IACtB,iBAAiB,EAAE,gBAAM;IACzB,YAAY,EAAE,gBAAM;IACpB,aAAa,EAAE,gBAAM;IEyJd,SAAS,EAAE,IAAI;IACf,KAAK,ED1LC,IAAI;IC2LV,WAAW,EAAE,6BAA6B;IAC1C,OAAO,EAAE,OAAO;;AAIxB,cAAe;EACX,MAAM,EAAE,GAAG;EACX,MAAM,EAAE,IAAI;EF/Jf,UAAU,EEgKa,IAAI;EF/J3B,eAAe,EE+JQ,IAAI;EF9J3B,kBAAkB,EE8JK,IAAI;EF7J3B,aAAa,EE6JU,IAAI;EF5J3B,cAAc,EE4JS,IAAI;EACxB,UAAU,EAAE,OAAO;EFpMnB,aAAa,EEqMU,KAAK;EFpM5B,kBAAkB,EEoMK,KAAK;EFnM5B,qBAAqB,EEmME,KAAK;EFlM5B,gBAAgB,EEkMO,KAAK;EFjM5B,iBAAiB,EEiMM,KAAK;EAC5B,4BAAc;IACV,UAAU,EDzMH,OAAO;EC2MlB,2BAAa;IACT,KAAK,EAAE,KAAK;IACZ,MAAM,EAAE,IAAI;IACZ,GAAG,EAAE,KAAK;IACV,UAAU,ED/MH,OAAO;IDqCrB,UAAU,EE2KiB,wCAAwC;IF1KnE,eAAe,EE0KY,wCAAwC;IFzKnE,kBAAkB,EEyKS,wCAAwC;IFxKnE,aAAa,EEwKc,wCAAwC;IFvKnE,cAAc,EEuKa,wCAAwC;IF9MhE,aAAa,EE+Mc,GAAG;IF9M9B,kBAAkB,EE8MS,GAAG;IF7M9B,qBAAqB,EE6MM,GAAG;IF5M9B,gBAAgB,EE4MW,GAAG;IF3M9B,iBAAiB,EE2MU,GAAG;IAC1B,OAAO,EAAE,IAAI;IACb,MAAM,EAAE,IAAI;IAIZ,KAAK,EAAE,KAAK;IACZ,qEAAkB;MACd,UAAU,EAAE,WAAW;MACvB,MAAM,EAAE,GAAG;MACX,KAAK,EAAE,IAAI;MACX,GAAG,EAAE,IAAI;MACT,SAAS,EAAE,IAAI;MACf,OAAO,EAAE,IAAI;IAEjB,kCAAS;MACL,WAAW,EAAE,6BAA6B;MAC1C,KAAK,EAAE,IAAI;MACX,OAAO,EAAE,OAAO;MAChB,IAAI,EAAE,IAAI;IAEd,iCAAQ;MACJ,WAAW,EAAE,6BAA6B;MAC1C,KAAK,EAAE,IAAI;MACX,OAAO,EAAE,OAAO;MAChB,IAAI,EAAE,IAAI;MACV,KAAK,EAAE,IAAI;IAEf,yCAAc;MACV,MAAM,EAAE,GAAG;MACX,MAAM,EAAE,IAAI;MACZ,UAAU,EAAE,WAAW;MACvB,WAAW,EAAE,IAAI;MACjB,KAAK,EAAE,IAAI;MACX,OAAO,EAAE,GAAG;;AAIxB,UAAW;EACP,aAAa,EAAE,IAAI;EACnB,UAAU,EAAE,IAAI;EAChB,gBAAM;IACF,aAAa,EAAE,IAAI;;AAG3B,aAAc;EACV,QAAQ,EAAE,QAAQ;;AAEtB,aAAc;EACV,QAAQ,EAAE,QAAQ;EAClB,GAAG,EAAE,IAAI;EACT,IAAI,EAAE,GAAG;EACT,OAAO,EAAE,EAAE;EFvOd,SAAS,EAAE,gBAAM;EACjB,cAAc,EAAE,gBAAM;EACtB,iBAAiB,EAAE,gBAAM;EACzB,YAAY,EAAE,gBAAM;EACpB,aAAa,EAAE,gBAAM;;AEsOtB,YAAa;EACT,UAAU,EAAE,KAAK;;AAErB,OAAQ;EACJ,KAAK,EAAE,KAAK;EACZ,MAAM,EAAE,IAAI;EACZ,OAAO,EAAE,YAAY;EACrB,WAAW,EAAE,SAAS;EACtB,WAAW,EAAE,IAAI;EACjB,SAAS,EAAE,IAAI;EACf,OAAO,EAAE,IAAI;EACb,MAAM,EAAE,IAAI;EACZ,MAAM,EAAE,OAAO;EACf,cAAc,EAAE,SAAS;EFnRzB,aAAa,EEoRU,GAAG;EFnR1B,kBAAkB,EEmRK,GAAG;EFlR1B,qBAAqB,EEkRE,GAAG;EFjR1B,gBAAgB,EEiRO,GAAG;EFhR1B,iBAAiB,EEgRM,GAAG;;AAE9B,MAAO;EACH,UAAU,EAAE,IAAI;EAChB,KAAK,EAAE,IAAI;EAEX,MAAM,EAAE,iBAAuB;EAC/B,YAAQ;IACJ,MAAM,EAAE,iBAAsB;IAC9B,UAAU,ED/RH,OAAO;ICgSd,KAAK,EAAE,IAAI;;AAGnB,OAAQ;EACJ,UAAU,EDpSC,OAAO;ECqSlB,KAAK,EAAE,IAAI;EACX,YAAY,EAAE,IAAI;EAClB,aAAQ;IACJ,gBAAgB,EAAE,OACtB;;AAEJ,WAAY;EACR,QAAQ,EAAE,QAAQ;;AAEtB,WAAY;EACR,OAAO,EAAE,KAAK;EACd,QAAQ,EAAE,QAAQ;EAClB,GAAG,EAAE,GAAG;EACR,KAAK,EAAE,CAAC;EACR,iBAAQ;IACJ,WAAW,EAAE,6BAA6B;IAC1C,QAAQ,EAAE,QAAQ;IAClB,OAAO,EAAE,OAAO;IAChB,KAAK,EAAE,IAAI;IACX,GAAG,EAAE,IAAI;IACT,SAAS,EAAE,IAAI;IACf,KAAK,EAAE,OAAO;;AAGtB,WAAY;EACR,MAAM,EAAE,iBAAiB;;AAE7B,YAAa;EACT,QAAQ,EAAE,QAAQ;EAClB,OAAO,EAAE,YAAY;EACrB,KAAK,EAAE,IAAI;EACX,aAAa,EAAE,IAAI;;AAEvB,UAAW;EACP,QAAQ,EAAE,QAAQ;EAClB,KAAK,EAAE,IAAI;EACX,OAAO,EAAE,EAAE;;ACzUf,qCAAsC;EAClC,UAAW;IACP,KAAK,EAAE,kBAAkB;IACzB,SAAS,EAAE,IAAI;IACf,MAAM,EAAE,MAAM;;EAElB,WAAY;IACR,OAAO,EAAE,IAAI;;EAEjB,YAAa;IACT,KAAK,EAAE,IAAI;;EAEf,cAAe;IACX,OAAO,EAAE,mBAAmB;AAIpC,oCAAqC;EAEjC,eAAgB;IACZ,KAAK,EAAE,IAAI;;EAEf,gBAAiB;IACb,YAAY,EAAE,IAAI;;EAEtB,cAAe;IACX,OAAO,EAAE,mBAAmB;AAIpC,oCAAsC;EAClC,SAAU;IHgEb,cAAc,EG/DiB,MAAM;IHgErC,mBAAmB,EGhEY,MAAM;IHiErC,sBAAsB,EGjES,MAAM;IHkErC,iBAAiB,EGlEc,MAAM;IHmErC,kBAAkB,EGnEa,MAAM;IAC9B,MAAM,EAAE,GAAG;;EAEf,qBAAsB;IAClB,KAAK,EAAE,IAAI;IACX,OAAO,EAAE,KAAK;AAMtB,oCAAqC;EACjC,OAAQ;IACJ,KAAK,EAAE,IAAI;;EAEf,OAAQ;IACJ,aAAa,EAAE,IAAI;IACnB,YAAY,EAAE,GAAG;;EAErB,iBAAkB;IH4CrB,cAAc,EG3CiB,MAAM;IH4CrC,mBAAmB,EG5CY,MAAM;IH6CrC,sBAAsB,EG7CS,MAAM;IH8CrC,iBAAiB,EG9Cc,MAAM;IH+CrC,kBAAkB,EG/Ca,MAAM", +"sources": ["../scss/common/extend.scss","../scss/common/fonts.scss","../scss/common/global.scss","../scss/common/minxi.scss","../scss/common/variables.scss","../scss/layouts/main.scss","../scss/layouts/responsive.scss"], +"names": [], +"file": "style.css" +} \ No newline at end of file diff --git a/dockers/web/html/colorlib-regform-16/fonts/material-icon/css/material-design-iconic-font.css b/dockers/web/html/colorlib-regform-16/fonts/material-icon/css/material-design-iconic-font.css new file mode 100755 index 0000000..81d090a --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/fonts/material-icon/css/material-design-iconic-font.css @@ -0,0 +1,5166 @@ +/*! + * Material Design Iconic Font by Sergey Kupletsky (@zavoloklom) - http://zavoloklom.github.io/material-design-iconic-font/ + * License - http://zavoloklom.github.io/material-design-iconic-font/license (Font: SIL OFL 1.1, CSS: MIT License) + */ +@font-face { + font-family: 'Material-Design-Iconic-Font'; + src: url('../fonts/Material-Design-Iconic-Font.woff2?v=2.2.0') format('woff2'), url('../fonts/Material-Design-Iconic-Font.woff?v=2.2.0') format('woff'), url('../fonts/Material-Design-Iconic-Font.ttf?v=2.2.0') format('truetype'); + font-weight: normal; + font-style: normal; +} +.zmdi { + display: inline-block; + font: normal normal normal 14px/1 'Material-Design-Iconic-Font'; + font-size: inherit; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.zmdi-hc-lg { + font-size: 1.33333333em; + line-height: 0.75em; + vertical-align: -15%; +} +.zmdi-hc-2x { + font-size: 2em; +} +.zmdi-hc-3x { + font-size: 3em; +} +.zmdi-hc-4x { + font-size: 4em; +} +.zmdi-hc-5x { + font-size: 5em; +} +.zmdi-hc-fw { + width: 1.28571429em; + text-align: center; +} +.zmdi-hc-ul { + padding-left: 0; + margin-left: 2.14285714em; + list-style-type: none; +} +.zmdi-hc-ul > li { + position: relative; +} +.zmdi-hc-li { + position: absolute; + left: -2.14285714em; + width: 2.14285714em; + top: 0.14285714em; + text-align: center; +} +.zmdi-hc-li.zmdi-hc-lg { + left: -1.85714286em; +} +.zmdi-hc-border { + padding: .1em .25em; + border: solid 0.1em #9e9e9e; + border-radius: 2px; +} +.zmdi-hc-border-circle { + padding: .1em .25em; + border: solid 0.1em #9e9e9e; + border-radius: 50%; +} +.zmdi.pull-left { + float: left; + margin-right: .15em; +} +.zmdi.pull-right { + float: right; + margin-left: .15em; +} +.zmdi-hc-spin { + -webkit-animation: zmdi-spin 1.5s infinite linear; + animation: zmdi-spin 1.5s infinite linear; +} +.zmdi-hc-spin-reverse { + -webkit-animation: zmdi-spin-reverse 1.5s infinite linear; + animation: zmdi-spin-reverse 1.5s infinite linear; +} +@-webkit-keyframes zmdi-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +@keyframes zmdi-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +@-webkit-keyframes zmdi-spin-reverse { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(-359deg); + transform: rotate(-359deg); + } +} +@keyframes zmdi-spin-reverse { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(-359deg); + transform: rotate(-359deg); + } +} +.zmdi-hc-rotate-90 { + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); +} +.zmdi-hc-rotate-180 { + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} +.zmdi-hc-rotate-270 { + -webkit-transform: rotate(270deg); + -ms-transform: rotate(270deg); + transform: rotate(270deg); +} +.zmdi-hc-flip-horizontal { + -webkit-transform: scale(-1, 1); + -ms-transform: scale(-1, 1); + transform: scale(-1, 1); +} +.zmdi-hc-flip-vertical { + -webkit-transform: scale(1, -1); + -ms-transform: scale(1, -1); + transform: scale(1, -1); +} +.zmdi-hc-stack { + position: relative; + display: inline-block; + width: 2em; + height: 2em; + line-height: 2em; + vertical-align: middle; +} +.zmdi-hc-stack-1x, +.zmdi-hc-stack-2x { + position: absolute; + left: 0; + width: 100%; + text-align: center; +} +.zmdi-hc-stack-1x { + line-height: inherit; +} +.zmdi-hc-stack-2x { + font-size: 2em; +} +.zmdi-hc-inverse { + color: #ffffff; +} +/* Material Design Iconic Font uses the Unicode Private Use Area (PUA) to ensure screen + readers do not read off random characters that represent icons */ +.zmdi-3d-rotation:before { + content: '\f101'; +} +.zmdi-airplane-off:before { + content: '\f102'; +} +.zmdi-airplane:before { + content: '\f103'; +} +.zmdi-album:before { + content: '\f104'; +} +.zmdi-archive:before { + content: '\f105'; +} +.zmdi-assignment-account:before { + content: '\f106'; +} +.zmdi-assignment-alert:before { + content: '\f107'; +} +.zmdi-assignment-check:before { + content: '\f108'; +} +.zmdi-assignment-o:before { + content: '\f109'; +} +.zmdi-assignment-return:before { + content: '\f10a'; +} +.zmdi-assignment-returned:before { + content: '\f10b'; +} +.zmdi-assignment:before { + content: '\f10c'; +} +.zmdi-attachment-alt:before { + content: '\f10d'; +} +.zmdi-attachment:before { + content: '\f10e'; +} +.zmdi-audio:before { + content: '\f10f'; +} +.zmdi-badge-check:before { + content: '\f110'; +} +.zmdi-balance-wallet:before { + content: '\f111'; +} +.zmdi-balance:before { + content: '\f112'; +} +.zmdi-battery-alert:before { + content: '\f113'; +} +.zmdi-battery-flash:before { + content: '\f114'; +} +.zmdi-battery-unknown:before { + content: '\f115'; +} +.zmdi-battery:before { + content: '\f116'; +} +.zmdi-bike:before { + content: '\f117'; +} +.zmdi-block-alt:before { + content: '\f118'; +} +.zmdi-block:before { + content: '\f119'; +} +.zmdi-boat:before { + content: '\f11a'; +} +.zmdi-book-image:before { + content: '\f11b'; +} +.zmdi-book:before { + content: '\f11c'; +} +.zmdi-bookmark-outline:before { + content: '\f11d'; +} +.zmdi-bookmark:before { + content: '\f11e'; +} +.zmdi-brush:before { + content: '\f11f'; +} +.zmdi-bug:before { + content: '\f120'; +} +.zmdi-bus:before { + content: '\f121'; +} +.zmdi-cake:before { + content: '\f122'; +} +.zmdi-car-taxi:before { + content: '\f123'; +} +.zmdi-car-wash:before { + content: '\f124'; +} +.zmdi-car:before { + content: '\f125'; +} +.zmdi-card-giftcard:before { + content: '\f126'; +} +.zmdi-card-membership:before { + content: '\f127'; +} +.zmdi-card-travel:before { + content: '\f128'; +} +.zmdi-card:before { + content: '\f129'; +} +.zmdi-case-check:before { + content: '\f12a'; +} +.zmdi-case-download:before { + content: '\f12b'; +} +.zmdi-case-play:before { + content: '\f12c'; +} +.zmdi-case:before { + content: '\f12d'; +} +.zmdi-cast-connected:before { + content: '\f12e'; +} +.zmdi-cast:before { + content: '\f12f'; +} +.zmdi-chart-donut:before { + content: '\f130'; +} +.zmdi-chart:before { + content: '\f131'; +} +.zmdi-city-alt:before { + content: '\f132'; +} +.zmdi-city:before { + content: '\f133'; +} +.zmdi-close-circle-o:before { + content: '\f134'; +} +.zmdi-close-circle:before { + content: '\f135'; +} +.zmdi-close:before { + content: '\f136'; +} +.zmdi-cocktail:before { + content: '\f137'; +} +.zmdi-code-setting:before { + content: '\f138'; +} +.zmdi-code-smartphone:before { + content: '\f139'; +} +.zmdi-code:before { + content: '\f13a'; +} +.zmdi-coffee:before { + content: '\f13b'; +} +.zmdi-collection-bookmark:before { + content: '\f13c'; +} +.zmdi-collection-case-play:before { + content: '\f13d'; +} +.zmdi-collection-folder-image:before { + content: '\f13e'; +} +.zmdi-collection-image-o:before { + content: '\f13f'; +} +.zmdi-collection-image:before { + content: '\f140'; +} +.zmdi-collection-item-1:before { + content: '\f141'; +} +.zmdi-collection-item-2:before { + content: '\f142'; +} +.zmdi-collection-item-3:before { + content: '\f143'; +} +.zmdi-collection-item-4:before { + content: '\f144'; +} +.zmdi-collection-item-5:before { + content: '\f145'; +} +.zmdi-collection-item-6:before { + content: '\f146'; +} +.zmdi-collection-item-7:before { + content: '\f147'; +} +.zmdi-collection-item-8:before { + content: '\f148'; +} +.zmdi-collection-item-9-plus:before { + content: '\f149'; +} +.zmdi-collection-item-9:before { + content: '\f14a'; +} +.zmdi-collection-item:before { + content: '\f14b'; +} +.zmdi-collection-music:before { + content: '\f14c'; +} +.zmdi-collection-pdf:before { + content: '\f14d'; +} +.zmdi-collection-plus:before { + content: '\f14e'; +} +.zmdi-collection-speaker:before { + content: '\f14f'; +} +.zmdi-collection-text:before { + content: '\f150'; +} +.zmdi-collection-video:before { + content: '\f151'; +} +.zmdi-compass:before { + content: '\f152'; +} +.zmdi-cutlery:before { + content: '\f153'; +} +.zmdi-delete:before { + content: '\f154'; +} +.zmdi-dialpad:before { + content: '\f155'; +} +.zmdi-dns:before { + content: '\f156'; +} +.zmdi-drink:before { + content: '\f157'; +} +.zmdi-edit:before { + content: '\f158'; +} +.zmdi-email-open:before { + content: '\f159'; +} +.zmdi-email:before { + content: '\f15a'; +} +.zmdi-eye-off:before { + content: '\f15b'; +} +.zmdi-eye:before { + content: '\f15c'; +} +.zmdi-eyedropper:before { + content: '\f15d'; +} +.zmdi-favorite-outline:before { + content: '\f15e'; +} +.zmdi-favorite:before { + content: '\f15f'; +} +.zmdi-filter-list:before { + content: '\f160'; +} +.zmdi-fire:before { + content: '\f161'; +} +.zmdi-flag:before { + content: '\f162'; +} +.zmdi-flare:before { + content: '\f163'; +} +.zmdi-flash-auto:before { + content: '\f164'; +} +.zmdi-flash-off:before { + content: '\f165'; +} +.zmdi-flash:before { + content: '\f166'; +} +.zmdi-flip:before { + content: '\f167'; +} +.zmdi-flower-alt:before { + content: '\f168'; +} +.zmdi-flower:before { + content: '\f169'; +} +.zmdi-font:before { + content: '\f16a'; +} +.zmdi-fullscreen-alt:before { + content: '\f16b'; +} +.zmdi-fullscreen-exit:before { + content: '\f16c'; +} +.zmdi-fullscreen:before { + content: '\f16d'; +} +.zmdi-functions:before { + content: '\f16e'; +} +.zmdi-gas-station:before { + content: '\f16f'; +} +.zmdi-gesture:before { + content: '\f170'; +} +.zmdi-globe-alt:before { + content: '\f171'; +} +.zmdi-globe-lock:before { + content: '\f172'; +} +.zmdi-globe:before { + content: '\f173'; +} +.zmdi-graduation-cap:before { + content: '\f174'; +} +.zmdi-home:before { + content: '\f175'; +} +.zmdi-hospital-alt:before { + content: '\f176'; +} +.zmdi-hospital:before { + content: '\f177'; +} +.zmdi-hotel:before { + content: '\f178'; +} +.zmdi-hourglass-alt:before { + content: '\f179'; +} +.zmdi-hourglass-outline:before { + content: '\f17a'; +} +.zmdi-hourglass:before { + content: '\f17b'; +} +.zmdi-http:before { + content: '\f17c'; +} +.zmdi-image-alt:before { + content: '\f17d'; +} +.zmdi-image-o:before { + content: '\f17e'; +} +.zmdi-image:before { + content: '\f17f'; +} +.zmdi-inbox:before { + content: '\f180'; +} +.zmdi-invert-colors-off:before { + content: '\f181'; +} +.zmdi-invert-colors:before { + content: '\f182'; +} +.zmdi-key:before { + content: '\f183'; +} +.zmdi-label-alt-outline:before { + content: '\f184'; +} +.zmdi-label-alt:before { + content: '\f185'; +} +.zmdi-label-heart:before { + content: '\f186'; +} +.zmdi-label:before { + content: '\f187'; +} +.zmdi-labels:before { + content: '\f188'; +} +.zmdi-lamp:before { + content: '\f189'; +} +.zmdi-landscape:before { + content: '\f18a'; +} +.zmdi-layers-off:before { + content: '\f18b'; +} +.zmdi-layers:before { + content: '\f18c'; +} +.zmdi-library:before { + content: '\f18d'; +} +.zmdi-link:before { + content: '\f18e'; +} +.zmdi-lock-open:before { + content: '\f18f'; +} +.zmdi-lock-outline:before { + content: '\f190'; +} +.zmdi-lock:before { + content: '\f191'; +} +.zmdi-mail-reply-all:before { + content: '\f192'; +} +.zmdi-mail-reply:before { + content: '\f193'; +} +.zmdi-mail-send:before { + content: '\f194'; +} +.zmdi-mall:before { + content: '\f195'; +} +.zmdi-map:before { + content: '\f196'; +} +.zmdi-menu:before { + content: '\f197'; +} +.zmdi-money-box:before { + content: '\f198'; +} +.zmdi-money-off:before { + content: '\f199'; +} +.zmdi-money:before { + content: '\f19a'; +} +.zmdi-more-vert:before { + content: '\f19b'; +} +.zmdi-more:before { + content: '\f19c'; +} +.zmdi-movie-alt:before { + content: '\f19d'; +} +.zmdi-movie:before { + content: '\f19e'; +} +.zmdi-nature-people:before { + content: '\f19f'; +} +.zmdi-nature:before { + content: '\f1a0'; +} +.zmdi-navigation:before { + content: '\f1a1'; +} +.zmdi-open-in-browser:before { + content: '\f1a2'; +} +.zmdi-open-in-new:before { + content: '\f1a3'; +} +.zmdi-palette:before { + content: '\f1a4'; +} +.zmdi-parking:before { + content: '\f1a5'; +} +.zmdi-pin-account:before { + content: '\f1a6'; +} +.zmdi-pin-assistant:before { + content: '\f1a7'; +} +.zmdi-pin-drop:before { + content: '\f1a8'; +} +.zmdi-pin-help:before { + content: '\f1a9'; +} +.zmdi-pin-off:before { + content: '\f1aa'; +} +.zmdi-pin:before { + content: '\f1ab'; +} +.zmdi-pizza:before { + content: '\f1ac'; +} +.zmdi-plaster:before { + content: '\f1ad'; +} +.zmdi-power-setting:before { + content: '\f1ae'; +} +.zmdi-power:before { + content: '\f1af'; +} +.zmdi-print:before { + content: '\f1b0'; +} +.zmdi-puzzle-piece:before { + content: '\f1b1'; +} +.zmdi-quote:before { + content: '\f1b2'; +} +.zmdi-railway:before { + content: '\f1b3'; +} +.zmdi-receipt:before { + content: '\f1b4'; +} +.zmdi-refresh-alt:before { + content: '\f1b5'; +} +.zmdi-refresh-sync-alert:before { + content: '\f1b6'; +} +.zmdi-refresh-sync-off:before { + content: '\f1b7'; +} +.zmdi-refresh-sync:before { + content: '\f1b8'; +} +.zmdi-refresh:before { + content: '\f1b9'; +} +.zmdi-roller:before { + content: '\f1ba'; +} +.zmdi-ruler:before { + content: '\f1bb'; +} +.zmdi-scissors:before { + content: '\f1bc'; +} +.zmdi-screen-rotation-lock:before { + content: '\f1bd'; +} +.zmdi-screen-rotation:before { + content: '\f1be'; +} +.zmdi-search-for:before { + content: '\f1bf'; +} +.zmdi-search-in-file:before { + content: '\f1c0'; +} +.zmdi-search-in-page:before { + content: '\f1c1'; +} +.zmdi-search-replace:before { + content: '\f1c2'; +} +.zmdi-search:before { + content: '\f1c3'; +} +.zmdi-seat:before { + content: '\f1c4'; +} +.zmdi-settings-square:before { + content: '\f1c5'; +} +.zmdi-settings:before { + content: '\f1c6'; +} +.zmdi-shield-check:before { + content: '\f1c7'; +} +.zmdi-shield-security:before { + content: '\f1c8'; +} +.zmdi-shopping-basket:before { + content: '\f1c9'; +} +.zmdi-shopping-cart-plus:before { + content: '\f1ca'; +} +.zmdi-shopping-cart:before { + content: '\f1cb'; +} +.zmdi-sign-in:before { + content: '\f1cc'; +} +.zmdi-sort-amount-asc:before { + content: '\f1cd'; +} +.zmdi-sort-amount-desc:before { + content: '\f1ce'; +} +.zmdi-sort-asc:before { + content: '\f1cf'; +} +.zmdi-sort-desc:before { + content: '\f1d0'; +} +.zmdi-spellcheck:before { + content: '\f1d1'; +} +.zmdi-storage:before { + content: '\f1d2'; +} +.zmdi-store-24:before { + content: '\f1d3'; +} +.zmdi-store:before { + content: '\f1d4'; +} +.zmdi-subway:before { + content: '\f1d5'; +} +.zmdi-sun:before { + content: '\f1d6'; +} +.zmdi-tab-unselected:before { + content: '\f1d7'; +} +.zmdi-tab:before { + content: '\f1d8'; +} +.zmdi-tag-close:before { + content: '\f1d9'; +} +.zmdi-tag-more:before { + content: '\f1da'; +} +.zmdi-tag:before { + content: '\f1db'; +} +.zmdi-thumb-down:before { + content: '\f1dc'; +} +.zmdi-thumb-up-down:before { + content: '\f1dd'; +} +.zmdi-thumb-up:before { + content: '\f1de'; +} +.zmdi-ticket-star:before { + content: '\f1df'; +} +.zmdi-toll:before { + content: '\f1e0'; +} +.zmdi-toys:before { + content: '\f1e1'; +} +.zmdi-traffic:before { + content: '\f1e2'; +} +.zmdi-translate:before { + content: '\f1e3'; +} +.zmdi-triangle-down:before { + content: '\f1e4'; +} +.zmdi-triangle-up:before { + content: '\f1e5'; +} +.zmdi-truck:before { + content: '\f1e6'; +} +.zmdi-turning-sign:before { + content: '\f1e7'; +} +.zmdi-wallpaper:before { + content: '\f1e8'; +} +.zmdi-washing-machine:before { + content: '\f1e9'; +} +.zmdi-window-maximize:before { + content: '\f1ea'; +} +.zmdi-window-minimize:before { + content: '\f1eb'; +} +.zmdi-window-restore:before { + content: '\f1ec'; +} +.zmdi-wrench:before { + content: '\f1ed'; +} +.zmdi-zoom-in:before { + content: '\f1ee'; +} +.zmdi-zoom-out:before { + content: '\f1ef'; +} +.zmdi-alert-circle-o:before { + content: '\f1f0'; +} +.zmdi-alert-circle:before { + content: '\f1f1'; +} +.zmdi-alert-octagon:before { + content: '\f1f2'; +} +.zmdi-alert-polygon:before { + content: '\f1f3'; +} +.zmdi-alert-triangle:before { + content: '\f1f4'; +} +.zmdi-help-outline:before { + content: '\f1f5'; +} +.zmdi-help:before { + content: '\f1f6'; +} +.zmdi-info-outline:before { + content: '\f1f7'; +} +.zmdi-info:before { + content: '\f1f8'; +} +.zmdi-notifications-active:before { + content: '\f1f9'; +} +.zmdi-notifications-add:before { + content: '\f1fa'; +} +.zmdi-notifications-none:before { + content: '\f1fb'; +} +.zmdi-notifications-off:before { + content: '\f1fc'; +} +.zmdi-notifications-paused:before { + content: '\f1fd'; +} +.zmdi-notifications:before { + content: '\f1fe'; +} +.zmdi-account-add:before { + content: '\f1ff'; +} +.zmdi-account-box-mail:before { + content: '\f200'; +} +.zmdi-account-box-o:before { + content: '\f201'; +} +.zmdi-account-box-phone:before { + content: '\f202'; +} +.zmdi-account-box:before { + content: '\f203'; +} +.zmdi-account-calendar:before { + content: '\f204'; +} +.zmdi-account-circle:before { + content: '\f205'; +} +.zmdi-account-o:before { + content: '\f206'; +} +.zmdi-account:before { + content: '\f207'; +} +.zmdi-accounts-add:before { + content: '\f208'; +} +.zmdi-accounts-alt:before { + content: '\f209'; +} +.zmdi-accounts-list-alt:before { + content: '\f20a'; +} +.zmdi-accounts-list:before { + content: '\f20b'; +} +.zmdi-accounts-outline:before { + content: '\f20c'; +} +.zmdi-accounts:before { + content: '\f20d'; +} +.zmdi-face:before { + content: '\f20e'; +} +.zmdi-female:before { + content: '\f20f'; +} +.zmdi-male-alt:before { + content: '\f210'; +} +.zmdi-male-female:before { + content: '\f211'; +} +.zmdi-male:before { + content: '\f212'; +} +.zmdi-mood-bad:before { + content: '\f213'; +} +.zmdi-mood:before { + content: '\f214'; +} +.zmdi-run:before { + content: '\f215'; +} +.zmdi-walk:before { + content: '\f216'; +} +.zmdi-cloud-box:before { + content: '\f217'; +} +.zmdi-cloud-circle:before { + content: '\f218'; +} +.zmdi-cloud-done:before { + content: '\f219'; +} +.zmdi-cloud-download:before { + content: '\f21a'; +} +.zmdi-cloud-off:before { + content: '\f21b'; +} +.zmdi-cloud-outline-alt:before { + content: '\f21c'; +} +.zmdi-cloud-outline:before { + content: '\f21d'; +} +.zmdi-cloud-upload:before { + content: '\f21e'; +} +.zmdi-cloud:before { + content: '\f21f'; +} +.zmdi-download:before { + content: '\f220'; +} +.zmdi-file-plus:before { + content: '\f221'; +} +.zmdi-file-text:before { + content: '\f222'; +} +.zmdi-file:before { + content: '\f223'; +} +.zmdi-folder-outline:before { + content: '\f224'; +} +.zmdi-folder-person:before { + content: '\f225'; +} +.zmdi-folder-star-alt:before { + content: '\f226'; +} +.zmdi-folder-star:before { + content: '\f227'; +} +.zmdi-folder:before { + content: '\f228'; +} +.zmdi-gif:before { + content: '\f229'; +} +.zmdi-upload:before { + content: '\f22a'; +} +.zmdi-border-all:before { + content: '\f22b'; +} +.zmdi-border-bottom:before { + content: '\f22c'; +} +.zmdi-border-clear:before { + content: '\f22d'; +} +.zmdi-border-color:before { + content: '\f22e'; +} +.zmdi-border-horizontal:before { + content: '\f22f'; +} +.zmdi-border-inner:before { + content: '\f230'; +} +.zmdi-border-left:before { + content: '\f231'; +} +.zmdi-border-outer:before { + content: '\f232'; +} +.zmdi-border-right:before { + content: '\f233'; +} +.zmdi-border-style:before { + content: '\f234'; +} +.zmdi-border-top:before { + content: '\f235'; +} +.zmdi-border-vertical:before { + content: '\f236'; +} +.zmdi-copy:before { + content: '\f237'; +} +.zmdi-crop:before { + content: '\f238'; +} +.zmdi-format-align-center:before { + content: '\f239'; +} +.zmdi-format-align-justify:before { + content: '\f23a'; +} +.zmdi-format-align-left:before { + content: '\f23b'; +} +.zmdi-format-align-right:before { + content: '\f23c'; +} +.zmdi-format-bold:before { + content: '\f23d'; +} +.zmdi-format-clear-all:before { + content: '\f23e'; +} +.zmdi-format-clear:before { + content: '\f23f'; +} +.zmdi-format-color-fill:before { + content: '\f240'; +} +.zmdi-format-color-reset:before { + content: '\f241'; +} +.zmdi-format-color-text:before { + content: '\f242'; +} +.zmdi-format-indent-decrease:before { + content: '\f243'; +} +.zmdi-format-indent-increase:before { + content: '\f244'; +} +.zmdi-format-italic:before { + content: '\f245'; +} +.zmdi-format-line-spacing:before { + content: '\f246'; +} +.zmdi-format-list-bulleted:before { + content: '\f247'; +} +.zmdi-format-list-numbered:before { + content: '\f248'; +} +.zmdi-format-ltr:before { + content: '\f249'; +} +.zmdi-format-rtl:before { + content: '\f24a'; +} +.zmdi-format-size:before { + content: '\f24b'; +} +.zmdi-format-strikethrough-s:before { + content: '\f24c'; +} +.zmdi-format-strikethrough:before { + content: '\f24d'; +} +.zmdi-format-subject:before { + content: '\f24e'; +} +.zmdi-format-underlined:before { + content: '\f24f'; +} +.zmdi-format-valign-bottom:before { + content: '\f250'; +} +.zmdi-format-valign-center:before { + content: '\f251'; +} +.zmdi-format-valign-top:before { + content: '\f252'; +} +.zmdi-redo:before { + content: '\f253'; +} +.zmdi-select-all:before { + content: '\f254'; +} +.zmdi-space-bar:before { + content: '\f255'; +} +.zmdi-text-format:before { + content: '\f256'; +} +.zmdi-transform:before { + content: '\f257'; +} +.zmdi-undo:before { + content: '\f258'; +} +.zmdi-wrap-text:before { + content: '\f259'; +} +.zmdi-comment-alert:before { + content: '\f25a'; +} +.zmdi-comment-alt-text:before { + content: '\f25b'; +} +.zmdi-comment-alt:before { + content: '\f25c'; +} +.zmdi-comment-edit:before { + content: '\f25d'; +} +.zmdi-comment-image:before { + content: '\f25e'; +} +.zmdi-comment-list:before { + content: '\f25f'; +} +.zmdi-comment-more:before { + content: '\f260'; +} +.zmdi-comment-outline:before { + content: '\f261'; +} +.zmdi-comment-text-alt:before { + content: '\f262'; +} +.zmdi-comment-text:before { + content: '\f263'; +} +.zmdi-comment-video:before { + content: '\f264'; +} +.zmdi-comment:before { + content: '\f265'; +} +.zmdi-comments:before { + content: '\f266'; +} +.zmdi-check-all:before { + content: '\f267'; +} +.zmdi-check-circle-u:before { + content: '\f268'; +} +.zmdi-check-circle:before { + content: '\f269'; +} +.zmdi-check-square:before { + content: '\f26a'; +} +.zmdi-check:before { + content: '\f26b'; +} +.zmdi-circle-o:before { + content: '\f26c'; +} +.zmdi-circle:before { + content: '\f26d'; +} +.zmdi-dot-circle-alt:before { + content: '\f26e'; +} +.zmdi-dot-circle:before { + content: '\f26f'; +} +.zmdi-minus-circle-outline:before { + content: '\f270'; +} +.zmdi-minus-circle:before { + content: '\f271'; +} +.zmdi-minus-square:before { + content: '\f272'; +} +.zmdi-minus:before { + content: '\f273'; +} +.zmdi-plus-circle-o-duplicate:before { + content: '\f274'; +} +.zmdi-plus-circle-o:before { + content: '\f275'; +} +.zmdi-plus-circle:before { + content: '\f276'; +} +.zmdi-plus-square:before { + content: '\f277'; +} +.zmdi-plus:before { + content: '\f278'; +} +.zmdi-square-o:before { + content: '\f279'; +} +.zmdi-star-circle:before { + content: '\f27a'; +} +.zmdi-star-half:before { + content: '\f27b'; +} +.zmdi-star-outline:before { + content: '\f27c'; +} +.zmdi-star:before { + content: '\f27d'; +} +.zmdi-bluetooth-connected:before { + content: '\f27e'; +} +.zmdi-bluetooth-off:before { + content: '\f27f'; +} +.zmdi-bluetooth-search:before { + content: '\f280'; +} +.zmdi-bluetooth-setting:before { + content: '\f281'; +} +.zmdi-bluetooth:before { + content: '\f282'; +} +.zmdi-camera-add:before { + content: '\f283'; +} +.zmdi-camera-alt:before { + content: '\f284'; +} +.zmdi-camera-bw:before { + content: '\f285'; +} +.zmdi-camera-front:before { + content: '\f286'; +} +.zmdi-camera-mic:before { + content: '\f287'; +} +.zmdi-camera-party-mode:before { + content: '\f288'; +} +.zmdi-camera-rear:before { + content: '\f289'; +} +.zmdi-camera-roll:before { + content: '\f28a'; +} +.zmdi-camera-switch:before { + content: '\f28b'; +} +.zmdi-camera:before { + content: '\f28c'; +} +.zmdi-card-alert:before { + content: '\f28d'; +} +.zmdi-card-off:before { + content: '\f28e'; +} +.zmdi-card-sd:before { + content: '\f28f'; +} +.zmdi-card-sim:before { + content: '\f290'; +} +.zmdi-desktop-mac:before { + content: '\f291'; +} +.zmdi-desktop-windows:before { + content: '\f292'; +} +.zmdi-device-hub:before { + content: '\f293'; +} +.zmdi-devices-off:before { + content: '\f294'; +} +.zmdi-devices:before { + content: '\f295'; +} +.zmdi-dock:before { + content: '\f296'; +} +.zmdi-floppy:before { + content: '\f297'; +} +.zmdi-gamepad:before { + content: '\f298'; +} +.zmdi-gps-dot:before { + content: '\f299'; +} +.zmdi-gps-off:before { + content: '\f29a'; +} +.zmdi-gps:before { + content: '\f29b'; +} +.zmdi-headset-mic:before { + content: '\f29c'; +} +.zmdi-headset:before { + content: '\f29d'; +} +.zmdi-input-antenna:before { + content: '\f29e'; +} +.zmdi-input-composite:before { + content: '\f29f'; +} +.zmdi-input-hdmi:before { + content: '\f2a0'; +} +.zmdi-input-power:before { + content: '\f2a1'; +} +.zmdi-input-svideo:before { + content: '\f2a2'; +} +.zmdi-keyboard-hide:before { + content: '\f2a3'; +} +.zmdi-keyboard:before { + content: '\f2a4'; +} +.zmdi-laptop-chromebook:before { + content: '\f2a5'; +} +.zmdi-laptop-mac:before { + content: '\f2a6'; +} +.zmdi-laptop:before { + content: '\f2a7'; +} +.zmdi-mic-off:before { + content: '\f2a8'; +} +.zmdi-mic-outline:before { + content: '\f2a9'; +} +.zmdi-mic-setting:before { + content: '\f2aa'; +} +.zmdi-mic:before { + content: '\f2ab'; +} +.zmdi-mouse:before { + content: '\f2ac'; +} +.zmdi-network-alert:before { + content: '\f2ad'; +} +.zmdi-network-locked:before { + content: '\f2ae'; +} +.zmdi-network-off:before { + content: '\f2af'; +} +.zmdi-network-outline:before { + content: '\f2b0'; +} +.zmdi-network-setting:before { + content: '\f2b1'; +} +.zmdi-network:before { + content: '\f2b2'; +} +.zmdi-phone-bluetooth:before { + content: '\f2b3'; +} +.zmdi-phone-end:before { + content: '\f2b4'; +} +.zmdi-phone-forwarded:before { + content: '\f2b5'; +} +.zmdi-phone-in-talk:before { + content: '\f2b6'; +} +.zmdi-phone-locked:before { + content: '\f2b7'; +} +.zmdi-phone-missed:before { + content: '\f2b8'; +} +.zmdi-phone-msg:before { + content: '\f2b9'; +} +.zmdi-phone-paused:before { + content: '\f2ba'; +} +.zmdi-phone-ring:before { + content: '\f2bb'; +} +.zmdi-phone-setting:before { + content: '\f2bc'; +} +.zmdi-phone-sip:before { + content: '\f2bd'; +} +.zmdi-phone:before { + content: '\f2be'; +} +.zmdi-portable-wifi-changes:before { + content: '\f2bf'; +} +.zmdi-portable-wifi-off:before { + content: '\f2c0'; +} +.zmdi-portable-wifi:before { + content: '\f2c1'; +} +.zmdi-radio:before { + content: '\f2c2'; +} +.zmdi-reader:before { + content: '\f2c3'; +} +.zmdi-remote-control-alt:before { + content: '\f2c4'; +} +.zmdi-remote-control:before { + content: '\f2c5'; +} +.zmdi-router:before { + content: '\f2c6'; +} +.zmdi-scanner:before { + content: '\f2c7'; +} +.zmdi-smartphone-android:before { + content: '\f2c8'; +} +.zmdi-smartphone-download:before { + content: '\f2c9'; +} +.zmdi-smartphone-erase:before { + content: '\f2ca'; +} +.zmdi-smartphone-info:before { + content: '\f2cb'; +} +.zmdi-smartphone-iphone:before { + content: '\f2cc'; +} +.zmdi-smartphone-landscape-lock:before { + content: '\f2cd'; +} +.zmdi-smartphone-landscape:before { + content: '\f2ce'; +} +.zmdi-smartphone-lock:before { + content: '\f2cf'; +} +.zmdi-smartphone-portrait-lock:before { + content: '\f2d0'; +} +.zmdi-smartphone-ring:before { + content: '\f2d1'; +} +.zmdi-smartphone-setting:before { + content: '\f2d2'; +} +.zmdi-smartphone-setup:before { + content: '\f2d3'; +} +.zmdi-smartphone:before { + content: '\f2d4'; +} +.zmdi-speaker:before { + content: '\f2d5'; +} +.zmdi-tablet-android:before { + content: '\f2d6'; +} +.zmdi-tablet-mac:before { + content: '\f2d7'; +} +.zmdi-tablet:before { + content: '\f2d8'; +} +.zmdi-tv-alt-play:before { + content: '\f2d9'; +} +.zmdi-tv-list:before { + content: '\f2da'; +} +.zmdi-tv-play:before { + content: '\f2db'; +} +.zmdi-tv:before { + content: '\f2dc'; +} +.zmdi-usb:before { + content: '\f2dd'; +} +.zmdi-videocam-off:before { + content: '\f2de'; +} +.zmdi-videocam-switch:before { + content: '\f2df'; +} +.zmdi-videocam:before { + content: '\f2e0'; +} +.zmdi-watch:before { + content: '\f2e1'; +} +.zmdi-wifi-alt-2:before { + content: '\f2e2'; +} +.zmdi-wifi-alt:before { + content: '\f2e3'; +} +.zmdi-wifi-info:before { + content: '\f2e4'; +} +.zmdi-wifi-lock:before { + content: '\f2e5'; +} +.zmdi-wifi-off:before { + content: '\f2e6'; +} +.zmdi-wifi-outline:before { + content: '\f2e7'; +} +.zmdi-wifi:before { + content: '\f2e8'; +} +.zmdi-arrow-left-bottom:before { + content: '\f2e9'; +} +.zmdi-arrow-left:before { + content: '\f2ea'; +} +.zmdi-arrow-merge:before { + content: '\f2eb'; +} +.zmdi-arrow-missed:before { + content: '\f2ec'; +} +.zmdi-arrow-right-top:before { + content: '\f2ed'; +} +.zmdi-arrow-right:before { + content: '\f2ee'; +} +.zmdi-arrow-split:before { + content: '\f2ef'; +} +.zmdi-arrows:before { + content: '\f2f0'; +} +.zmdi-caret-down-circle:before { + content: '\f2f1'; +} +.zmdi-caret-down:before { + content: '\f2f2'; +} +.zmdi-caret-left-circle:before { + content: '\f2f3'; +} +.zmdi-caret-left:before { + content: '\f2f4'; +} +.zmdi-caret-right-circle:before { + content: '\f2f5'; +} +.zmdi-caret-right:before { + content: '\f2f6'; +} +.zmdi-caret-up-circle:before { + content: '\f2f7'; +} +.zmdi-caret-up:before { + content: '\f2f8'; +} +.zmdi-chevron-down:before { + content: '\f2f9'; +} +.zmdi-chevron-left:before { + content: '\f2fa'; +} +.zmdi-chevron-right:before { + content: '\f2fb'; +} +.zmdi-chevron-up:before { + content: '\f2fc'; +} +.zmdi-forward:before { + content: '\f2fd'; +} +.zmdi-long-arrow-down:before { + content: '\f2fe'; +} +.zmdi-long-arrow-left:before { + content: '\f2ff'; +} +.zmdi-long-arrow-return:before { + content: '\f300'; +} +.zmdi-long-arrow-right:before { + content: '\f301'; +} +.zmdi-long-arrow-tab:before { + content: '\f302'; +} +.zmdi-long-arrow-up:before { + content: '\f303'; +} +.zmdi-rotate-ccw:before { + content: '\f304'; +} +.zmdi-rotate-cw:before { + content: '\f305'; +} +.zmdi-rotate-left:before { + content: '\f306'; +} +.zmdi-rotate-right:before { + content: '\f307'; +} +.zmdi-square-down:before { + content: '\f308'; +} +.zmdi-square-right:before { + content: '\f309'; +} +.zmdi-swap-alt:before { + content: '\f30a'; +} +.zmdi-swap-vertical-circle:before { + content: '\f30b'; +} +.zmdi-swap-vertical:before { + content: '\f30c'; +} +.zmdi-swap:before { + content: '\f30d'; +} +.zmdi-trending-down:before { + content: '\f30e'; +} +.zmdi-trending-flat:before { + content: '\f30f'; +} +.zmdi-trending-up:before { + content: '\f310'; +} +.zmdi-unfold-less:before { + content: '\f311'; +} +.zmdi-unfold-more:before { + content: '\f312'; +} +.zmdi-apps:before { + content: '\f313'; +} +.zmdi-grid-off:before { + content: '\f314'; +} +.zmdi-grid:before { + content: '\f315'; +} +.zmdi-view-agenda:before { + content: '\f316'; +} +.zmdi-view-array:before { + content: '\f317'; +} +.zmdi-view-carousel:before { + content: '\f318'; +} +.zmdi-view-column:before { + content: '\f319'; +} +.zmdi-view-comfy:before { + content: '\f31a'; +} +.zmdi-view-compact:before { + content: '\f31b'; +} +.zmdi-view-dashboard:before { + content: '\f31c'; +} +.zmdi-view-day:before { + content: '\f31d'; +} +.zmdi-view-headline:before { + content: '\f31e'; +} +.zmdi-view-list-alt:before { + content: '\f31f'; +} +.zmdi-view-list:before { + content: '\f320'; +} +.zmdi-view-module:before { + content: '\f321'; +} +.zmdi-view-quilt:before { + content: '\f322'; +} +.zmdi-view-stream:before { + content: '\f323'; +} +.zmdi-view-subtitles:before { + content: '\f324'; +} +.zmdi-view-toc:before { + content: '\f325'; +} +.zmdi-view-web:before { + content: '\f326'; +} +.zmdi-view-week:before { + content: '\f327'; +} +.zmdi-widgets:before { + content: '\f328'; +} +.zmdi-alarm-check:before { + content: '\f329'; +} +.zmdi-alarm-off:before { + content: '\f32a'; +} +.zmdi-alarm-plus:before { + content: '\f32b'; +} +.zmdi-alarm-snooze:before { + content: '\f32c'; +} +.zmdi-alarm:before { + content: '\f32d'; +} +.zmdi-calendar-alt:before { + content: '\f32e'; +} +.zmdi-calendar-check:before { + content: '\f32f'; +} +.zmdi-calendar-close:before { + content: '\f330'; +} +.zmdi-calendar-note:before { + content: '\f331'; +} +.zmdi-calendar:before { + content: '\f332'; +} +.zmdi-time-countdown:before { + content: '\f333'; +} +.zmdi-time-interval:before { + content: '\f334'; +} +.zmdi-time-restore-setting:before { + content: '\f335'; +} +.zmdi-time-restore:before { + content: '\f336'; +} +.zmdi-time:before { + content: '\f337'; +} +.zmdi-timer-off:before { + content: '\f338'; +} +.zmdi-timer:before { + content: '\f339'; +} +.zmdi-android-alt:before { + content: '\f33a'; +} +.zmdi-android:before { + content: '\f33b'; +} +.zmdi-apple:before { + content: '\f33c'; +} +.zmdi-behance:before { + content: '\f33d'; +} +.zmdi-codepen:before { + content: '\f33e'; +} +.zmdi-dribbble:before { + content: '\f33f'; +} +.zmdi-dropbox:before { + content: '\f340'; +} +.zmdi-evernote:before { + content: '\f341'; +} +.zmdi-facebook-box:before { + content: '\f342'; +} +.zmdi-facebook:before { + content: '\f343'; +} +.zmdi-github-box:before { + content: '\f344'; +} +.zmdi-github:before { + content: '\f345'; +} +.zmdi-google-drive:before { + content: '\f346'; +} +.zmdi-google-earth:before { + content: '\f347'; +} +.zmdi-google-glass:before { + content: '\f348'; +} +.zmdi-google-maps:before { + content: '\f349'; +} +.zmdi-google-pages:before { + content: '\f34a'; +} +.zmdi-google-play:before { + content: '\f34b'; +} +.zmdi-google-plus-box:before { + content: '\f34c'; +} +.zmdi-google-plus:before { + content: '\f34d'; +} +.zmdi-google:before { + content: '\f34e'; +} +.zmdi-instagram:before { + content: '\f34f'; +} +.zmdi-language-css3:before { + content: '\f350'; +} +.zmdi-language-html5:before { + content: '\f351'; +} +.zmdi-language-javascript:before { + content: '\f352'; +} +.zmdi-language-python-alt:before { + content: '\f353'; +} +.zmdi-language-python:before { + content: '\f354'; +} +.zmdi-lastfm:before { + content: '\f355'; +} +.zmdi-linkedin-box:before { + content: '\f356'; +} +.zmdi-paypal:before { + content: '\f357'; +} +.zmdi-pinterest-box:before { + content: '\f358'; +} +.zmdi-pocket:before { + content: '\f359'; +} +.zmdi-polymer:before { + content: '\f35a'; +} +.zmdi-share:before { + content: '\f35b'; +} +.zmdi-stackoverflow:before { + content: '\f35c'; +} +.zmdi-steam-square:before { + content: '\f35d'; +} +.zmdi-steam:before { + content: '\f35e'; +} +.zmdi-twitter-box:before { + content: '\f35f'; +} +.zmdi-twitter:before { + content: '\f360'; +} +.zmdi-vk:before { + content: '\f361'; +} +.zmdi-wikipedia:before { + content: '\f362'; +} +.zmdi-windows:before { + content: '\f363'; +} +.zmdi-aspect-ratio-alt:before { + content: '\f364'; +} +.zmdi-aspect-ratio:before { + content: '\f365'; +} +.zmdi-blur-circular:before { + content: '\f366'; +} +.zmdi-blur-linear:before { + content: '\f367'; +} +.zmdi-blur-off:before { + content: '\f368'; +} +.zmdi-blur:before { + content: '\f369'; +} +.zmdi-brightness-2:before { + content: '\f36a'; +} +.zmdi-brightness-3:before { + content: '\f36b'; +} +.zmdi-brightness-4:before { + content: '\f36c'; +} +.zmdi-brightness-5:before { + content: '\f36d'; +} +.zmdi-brightness-6:before { + content: '\f36e'; +} +.zmdi-brightness-7:before { + content: '\f36f'; +} +.zmdi-brightness-auto:before { + content: '\f370'; +} +.zmdi-brightness-setting:before { + content: '\f371'; +} +.zmdi-broken-image:before { + content: '\f372'; +} +.zmdi-center-focus-strong:before { + content: '\f373'; +} +.zmdi-center-focus-weak:before { + content: '\f374'; +} +.zmdi-compare:before { + content: '\f375'; +} +.zmdi-crop-16-9:before { + content: '\f376'; +} +.zmdi-crop-3-2:before { + content: '\f377'; +} +.zmdi-crop-5-4:before { + content: '\f378'; +} +.zmdi-crop-7-5:before { + content: '\f379'; +} +.zmdi-crop-din:before { + content: '\f37a'; +} +.zmdi-crop-free:before { + content: '\f37b'; +} +.zmdi-crop-landscape:before { + content: '\f37c'; +} +.zmdi-crop-portrait:before { + content: '\f37d'; +} +.zmdi-crop-square:before { + content: '\f37e'; +} +.zmdi-exposure-alt:before { + content: '\f37f'; +} +.zmdi-exposure:before { + content: '\f380'; +} +.zmdi-filter-b-and-w:before { + content: '\f381'; +} +.zmdi-filter-center-focus:before { + content: '\f382'; +} +.zmdi-filter-frames:before { + content: '\f383'; +} +.zmdi-filter-tilt-shift:before { + content: '\f384'; +} +.zmdi-gradient:before { + content: '\f385'; +} +.zmdi-grain:before { + content: '\f386'; +} +.zmdi-graphic-eq:before { + content: '\f387'; +} +.zmdi-hdr-off:before { + content: '\f388'; +} +.zmdi-hdr-strong:before { + content: '\f389'; +} +.zmdi-hdr-weak:before { + content: '\f38a'; +} +.zmdi-hdr:before { + content: '\f38b'; +} +.zmdi-iridescent:before { + content: '\f38c'; +} +.zmdi-leak-off:before { + content: '\f38d'; +} +.zmdi-leak:before { + content: '\f38e'; +} +.zmdi-looks:before { + content: '\f38f'; +} +.zmdi-loupe:before { + content: '\f390'; +} +.zmdi-panorama-horizontal:before { + content: '\f391'; +} +.zmdi-panorama-vertical:before { + content: '\f392'; +} +.zmdi-panorama-wide-angle:before { + content: '\f393'; +} +.zmdi-photo-size-select-large:before { + content: '\f394'; +} +.zmdi-photo-size-select-small:before { + content: '\f395'; +} +.zmdi-picture-in-picture:before { + content: '\f396'; +} +.zmdi-slideshow:before { + content: '\f397'; +} +.zmdi-texture:before { + content: '\f398'; +} +.zmdi-tonality:before { + content: '\f399'; +} +.zmdi-vignette:before { + content: '\f39a'; +} +.zmdi-wb-auto:before { + content: '\f39b'; +} +.zmdi-eject-alt:before { + content: '\f39c'; +} +.zmdi-eject:before { + content: '\f39d'; +} +.zmdi-equalizer:before { + content: '\f39e'; +} +.zmdi-fast-forward:before { + content: '\f39f'; +} +.zmdi-fast-rewind:before { + content: '\f3a0'; +} +.zmdi-forward-10:before { + content: '\f3a1'; +} +.zmdi-forward-30:before { + content: '\f3a2'; +} +.zmdi-forward-5:before { + content: '\f3a3'; +} +.zmdi-hearing:before { + content: '\f3a4'; +} +.zmdi-pause-circle-outline:before { + content: '\f3a5'; +} +.zmdi-pause-circle:before { + content: '\f3a6'; +} +.zmdi-pause:before { + content: '\f3a7'; +} +.zmdi-play-circle-outline:before { + content: '\f3a8'; +} +.zmdi-play-circle:before { + content: '\f3a9'; +} +.zmdi-play:before { + content: '\f3aa'; +} +.zmdi-playlist-audio:before { + content: '\f3ab'; +} +.zmdi-playlist-plus:before { + content: '\f3ac'; +} +.zmdi-repeat-one:before { + content: '\f3ad'; +} +.zmdi-repeat:before { + content: '\f3ae'; +} +.zmdi-replay-10:before { + content: '\f3af'; +} +.zmdi-replay-30:before { + content: '\f3b0'; +} +.zmdi-replay-5:before { + content: '\f3b1'; +} +.zmdi-replay:before { + content: '\f3b2'; +} +.zmdi-shuffle:before { + content: '\f3b3'; +} +.zmdi-skip-next:before { + content: '\f3b4'; +} +.zmdi-skip-previous:before { + content: '\f3b5'; +} +.zmdi-stop:before { + content: '\f3b6'; +} +.zmdi-surround-sound:before { + content: '\f3b7'; +} +.zmdi-tune:before { + content: '\f3b8'; +} +.zmdi-volume-down:before { + content: '\f3b9'; +} +.zmdi-volume-mute:before { + content: '\f3ba'; +} +.zmdi-volume-off:before { + content: '\f3bb'; +} +.zmdi-volume-up:before { + content: '\f3bc'; +} +.zmdi-n-1-square:before { + content: '\f3bd'; +} +.zmdi-n-2-square:before { + content: '\f3be'; +} +.zmdi-n-3-square:before { + content: '\f3bf'; +} +.zmdi-n-4-square:before { + content: '\f3c0'; +} +.zmdi-n-5-square:before { + content: '\f3c1'; +} +.zmdi-n-6-square:before { + content: '\f3c2'; +} +.zmdi-neg-1:before { + content: '\f3c3'; +} +.zmdi-neg-2:before { + content: '\f3c4'; +} +.zmdi-plus-1:before { + content: '\f3c5'; +} +.zmdi-plus-2:before { + content: '\f3c6'; +} +.zmdi-sec-10:before { + content: '\f3c7'; +} +.zmdi-sec-3:before { + content: '\f3c8'; +} +.zmdi-zero:before { + content: '\f3c9'; +} +.zmdi-airline-seat-flat-angled:before { + content: '\f3ca'; +} +.zmdi-airline-seat-flat:before { + content: '\f3cb'; +} +.zmdi-airline-seat-individual-suite:before { + content: '\f3cc'; +} +.zmdi-airline-seat-legroom-extra:before { + content: '\f3cd'; +} +.zmdi-airline-seat-legroom-normal:before { + content: '\f3ce'; +} +.zmdi-airline-seat-legroom-reduced:before { + content: '\f3cf'; +} +.zmdi-airline-seat-recline-extra:before { + content: '\f3d0'; +} +.zmdi-airline-seat-recline-normal:before { + content: '\f3d1'; +} +.zmdi-airplay:before { + content: '\f3d2'; +} +.zmdi-closed-caption:before { + content: '\f3d3'; +} +.zmdi-confirmation-number:before { + content: '\f3d4'; +} +.zmdi-developer-board:before { + content: '\f3d5'; +} +.zmdi-disc-full:before { + content: '\f3d6'; +} +.zmdi-explicit:before { + content: '\f3d7'; +} +.zmdi-flight-land:before { + content: '\f3d8'; +} +.zmdi-flight-takeoff:before { + content: '\f3d9'; +} +.zmdi-flip-to-back:before { + content: '\f3da'; +} +.zmdi-flip-to-front:before { + content: '\f3db'; +} +.zmdi-group-work:before { + content: '\f3dc'; +} +.zmdi-hd:before { + content: '\f3dd'; +} +.zmdi-hq:before { + content: '\f3de'; +} +.zmdi-markunread-mailbox:before { + content: '\f3df'; +} +.zmdi-memory:before { + content: '\f3e0'; +} +.zmdi-nfc:before { + content: '\f3e1'; +} +.zmdi-play-for-work:before { + content: '\f3e2'; +} +.zmdi-power-input:before { + content: '\f3e3'; +} +.zmdi-present-to-all:before { + content: '\f3e4'; +} +.zmdi-satellite:before { + content: '\f3e5'; +} +.zmdi-tap-and-play:before { + content: '\f3e6'; +} +.zmdi-vibration:before { + content: '\f3e7'; +} +.zmdi-voicemail:before { + content: '\f3e8'; +} +.zmdi-group:before { + content: '\f3e9'; +} +.zmdi-rss:before { + content: '\f3ea'; +} +.zmdi-shape:before { + content: '\f3eb'; +} +.zmdi-spinner:before { + content: '\f3ec'; +} +.zmdi-ungroup:before { + content: '\f3ed'; +} +.zmdi-500px:before { + content: '\f3ee'; +} +.zmdi-8tracks:before { + content: '\f3ef'; +} +.zmdi-amazon:before { + content: '\f3f0'; +} +.zmdi-blogger:before { + content: '\f3f1'; +} +.zmdi-delicious:before { + content: '\f3f2'; +} +.zmdi-disqus:before { + content: '\f3f3'; +} +.zmdi-flattr:before { + content: '\f3f4'; +} +.zmdi-flickr:before { + content: '\f3f5'; +} +.zmdi-github-alt:before { + content: '\f3f6'; +} +.zmdi-google-old:before { + content: '\f3f7'; +} +.zmdi-linkedin:before { + content: '\f3f8'; +} +.zmdi-odnoklassniki:before { + content: '\f3f9'; +} +.zmdi-outlook:before { + content: '\f3fa'; +} +.zmdi-paypal-alt:before { + content: '\f3fb'; +} +.zmdi-pinterest:before { + content: '\f3fc'; +} +.zmdi-playstation:before { + content: '\f3fd'; +} +.zmdi-reddit:before { + content: '\f3fe'; +} +.zmdi-skype:before { + content: '\f3ff'; +} +.zmdi-slideshare:before { + content: '\f400'; +} +.zmdi-soundcloud:before { + content: '\f401'; +} +.zmdi-tumblr:before { + content: '\f402'; +} +.zmdi-twitch:before { + content: '\f403'; +} +.zmdi-vimeo:before { + content: '\f404'; +} +.zmdi-whatsapp:before { + content: '\f405'; +} +.zmdi-xbox:before { + content: '\f406'; +} +.zmdi-yahoo:before { + content: '\f407'; +} +.zmdi-youtube-play:before { + content: '\f408'; +} +.zmdi-youtube:before { + content: '\f409'; +} +.zmdi-3d-rotation:before { + content: '\f101'; +} +.zmdi-airplane-off:before { + content: '\f102'; +} +.zmdi-airplane:before { + content: '\f103'; +} +.zmdi-album:before { + content: '\f104'; +} +.zmdi-archive:before { + content: '\f105'; +} +.zmdi-assignment-account:before { + content: '\f106'; +} +.zmdi-assignment-alert:before { + content: '\f107'; +} +.zmdi-assignment-check:before { + content: '\f108'; +} +.zmdi-assignment-o:before { + content: '\f109'; +} +.zmdi-assignment-return:before { + content: '\f10a'; +} +.zmdi-assignment-returned:before { + content: '\f10b'; +} +.zmdi-assignment:before { + content: '\f10c'; +} +.zmdi-attachment-alt:before { + content: '\f10d'; +} +.zmdi-attachment:before { + content: '\f10e'; +} +.zmdi-audio:before { + content: '\f10f'; +} +.zmdi-badge-check:before { + content: '\f110'; +} +.zmdi-balance-wallet:before { + content: '\f111'; +} +.zmdi-balance:before { + content: '\f112'; +} +.zmdi-battery-alert:before { + content: '\f113'; +} +.zmdi-battery-flash:before { + content: '\f114'; +} +.zmdi-battery-unknown:before { + content: '\f115'; +} +.zmdi-battery:before { + content: '\f116'; +} +.zmdi-bike:before { + content: '\f117'; +} +.zmdi-block-alt:before { + content: '\f118'; +} +.zmdi-block:before { + content: '\f119'; +} +.zmdi-boat:before { + content: '\f11a'; +} +.zmdi-book-image:before { + content: '\f11b'; +} +.zmdi-book:before { + content: '\f11c'; +} +.zmdi-bookmark-outline:before { + content: '\f11d'; +} +.zmdi-bookmark:before { + content: '\f11e'; +} +.zmdi-brush:before { + content: '\f11f'; +} +.zmdi-bug:before { + content: '\f120'; +} +.zmdi-bus:before { + content: '\f121'; +} +.zmdi-cake:before { + content: '\f122'; +} +.zmdi-car-taxi:before { + content: '\f123'; +} +.zmdi-car-wash:before { + content: '\f124'; +} +.zmdi-car:before { + content: '\f125'; +} +.zmdi-card-giftcard:before { + content: '\f126'; +} +.zmdi-card-membership:before { + content: '\f127'; +} +.zmdi-card-travel:before { + content: '\f128'; +} +.zmdi-card:before { + content: '\f129'; +} +.zmdi-case-check:before { + content: '\f12a'; +} +.zmdi-case-download:before { + content: '\f12b'; +} +.zmdi-case-play:before { + content: '\f12c'; +} +.zmdi-case:before { + content: '\f12d'; +} +.zmdi-cast-connected:before { + content: '\f12e'; +} +.zmdi-cast:before { + content: '\f12f'; +} +.zmdi-chart-donut:before { + content: '\f130'; +} +.zmdi-chart:before { + content: '\f131'; +} +.zmdi-city-alt:before { + content: '\f132'; +} +.zmdi-city:before { + content: '\f133'; +} +.zmdi-close-circle-o:before { + content: '\f134'; +} +.zmdi-close-circle:before { + content: '\f135'; +} +.zmdi-close:before { + content: '\f136'; +} +.zmdi-cocktail:before { + content: '\f137'; +} +.zmdi-code-setting:before { + content: '\f138'; +} +.zmdi-code-smartphone:before { + content: '\f139'; +} +.zmdi-code:before { + content: '\f13a'; +} +.zmdi-coffee:before { + content: '\f13b'; +} +.zmdi-collection-bookmark:before { + content: '\f13c'; +} +.zmdi-collection-case-play:before { + content: '\f13d'; +} +.zmdi-collection-folder-image:before { + content: '\f13e'; +} +.zmdi-collection-image-o:before { + content: '\f13f'; +} +.zmdi-collection-image:before { + content: '\f140'; +} +.zmdi-collection-item-1:before { + content: '\f141'; +} +.zmdi-collection-item-2:before { + content: '\f142'; +} +.zmdi-collection-item-3:before { + content: '\f143'; +} +.zmdi-collection-item-4:before { + content: '\f144'; +} +.zmdi-collection-item-5:before { + content: '\f145'; +} +.zmdi-collection-item-6:before { + content: '\f146'; +} +.zmdi-collection-item-7:before { + content: '\f147'; +} +.zmdi-collection-item-8:before { + content: '\f148'; +} +.zmdi-collection-item-9-plus:before { + content: '\f149'; +} +.zmdi-collection-item-9:before { + content: '\f14a'; +} +.zmdi-collection-item:before { + content: '\f14b'; +} +.zmdi-collection-music:before { + content: '\f14c'; +} +.zmdi-collection-pdf:before { + content: '\f14d'; +} +.zmdi-collection-plus:before { + content: '\f14e'; +} +.zmdi-collection-speaker:before { + content: '\f14f'; +} +.zmdi-collection-text:before { + content: '\f150'; +} +.zmdi-collection-video:before { + content: '\f151'; +} +.zmdi-compass:before { + content: '\f152'; +} +.zmdi-cutlery:before { + content: '\f153'; +} +.zmdi-delete:before { + content: '\f154'; +} +.zmdi-dialpad:before { + content: '\f155'; +} +.zmdi-dns:before { + content: '\f156'; +} +.zmdi-drink:before { + content: '\f157'; +} +.zmdi-edit:before { + content: '\f158'; +} +.zmdi-email-open:before { + content: '\f159'; +} +.zmdi-email:before { + content: '\f15a'; +} +.zmdi-eye-off:before { + content: '\f15b'; +} +.zmdi-eye:before { + content: '\f15c'; +} +.zmdi-eyedropper:before { + content: '\f15d'; +} +.zmdi-favorite-outline:before { + content: '\f15e'; +} +.zmdi-favorite:before { + content: '\f15f'; +} +.zmdi-filter-list:before { + content: '\f160'; +} +.zmdi-fire:before { + content: '\f161'; +} +.zmdi-flag:before { + content: '\f162'; +} +.zmdi-flare:before { + content: '\f163'; +} +.zmdi-flash-auto:before { + content: '\f164'; +} +.zmdi-flash-off:before { + content: '\f165'; +} +.zmdi-flash:before { + content: '\f166'; +} +.zmdi-flip:before { + content: '\f167'; +} +.zmdi-flower-alt:before { + content: '\f168'; +} +.zmdi-flower:before { + content: '\f169'; +} +.zmdi-font:before { + content: '\f16a'; +} +.zmdi-fullscreen-alt:before { + content: '\f16b'; +} +.zmdi-fullscreen-exit:before { + content: '\f16c'; +} +.zmdi-fullscreen:before { + content: '\f16d'; +} +.zmdi-functions:before { + content: '\f16e'; +} +.zmdi-gas-station:before { + content: '\f16f'; +} +.zmdi-gesture:before { + content: '\f170'; +} +.zmdi-globe-alt:before { + content: '\f171'; +} +.zmdi-globe-lock:before { + content: '\f172'; +} +.zmdi-globe:before { + content: '\f173'; +} +.zmdi-graduation-cap:before { + content: '\f174'; +} +.zmdi-home:before { + content: '\f175'; +} +.zmdi-hospital-alt:before { + content: '\f176'; +} +.zmdi-hospital:before { + content: '\f177'; +} +.zmdi-hotel:before { + content: '\f178'; +} +.zmdi-hourglass-alt:before { + content: '\f179'; +} +.zmdi-hourglass-outline:before { + content: '\f17a'; +} +.zmdi-hourglass:before { + content: '\f17b'; +} +.zmdi-http:before { + content: '\f17c'; +} +.zmdi-image-alt:before { + content: '\f17d'; +} +.zmdi-image-o:before { + content: '\f17e'; +} +.zmdi-image:before { + content: '\f17f'; +} +.zmdi-inbox:before { + content: '\f180'; +} +.zmdi-invert-colors-off:before { + content: '\f181'; +} +.zmdi-invert-colors:before { + content: '\f182'; +} +.zmdi-key:before { + content: '\f183'; +} +.zmdi-label-alt-outline:before { + content: '\f184'; +} +.zmdi-label-alt:before { + content: '\f185'; +} +.zmdi-label-heart:before { + content: '\f186'; +} +.zmdi-label:before { + content: '\f187'; +} +.zmdi-labels:before { + content: '\f188'; +} +.zmdi-lamp:before { + content: '\f189'; +} +.zmdi-landscape:before { + content: '\f18a'; +} +.zmdi-layers-off:before { + content: '\f18b'; +} +.zmdi-layers:before { + content: '\f18c'; +} +.zmdi-library:before { + content: '\f18d'; +} +.zmdi-link:before { + content: '\f18e'; +} +.zmdi-lock-open:before { + content: '\f18f'; +} +.zmdi-lock-outline:before { + content: '\f190'; +} +.zmdi-lock:before { + content: '\f191'; +} +.zmdi-mail-reply-all:before { + content: '\f192'; +} +.zmdi-mail-reply:before { + content: '\f193'; +} +.zmdi-mail-send:before { + content: '\f194'; +} +.zmdi-mall:before { + content: '\f195'; +} +.zmdi-map:before { + content: '\f196'; +} +.zmdi-menu:before { + content: '\f197'; +} +.zmdi-money-box:before { + content: '\f198'; +} +.zmdi-money-off:before { + content: '\f199'; +} +.zmdi-money:before { + content: '\f19a'; +} +.zmdi-more-vert:before { + content: '\f19b'; +} +.zmdi-more:before { + content: '\f19c'; +} +.zmdi-movie-alt:before { + content: '\f19d'; +} +.zmdi-movie:before { + content: '\f19e'; +} +.zmdi-nature-people:before { + content: '\f19f'; +} +.zmdi-nature:before { + content: '\f1a0'; +} +.zmdi-navigation:before { + content: '\f1a1'; +} +.zmdi-open-in-browser:before { + content: '\f1a2'; +} +.zmdi-open-in-new:before { + content: '\f1a3'; +} +.zmdi-palette:before { + content: '\f1a4'; +} +.zmdi-parking:before { + content: '\f1a5'; +} +.zmdi-pin-account:before { + content: '\f1a6'; +} +.zmdi-pin-assistant:before { + content: '\f1a7'; +} +.zmdi-pin-drop:before { + content: '\f1a8'; +} +.zmdi-pin-help:before { + content: '\f1a9'; +} +.zmdi-pin-off:before { + content: '\f1aa'; +} +.zmdi-pin:before { + content: '\f1ab'; +} +.zmdi-pizza:before { + content: '\f1ac'; +} +.zmdi-plaster:before { + content: '\f1ad'; +} +.zmdi-power-setting:before { + content: '\f1ae'; +} +.zmdi-power:before { + content: '\f1af'; +} +.zmdi-print:before { + content: '\f1b0'; +} +.zmdi-puzzle-piece:before { + content: '\f1b1'; +} +.zmdi-quote:before { + content: '\f1b2'; +} +.zmdi-railway:before { + content: '\f1b3'; +} +.zmdi-receipt:before { + content: '\f1b4'; +} +.zmdi-refresh-alt:before { + content: '\f1b5'; +} +.zmdi-refresh-sync-alert:before { + content: '\f1b6'; +} +.zmdi-refresh-sync-off:before { + content: '\f1b7'; +} +.zmdi-refresh-sync:before { + content: '\f1b8'; +} +.zmdi-refresh:before { + content: '\f1b9'; +} +.zmdi-roller:before { + content: '\f1ba'; +} +.zmdi-ruler:before { + content: '\f1bb'; +} +.zmdi-scissors:before { + content: '\f1bc'; +} +.zmdi-screen-rotation-lock:before { + content: '\f1bd'; +} +.zmdi-screen-rotation:before { + content: '\f1be'; +} +.zmdi-search-for:before { + content: '\f1bf'; +} +.zmdi-search-in-file:before { + content: '\f1c0'; +} +.zmdi-search-in-page:before { + content: '\f1c1'; +} +.zmdi-search-replace:before { + content: '\f1c2'; +} +.zmdi-search:before { + content: '\f1c3'; +} +.zmdi-seat:before { + content: '\f1c4'; +} +.zmdi-settings-square:before { + content: '\f1c5'; +} +.zmdi-settings:before { + content: '\f1c6'; +} +.zmdi-shield-check:before { + content: '\f1c7'; +} +.zmdi-shield-security:before { + content: '\f1c8'; +} +.zmdi-shopping-basket:before { + content: '\f1c9'; +} +.zmdi-shopping-cart-plus:before { + content: '\f1ca'; +} +.zmdi-shopping-cart:before { + content: '\f1cb'; +} +.zmdi-sign-in:before { + content: '\f1cc'; +} +.zmdi-sort-amount-asc:before { + content: '\f1cd'; +} +.zmdi-sort-amount-desc:before { + content: '\f1ce'; +} +.zmdi-sort-asc:before { + content: '\f1cf'; +} +.zmdi-sort-desc:before { + content: '\f1d0'; +} +.zmdi-spellcheck:before { + content: '\f1d1'; +} +.zmdi-storage:before { + content: '\f1d2'; +} +.zmdi-store-24:before { + content: '\f1d3'; +} +.zmdi-store:before { + content: '\f1d4'; +} +.zmdi-subway:before { + content: '\f1d5'; +} +.zmdi-sun:before { + content: '\f1d6'; +} +.zmdi-tab-unselected:before { + content: '\f1d7'; +} +.zmdi-tab:before { + content: '\f1d8'; +} +.zmdi-tag-close:before { + content: '\f1d9'; +} +.zmdi-tag-more:before { + content: '\f1da'; +} +.zmdi-tag:before { + content: '\f1db'; +} +.zmdi-thumb-down:before { + content: '\f1dc'; +} +.zmdi-thumb-up-down:before { + content: '\f1dd'; +} +.zmdi-thumb-up:before { + content: '\f1de'; +} +.zmdi-ticket-star:before { + content: '\f1df'; +} +.zmdi-toll:before { + content: '\f1e0'; +} +.zmdi-toys:before { + content: '\f1e1'; +} +.zmdi-traffic:before { + content: '\f1e2'; +} +.zmdi-translate:before { + content: '\f1e3'; +} +.zmdi-triangle-down:before { + content: '\f1e4'; +} +.zmdi-triangle-up:before { + content: '\f1e5'; +} +.zmdi-truck:before { + content: '\f1e6'; +} +.zmdi-turning-sign:before { + content: '\f1e7'; +} +.zmdi-wallpaper:before { + content: '\f1e8'; +} +.zmdi-washing-machine:before { + content: '\f1e9'; +} +.zmdi-window-maximize:before { + content: '\f1ea'; +} +.zmdi-window-minimize:before { + content: '\f1eb'; +} +.zmdi-window-restore:before { + content: '\f1ec'; +} +.zmdi-wrench:before { + content: '\f1ed'; +} +.zmdi-zoom-in:before { + content: '\f1ee'; +} +.zmdi-zoom-out:before { + content: '\f1ef'; +} +.zmdi-alert-circle-o:before { + content: '\f1f0'; +} +.zmdi-alert-circle:before { + content: '\f1f1'; +} +.zmdi-alert-octagon:before { + content: '\f1f2'; +} +.zmdi-alert-polygon:before { + content: '\f1f3'; +} +.zmdi-alert-triangle:before { + content: '\f1f4'; +} +.zmdi-help-outline:before { + content: '\f1f5'; +} +.zmdi-help:before { + content: '\f1f6'; +} +.zmdi-info-outline:before { + content: '\f1f7'; +} +.zmdi-info:before { + content: '\f1f8'; +} +.zmdi-notifications-active:before { + content: '\f1f9'; +} +.zmdi-notifications-add:before { + content: '\f1fa'; +} +.zmdi-notifications-none:before { + content: '\f1fb'; +} +.zmdi-notifications-off:before { + content: '\f1fc'; +} +.zmdi-notifications-paused:before { + content: '\f1fd'; +} +.zmdi-notifications:before { + content: '\f1fe'; +} +.zmdi-account-add:before { + content: '\f1ff'; +} +.zmdi-account-box-mail:before { + content: '\f200'; +} +.zmdi-account-box-o:before { + content: '\f201'; +} +.zmdi-account-box-phone:before { + content: '\f202'; +} +.zmdi-account-box:before { + content: '\f203'; +} +.zmdi-account-calendar:before { + content: '\f204'; +} +.zmdi-account-circle:before { + content: '\f205'; +} +.zmdi-account-o:before { + content: '\f206'; +} +.zmdi-account:before { + content: '\f207'; +} +.zmdi-accounts-add:before { + content: '\f208'; +} +.zmdi-accounts-alt:before { + content: '\f209'; +} +.zmdi-accounts-list-alt:before { + content: '\f20a'; +} +.zmdi-accounts-list:before { + content: '\f20b'; +} +.zmdi-accounts-outline:before { + content: '\f20c'; +} +.zmdi-accounts:before { + content: '\f20d'; +} +.zmdi-face:before { + content: '\f20e'; +} +.zmdi-female:before { + content: '\f20f'; +} +.zmdi-male-alt:before { + content: '\f210'; +} +.zmdi-male-female:before { + content: '\f211'; +} +.zmdi-male:before { + content: '\f212'; +} +.zmdi-mood-bad:before { + content: '\f213'; +} +.zmdi-mood:before { + content: '\f214'; +} +.zmdi-run:before { + content: '\f215'; +} +.zmdi-walk:before { + content: '\f216'; +} +.zmdi-cloud-box:before { + content: '\f217'; +} +.zmdi-cloud-circle:before { + content: '\f218'; +} +.zmdi-cloud-done:before { + content: '\f219'; +} +.zmdi-cloud-download:before { + content: '\f21a'; +} +.zmdi-cloud-off:before { + content: '\f21b'; +} +.zmdi-cloud-outline-alt:before { + content: '\f21c'; +} +.zmdi-cloud-outline:before { + content: '\f21d'; +} +.zmdi-cloud-upload:before { + content: '\f21e'; +} +.zmdi-cloud:before { + content: '\f21f'; +} +.zmdi-download:before { + content: '\f220'; +} +.zmdi-file-plus:before { + content: '\f221'; +} +.zmdi-file-text:before { + content: '\f222'; +} +.zmdi-file:before { + content: '\f223'; +} +.zmdi-folder-outline:before { + content: '\f224'; +} +.zmdi-folder-person:before { + content: '\f225'; +} +.zmdi-folder-star-alt:before { + content: '\f226'; +} +.zmdi-folder-star:before { + content: '\f227'; +} +.zmdi-folder:before { + content: '\f228'; +} +.zmdi-gif:before { + content: '\f229'; +} +.zmdi-upload:before { + content: '\f22a'; +} +.zmdi-border-all:before { + content: '\f22b'; +} +.zmdi-border-bottom:before { + content: '\f22c'; +} +.zmdi-border-clear:before { + content: '\f22d'; +} +.zmdi-border-color:before { + content: '\f22e'; +} +.zmdi-border-horizontal:before { + content: '\f22f'; +} +.zmdi-border-inner:before { + content: '\f230'; +} +.zmdi-border-left:before { + content: '\f231'; +} +.zmdi-border-outer:before { + content: '\f232'; +} +.zmdi-border-right:before { + content: '\f233'; +} +.zmdi-border-style:before { + content: '\f234'; +} +.zmdi-border-top:before { + content: '\f235'; +} +.zmdi-border-vertical:before { + content: '\f236'; +} +.zmdi-copy:before { + content: '\f237'; +} +.zmdi-crop:before { + content: '\f238'; +} +.zmdi-format-align-center:before { + content: '\f239'; +} +.zmdi-format-align-justify:before { + content: '\f23a'; +} +.zmdi-format-align-left:before { + content: '\f23b'; +} +.zmdi-format-align-right:before { + content: '\f23c'; +} +.zmdi-format-bold:before { + content: '\f23d'; +} +.zmdi-format-clear-all:before { + content: '\f23e'; +} +.zmdi-format-clear:before { + content: '\f23f'; +} +.zmdi-format-color-fill:before { + content: '\f240'; +} +.zmdi-format-color-reset:before { + content: '\f241'; +} +.zmdi-format-color-text:before { + content: '\f242'; +} +.zmdi-format-indent-decrease:before { + content: '\f243'; +} +.zmdi-format-indent-increase:before { + content: '\f244'; +} +.zmdi-format-italic:before { + content: '\f245'; +} +.zmdi-format-line-spacing:before { + content: '\f246'; +} +.zmdi-format-list-bulleted:before { + content: '\f247'; +} +.zmdi-format-list-numbered:before { + content: '\f248'; +} +.zmdi-format-ltr:before { + content: '\f249'; +} +.zmdi-format-rtl:before { + content: '\f24a'; +} +.zmdi-format-size:before { + content: '\f24b'; +} +.zmdi-format-strikethrough-s:before { + content: '\f24c'; +} +.zmdi-format-strikethrough:before { + content: '\f24d'; +} +.zmdi-format-subject:before { + content: '\f24e'; +} +.zmdi-format-underlined:before { + content: '\f24f'; +} +.zmdi-format-valign-bottom:before { + content: '\f250'; +} +.zmdi-format-valign-center:before { + content: '\f251'; +} +.zmdi-format-valign-top:before { + content: '\f252'; +} +.zmdi-redo:before { + content: '\f253'; +} +.zmdi-select-all:before { + content: '\f254'; +} +.zmdi-space-bar:before { + content: '\f255'; +} +.zmdi-text-format:before { + content: '\f256'; +} +.zmdi-transform:before { + content: '\f257'; +} +.zmdi-undo:before { + content: '\f258'; +} +.zmdi-wrap-text:before { + content: '\f259'; +} +.zmdi-comment-alert:before { + content: '\f25a'; +} +.zmdi-comment-alt-text:before { + content: '\f25b'; +} +.zmdi-comment-alt:before { + content: '\f25c'; +} +.zmdi-comment-edit:before { + content: '\f25d'; +} +.zmdi-comment-image:before { + content: '\f25e'; +} +.zmdi-comment-list:before { + content: '\f25f'; +} +.zmdi-comment-more:before { + content: '\f260'; +} +.zmdi-comment-outline:before { + content: '\f261'; +} +.zmdi-comment-text-alt:before { + content: '\f262'; +} +.zmdi-comment-text:before { + content: '\f263'; +} +.zmdi-comment-video:before { + content: '\f264'; +} +.zmdi-comment:before { + content: '\f265'; +} +.zmdi-comments:before { + content: '\f266'; +} +.zmdi-check-all:before { + content: '\f267'; +} +.zmdi-check-circle-u:before { + content: '\f268'; +} +.zmdi-check-circle:before { + content: '\f269'; +} +.zmdi-check-square:before { + content: '\f26a'; +} +.zmdi-check:before { + content: '\f26b'; +} +.zmdi-circle-o:before { + content: '\f26c'; +} +.zmdi-circle:before { + content: '\f26d'; +} +.zmdi-dot-circle-alt:before { + content: '\f26e'; +} +.zmdi-dot-circle:before { + content: '\f26f'; +} +.zmdi-minus-circle-outline:before { + content: '\f270'; +} +.zmdi-minus-circle:before { + content: '\f271'; +} +.zmdi-minus-square:before { + content: '\f272'; +} +.zmdi-minus:before { + content: '\f273'; +} +.zmdi-plus-circle-o-duplicate:before { + content: '\f274'; +} +.zmdi-plus-circle-o:before { + content: '\f275'; +} +.zmdi-plus-circle:before { + content: '\f276'; +} +.zmdi-plus-square:before { + content: '\f277'; +} +.zmdi-plus:before { + content: '\f278'; +} +.zmdi-square-o:before { + content: '\f279'; +} +.zmdi-star-circle:before { + content: '\f27a'; +} +.zmdi-star-half:before { + content: '\f27b'; +} +.zmdi-star-outline:before { + content: '\f27c'; +} +.zmdi-star:before { + content: '\f27d'; +} +.zmdi-bluetooth-connected:before { + content: '\f27e'; +} +.zmdi-bluetooth-off:before { + content: '\f27f'; +} +.zmdi-bluetooth-search:before { + content: '\f280'; +} +.zmdi-bluetooth-setting:before { + content: '\f281'; +} +.zmdi-bluetooth:before { + content: '\f282'; +} +.zmdi-camera-add:before { + content: '\f283'; +} +.zmdi-camera-alt:before { + content: '\f284'; +} +.zmdi-camera-bw:before { + content: '\f285'; +} +.zmdi-camera-front:before { + content: '\f286'; +} +.zmdi-camera-mic:before { + content: '\f287'; +} +.zmdi-camera-party-mode:before { + content: '\f288'; +} +.zmdi-camera-rear:before { + content: '\f289'; +} +.zmdi-camera-roll:before { + content: '\f28a'; +} +.zmdi-camera-switch:before { + content: '\f28b'; +} +.zmdi-camera:before { + content: '\f28c'; +} +.zmdi-card-alert:before { + content: '\f28d'; +} +.zmdi-card-off:before { + content: '\f28e'; +} +.zmdi-card-sd:before { + content: '\f28f'; +} +.zmdi-card-sim:before { + content: '\f290'; +} +.zmdi-desktop-mac:before { + content: '\f291'; +} +.zmdi-desktop-windows:before { + content: '\f292'; +} +.zmdi-device-hub:before { + content: '\f293'; +} +.zmdi-devices-off:before { + content: '\f294'; +} +.zmdi-devices:before { + content: '\f295'; +} +.zmdi-dock:before { + content: '\f296'; +} +.zmdi-floppy:before { + content: '\f297'; +} +.zmdi-gamepad:before { + content: '\f298'; +} +.zmdi-gps-dot:before { + content: '\f299'; +} +.zmdi-gps-off:before { + content: '\f29a'; +} +.zmdi-gps:before { + content: '\f29b'; +} +.zmdi-headset-mic:before { + content: '\f29c'; +} +.zmdi-headset:before { + content: '\f29d'; +} +.zmdi-input-antenna:before { + content: '\f29e'; +} +.zmdi-input-composite:before { + content: '\f29f'; +} +.zmdi-input-hdmi:before { + content: '\f2a0'; +} +.zmdi-input-power:before { + content: '\f2a1'; +} +.zmdi-input-svideo:before { + content: '\f2a2'; +} +.zmdi-keyboard-hide:before { + content: '\f2a3'; +} +.zmdi-keyboard:before { + content: '\f2a4'; +} +.zmdi-laptop-chromebook:before { + content: '\f2a5'; +} +.zmdi-laptop-mac:before { + content: '\f2a6'; +} +.zmdi-laptop:before { + content: '\f2a7'; +} +.zmdi-mic-off:before { + content: '\f2a8'; +} +.zmdi-mic-outline:before { + content: '\f2a9'; +} +.zmdi-mic-setting:before { + content: '\f2aa'; +} +.zmdi-mic:before { + content: '\f2ab'; +} +.zmdi-mouse:before { + content: '\f2ac'; +} +.zmdi-network-alert:before { + content: '\f2ad'; +} +.zmdi-network-locked:before { + content: '\f2ae'; +} +.zmdi-network-off:before { + content: '\f2af'; +} +.zmdi-network-outline:before { + content: '\f2b0'; +} +.zmdi-network-setting:before { + content: '\f2b1'; +} +.zmdi-network:before { + content: '\f2b2'; +} +.zmdi-phone-bluetooth:before { + content: '\f2b3'; +} +.zmdi-phone-end:before { + content: '\f2b4'; +} +.zmdi-phone-forwarded:before { + content: '\f2b5'; +} +.zmdi-phone-in-talk:before { + content: '\f2b6'; +} +.zmdi-phone-locked:before { + content: '\f2b7'; +} +.zmdi-phone-missed:before { + content: '\f2b8'; +} +.zmdi-phone-msg:before { + content: '\f2b9'; +} +.zmdi-phone-paused:before { + content: '\f2ba'; +} +.zmdi-phone-ring:before { + content: '\f2bb'; +} +.zmdi-phone-setting:before { + content: '\f2bc'; +} +.zmdi-phone-sip:before { + content: '\f2bd'; +} +.zmdi-phone:before { + content: '\f2be'; +} +.zmdi-portable-wifi-changes:before { + content: '\f2bf'; +} +.zmdi-portable-wifi-off:before { + content: '\f2c0'; +} +.zmdi-portable-wifi:before { + content: '\f2c1'; +} +.zmdi-radio:before { + content: '\f2c2'; +} +.zmdi-reader:before { + content: '\f2c3'; +} +.zmdi-remote-control-alt:before { + content: '\f2c4'; +} +.zmdi-remote-control:before { + content: '\f2c5'; +} +.zmdi-router:before { + content: '\f2c6'; +} +.zmdi-scanner:before { + content: '\f2c7'; +} +.zmdi-smartphone-android:before { + content: '\f2c8'; +} +.zmdi-smartphone-download:before { + content: '\f2c9'; +} +.zmdi-smartphone-erase:before { + content: '\f2ca'; +} +.zmdi-smartphone-info:before { + content: '\f2cb'; +} +.zmdi-smartphone-iphone:before { + content: '\f2cc'; +} +.zmdi-smartphone-landscape-lock:before { + content: '\f2cd'; +} +.zmdi-smartphone-landscape:before { + content: '\f2ce'; +} +.zmdi-smartphone-lock:before { + content: '\f2cf'; +} +.zmdi-smartphone-portrait-lock:before { + content: '\f2d0'; +} +.zmdi-smartphone-ring:before { + content: '\f2d1'; +} +.zmdi-smartphone-setting:before { + content: '\f2d2'; +} +.zmdi-smartphone-setup:before { + content: '\f2d3'; +} +.zmdi-smartphone:before { + content: '\f2d4'; +} +.zmdi-speaker:before { + content: '\f2d5'; +} +.zmdi-tablet-android:before { + content: '\f2d6'; +} +.zmdi-tablet-mac:before { + content: '\f2d7'; +} +.zmdi-tablet:before { + content: '\f2d8'; +} +.zmdi-tv-alt-play:before { + content: '\f2d9'; +} +.zmdi-tv-list:before { + content: '\f2da'; +} +.zmdi-tv-play:before { + content: '\f2db'; +} +.zmdi-tv:before { + content: '\f2dc'; +} +.zmdi-usb:before { + content: '\f2dd'; +} +.zmdi-videocam-off:before { + content: '\f2de'; +} +.zmdi-videocam-switch:before { + content: '\f2df'; +} +.zmdi-videocam:before { + content: '\f2e0'; +} +.zmdi-watch:before { + content: '\f2e1'; +} +.zmdi-wifi-alt-2:before { + content: '\f2e2'; +} +.zmdi-wifi-alt:before { + content: '\f2e3'; +} +.zmdi-wifi-info:before { + content: '\f2e4'; +} +.zmdi-wifi-lock:before { + content: '\f2e5'; +} +.zmdi-wifi-off:before { + content: '\f2e6'; +} +.zmdi-wifi-outline:before { + content: '\f2e7'; +} +.zmdi-wifi:before { + content: '\f2e8'; +} +.zmdi-arrow-left-bottom:before { + content: '\f2e9'; +} +.zmdi-arrow-left:before { + content: '\f2ea'; +} +.zmdi-arrow-merge:before { + content: '\f2eb'; +} +.zmdi-arrow-missed:before { + content: '\f2ec'; +} +.zmdi-arrow-right-top:before { + content: '\f2ed'; +} +.zmdi-arrow-right:before { + content: '\f2ee'; +} +.zmdi-arrow-split:before { + content: '\f2ef'; +} +.zmdi-arrows:before { + content: '\f2f0'; +} +.zmdi-caret-down-circle:before { + content: '\f2f1'; +} +.zmdi-caret-down:before { + content: '\f2f2'; +} +.zmdi-caret-left-circle:before { + content: '\f2f3'; +} +.zmdi-caret-left:before { + content: '\f2f4'; +} +.zmdi-caret-right-circle:before { + content: '\f2f5'; +} +.zmdi-caret-right:before { + content: '\f2f6'; +} +.zmdi-caret-up-circle:before { + content: '\f2f7'; +} +.zmdi-caret-up:before { + content: '\f2f8'; +} +.zmdi-chevron-down:before { + content: '\f2f9'; +} +.zmdi-chevron-left:before { + content: '\f2fa'; +} +.zmdi-chevron-right:before { + content: '\f2fb'; +} +.zmdi-chevron-up:before { + content: '\f2fc'; +} +.zmdi-forward:before { + content: '\f2fd'; +} +.zmdi-long-arrow-down:before { + content: '\f2fe'; +} +.zmdi-long-arrow-left:before { + content: '\f2ff'; +} +.zmdi-long-arrow-return:before { + content: '\f300'; +} +.zmdi-long-arrow-right:before { + content: '\f301'; +} +.zmdi-long-arrow-tab:before { + content: '\f302'; +} +.zmdi-long-arrow-up:before { + content: '\f303'; +} +.zmdi-rotate-ccw:before { + content: '\f304'; +} +.zmdi-rotate-cw:before { + content: '\f305'; +} +.zmdi-rotate-left:before { + content: '\f306'; +} +.zmdi-rotate-right:before { + content: '\f307'; +} +.zmdi-square-down:before { + content: '\f308'; +} +.zmdi-square-right:before { + content: '\f309'; +} +.zmdi-swap-alt:before { + content: '\f30a'; +} +.zmdi-swap-vertical-circle:before { + content: '\f30b'; +} +.zmdi-swap-vertical:before { + content: '\f30c'; +} +.zmdi-swap:before { + content: '\f30d'; +} +.zmdi-trending-down:before { + content: '\f30e'; +} +.zmdi-trending-flat:before { + content: '\f30f'; +} +.zmdi-trending-up:before { + content: '\f310'; +} +.zmdi-unfold-less:before { + content: '\f311'; +} +.zmdi-unfold-more:before { + content: '\f312'; +} +.zmdi-apps:before { + content: '\f313'; +} +.zmdi-grid-off:before { + content: '\f314'; +} +.zmdi-grid:before { + content: '\f315'; +} +.zmdi-view-agenda:before { + content: '\f316'; +} +.zmdi-view-array:before { + content: '\f317'; +} +.zmdi-view-carousel:before { + content: '\f318'; +} +.zmdi-view-column:before { + content: '\f319'; +} +.zmdi-view-comfy:before { + content: '\f31a'; +} +.zmdi-view-compact:before { + content: '\f31b'; +} +.zmdi-view-dashboard:before { + content: '\f31c'; +} +.zmdi-view-day:before { + content: '\f31d'; +} +.zmdi-view-headline:before { + content: '\f31e'; +} +.zmdi-view-list-alt:before { + content: '\f31f'; +} +.zmdi-view-list:before { + content: '\f320'; +} +.zmdi-view-module:before { + content: '\f321'; +} +.zmdi-view-quilt:before { + content: '\f322'; +} +.zmdi-view-stream:before { + content: '\f323'; +} +.zmdi-view-subtitles:before { + content: '\f324'; +} +.zmdi-view-toc:before { + content: '\f325'; +} +.zmdi-view-web:before { + content: '\f326'; +} +.zmdi-view-week:before { + content: '\f327'; +} +.zmdi-widgets:before { + content: '\f328'; +} +.zmdi-alarm-check:before { + content: '\f329'; +} +.zmdi-alarm-off:before { + content: '\f32a'; +} +.zmdi-alarm-plus:before { + content: '\f32b'; +} +.zmdi-alarm-snooze:before { + content: '\f32c'; +} +.zmdi-alarm:before { + content: '\f32d'; +} +.zmdi-calendar-alt:before { + content: '\f32e'; +} +.zmdi-calendar-check:before { + content: '\f32f'; +} +.zmdi-calendar-close:before { + content: '\f330'; +} +.zmdi-calendar-note:before { + content: '\f331'; +} +.zmdi-calendar:before { + content: '\f332'; +} +.zmdi-time-countdown:before { + content: '\f333'; +} +.zmdi-time-interval:before { + content: '\f334'; +} +.zmdi-time-restore-setting:before { + content: '\f335'; +} +.zmdi-time-restore:before { + content: '\f336'; +} +.zmdi-time:before { + content: '\f337'; +} +.zmdi-timer-off:before { + content: '\f338'; +} +.zmdi-timer:before { + content: '\f339'; +} +.zmdi-android-alt:before { + content: '\f33a'; +} +.zmdi-android:before { + content: '\f33b'; +} +.zmdi-apple:before { + content: '\f33c'; +} +.zmdi-behance:before { + content: '\f33d'; +} +.zmdi-codepen:before { + content: '\f33e'; +} +.zmdi-dribbble:before { + content: '\f33f'; +} +.zmdi-dropbox:before { + content: '\f340'; +} +.zmdi-evernote:before { + content: '\f341'; +} +.zmdi-facebook-box:before { + content: '\f342'; +} +.zmdi-facebook:before { + content: '\f343'; +} +.zmdi-github-box:before { + content: '\f344'; +} +.zmdi-github:before { + content: '\f345'; +} +.zmdi-google-drive:before { + content: '\f346'; +} +.zmdi-google-earth:before { + content: '\f347'; +} +.zmdi-google-glass:before { + content: '\f348'; +} +.zmdi-google-maps:before { + content: '\f349'; +} +.zmdi-google-pages:before { + content: '\f34a'; +} +.zmdi-google-play:before { + content: '\f34b'; +} +.zmdi-google-plus-box:before { + content: '\f34c'; +} +.zmdi-google-plus:before { + content: '\f34d'; +} +.zmdi-google:before { + content: '\f34e'; +} +.zmdi-instagram:before { + content: '\f34f'; +} +.zmdi-language-css3:before { + content: '\f350'; +} +.zmdi-language-html5:before { + content: '\f351'; +} +.zmdi-language-javascript:before { + content: '\f352'; +} +.zmdi-language-python-alt:before { + content: '\f353'; +} +.zmdi-language-python:before { + content: '\f354'; +} +.zmdi-lastfm:before { + content: '\f355'; +} +.zmdi-linkedin-box:before { + content: '\f356'; +} +.zmdi-paypal:before { + content: '\f357'; +} +.zmdi-pinterest-box:before { + content: '\f358'; +} +.zmdi-pocket:before { + content: '\f359'; +} +.zmdi-polymer:before { + content: '\f35a'; +} +.zmdi-share:before { + content: '\f35b'; +} +.zmdi-stackoverflow:before { + content: '\f35c'; +} +.zmdi-steam-square:before { + content: '\f35d'; +} +.zmdi-steam:before { + content: '\f35e'; +} +.zmdi-twitter-box:before { + content: '\f35f'; +} +.zmdi-twitter:before { + content: '\f360'; +} +.zmdi-vk:before { + content: '\f361'; +} +.zmdi-wikipedia:before { + content: '\f362'; +} +.zmdi-windows:before { + content: '\f363'; +} +.zmdi-aspect-ratio-alt:before { + content: '\f364'; +} +.zmdi-aspect-ratio:before { + content: '\f365'; +} +.zmdi-blur-circular:before { + content: '\f366'; +} +.zmdi-blur-linear:before { + content: '\f367'; +} +.zmdi-blur-off:before { + content: '\f368'; +} +.zmdi-blur:before { + content: '\f369'; +} +.zmdi-brightness-2:before { + content: '\f36a'; +} +.zmdi-brightness-3:before { + content: '\f36b'; +} +.zmdi-brightness-4:before { + content: '\f36c'; +} +.zmdi-brightness-5:before { + content: '\f36d'; +} +.zmdi-brightness-6:before { + content: '\f36e'; +} +.zmdi-brightness-7:before { + content: '\f36f'; +} +.zmdi-brightness-auto:before { + content: '\f370'; +} +.zmdi-brightness-setting:before { + content: '\f371'; +} +.zmdi-broken-image:before { + content: '\f372'; +} +.zmdi-center-focus-strong:before { + content: '\f373'; +} +.zmdi-center-focus-weak:before { + content: '\f374'; +} +.zmdi-compare:before { + content: '\f375'; +} +.zmdi-crop-16-9:before { + content: '\f376'; +} +.zmdi-crop-3-2:before { + content: '\f377'; +} +.zmdi-crop-5-4:before { + content: '\f378'; +} +.zmdi-crop-7-5:before { + content: '\f379'; +} +.zmdi-crop-din:before { + content: '\f37a'; +} +.zmdi-crop-free:before { + content: '\f37b'; +} +.zmdi-crop-landscape:before { + content: '\f37c'; +} +.zmdi-crop-portrait:before { + content: '\f37d'; +} +.zmdi-crop-square:before { + content: '\f37e'; +} +.zmdi-exposure-alt:before { + content: '\f37f'; +} +.zmdi-exposure:before { + content: '\f380'; +} +.zmdi-filter-b-and-w:before { + content: '\f381'; +} +.zmdi-filter-center-focus:before { + content: '\f382'; +} +.zmdi-filter-frames:before { + content: '\f383'; +} +.zmdi-filter-tilt-shift:before { + content: '\f384'; +} +.zmdi-gradient:before { + content: '\f385'; +} +.zmdi-grain:before { + content: '\f386'; +} +.zmdi-graphic-eq:before { + content: '\f387'; +} +.zmdi-hdr-off:before { + content: '\f388'; +} +.zmdi-hdr-strong:before { + content: '\f389'; +} +.zmdi-hdr-weak:before { + content: '\f38a'; +} +.zmdi-hdr:before { + content: '\f38b'; +} +.zmdi-iridescent:before { + content: '\f38c'; +} +.zmdi-leak-off:before { + content: '\f38d'; +} +.zmdi-leak:before { + content: '\f38e'; +} +.zmdi-looks:before { + content: '\f38f'; +} +.zmdi-loupe:before { + content: '\f390'; +} +.zmdi-panorama-horizontal:before { + content: '\f391'; +} +.zmdi-panorama-vertical:before { + content: '\f392'; +} +.zmdi-panorama-wide-angle:before { + content: '\f393'; +} +.zmdi-photo-size-select-large:before { + content: '\f394'; +} +.zmdi-photo-size-select-small:before { + content: '\f395'; +} +.zmdi-picture-in-picture:before { + content: '\f396'; +} +.zmdi-slideshow:before { + content: '\f397'; +} +.zmdi-texture:before { + content: '\f398'; +} +.zmdi-tonality:before { + content: '\f399'; +} +.zmdi-vignette:before { + content: '\f39a'; +} +.zmdi-wb-auto:before { + content: '\f39b'; +} +.zmdi-eject-alt:before { + content: '\f39c'; +} +.zmdi-eject:before { + content: '\f39d'; +} +.zmdi-equalizer:before { + content: '\f39e'; +} +.zmdi-fast-forward:before { + content: '\f39f'; +} +.zmdi-fast-rewind:before { + content: '\f3a0'; +} +.zmdi-forward-10:before { + content: '\f3a1'; +} +.zmdi-forward-30:before { + content: '\f3a2'; +} +.zmdi-forward-5:before { + content: '\f3a3'; +} +.zmdi-hearing:before { + content: '\f3a4'; +} +.zmdi-pause-circle-outline:before { + content: '\f3a5'; +} +.zmdi-pause-circle:before { + content: '\f3a6'; +} +.zmdi-pause:before { + content: '\f3a7'; +} +.zmdi-play-circle-outline:before { + content: '\f3a8'; +} +.zmdi-play-circle:before { + content: '\f3a9'; +} +.zmdi-play:before { + content: '\f3aa'; +} +.zmdi-playlist-audio:before { + content: '\f3ab'; +} +.zmdi-playlist-plus:before { + content: '\f3ac'; +} +.zmdi-repeat-one:before { + content: '\f3ad'; +} +.zmdi-repeat:before { + content: '\f3ae'; +} +.zmdi-replay-10:before { + content: '\f3af'; +} +.zmdi-replay-30:before { + content: '\f3b0'; +} +.zmdi-replay-5:before { + content: '\f3b1'; +} +.zmdi-replay:before { + content: '\f3b2'; +} +.zmdi-shuffle:before { + content: '\f3b3'; +} +.zmdi-skip-next:before { + content: '\f3b4'; +} +.zmdi-skip-previous:before { + content: '\f3b5'; +} +.zmdi-stop:before { + content: '\f3b6'; +} +.zmdi-surround-sound:before { + content: '\f3b7'; +} +.zmdi-tune:before { + content: '\f3b8'; +} +.zmdi-volume-down:before { + content: '\f3b9'; +} +.zmdi-volume-mute:before { + content: '\f3ba'; +} +.zmdi-volume-off:before { + content: '\f3bb'; +} +.zmdi-volume-up:before { + content: '\f3bc'; +} +.zmdi-n-1-square:before { + content: '\f3bd'; +} +.zmdi-n-2-square:before { + content: '\f3be'; +} +.zmdi-n-3-square:before { + content: '\f3bf'; +} +.zmdi-n-4-square:before { + content: '\f3c0'; +} +.zmdi-n-5-square:before { + content: '\f3c1'; +} +.zmdi-n-6-square:before { + content: '\f3c2'; +} +.zmdi-neg-1:before { + content: '\f3c3'; +} +.zmdi-neg-2:before { + content: '\f3c4'; +} +.zmdi-plus-1:before { + content: '\f3c5'; +} +.zmdi-plus-2:before { + content: '\f3c6'; +} +.zmdi-sec-10:before { + content: '\f3c7'; +} +.zmdi-sec-3:before { + content: '\f3c8'; +} +.zmdi-zero:before { + content: '\f3c9'; +} +.zmdi-airline-seat-flat-angled:before { + content: '\f3ca'; +} +.zmdi-airline-seat-flat:before { + content: '\f3cb'; +} +.zmdi-airline-seat-individual-suite:before { + content: '\f3cc'; +} +.zmdi-airline-seat-legroom-extra:before { + content: '\f3cd'; +} +.zmdi-airline-seat-legroom-normal:before { + content: '\f3ce'; +} +.zmdi-airline-seat-legroom-reduced:before { + content: '\f3cf'; +} +.zmdi-airline-seat-recline-extra:before { + content: '\f3d0'; +} +.zmdi-airline-seat-recline-normal:before { + content: '\f3d1'; +} +.zmdi-airplay:before { + content: '\f3d2'; +} +.zmdi-closed-caption:before { + content: '\f3d3'; +} +.zmdi-confirmation-number:before { + content: '\f3d4'; +} +.zmdi-developer-board:before { + content: '\f3d5'; +} +.zmdi-disc-full:before { + content: '\f3d6'; +} +.zmdi-explicit:before { + content: '\f3d7'; +} +.zmdi-flight-land:before { + content: '\f3d8'; +} +.zmdi-flight-takeoff:before { + content: '\f3d9'; +} +.zmdi-flip-to-back:before { + content: '\f3da'; +} +.zmdi-flip-to-front:before { + content: '\f3db'; +} +.zmdi-group-work:before { + content: '\f3dc'; +} +.zmdi-hd:before { + content: '\f3dd'; +} +.zmdi-hq:before { + content: '\f3de'; +} +.zmdi-markunread-mailbox:before { + content: '\f3df'; +} +.zmdi-memory:before { + content: '\f3e0'; +} +.zmdi-nfc:before { + content: '\f3e1'; +} +.zmdi-play-for-work:before { + content: '\f3e2'; +} +.zmdi-power-input:before { + content: '\f3e3'; +} +.zmdi-present-to-all:before { + content: '\f3e4'; +} +.zmdi-satellite:before { + content: '\f3e5'; +} +.zmdi-tap-and-play:before { + content: '\f3e6'; +} +.zmdi-vibration:before { + content: '\f3e7'; +} +.zmdi-voicemail:before { + content: '\f3e8'; +} +.zmdi-group:before { + content: '\f3e9'; +} +.zmdi-rss:before { + content: '\f3ea'; +} +.zmdi-shape:before { + content: '\f3eb'; +} +.zmdi-spinner:before { + content: '\f3ec'; +} +.zmdi-ungroup:before { + content: '\f3ed'; +} +.zmdi-500px:before { + content: '\f3ee'; +} +.zmdi-8tracks:before { + content: '\f3ef'; +} +.zmdi-amazon:before { + content: '\f3f0'; +} +.zmdi-blogger:before { + content: '\f3f1'; +} +.zmdi-delicious:before { + content: '\f3f2'; +} +.zmdi-disqus:before { + content: '\f3f3'; +} +.zmdi-flattr:before { + content: '\f3f4'; +} +.zmdi-flickr:before { + content: '\f3f5'; +} +.zmdi-github-alt:before { + content: '\f3f6'; +} +.zmdi-google-old:before { + content: '\f3f7'; +} +.zmdi-linkedin:before { + content: '\f3f8'; +} +.zmdi-odnoklassniki:before { + content: '\f3f9'; +} +.zmdi-outlook:before { + content: '\f3fa'; +} +.zmdi-paypal-alt:before { + content: '\f3fb'; +} +.zmdi-pinterest:before { + content: '\f3fc'; +} +.zmdi-playstation:before { + content: '\f3fd'; +} +.zmdi-reddit:before { + content: '\f3fe'; +} +.zmdi-skype:before { + content: '\f3ff'; +} +.zmdi-slideshare:before { + content: '\f400'; +} +.zmdi-soundcloud:before { + content: '\f401'; +} +.zmdi-tumblr:before { + content: '\f402'; +} +.zmdi-twitch:before { + content: '\f403'; +} +.zmdi-vimeo:before { + content: '\f404'; +} +.zmdi-whatsapp:before { + content: '\f405'; +} +.zmdi-xbox:before { + content: '\f406'; +} +.zmdi-yahoo:before { + content: '\f407'; +} +.zmdi-youtube-play:before { + content: '\f408'; +} +.zmdi-youtube:before { + content: '\f409'; +} +.zmdi-import-export:before { + content: '\f30c'; +} +.zmdi-swap-vertical-:before { + content: '\f30c'; +} +.zmdi-airplanemode-inactive:before { + content: '\f102'; +} +.zmdi-airplanemode-active:before { + content: '\f103'; +} +.zmdi-rate-review:before { + content: '\f103'; +} +.zmdi-comment-sign:before { + content: '\f25a'; +} +.zmdi-network-warning:before { + content: '\f2ad'; +} +.zmdi-shopping-cart-add:before { + content: '\f1ca'; +} +.zmdi-file-add:before { + content: '\f221'; +} +.zmdi-network-wifi-scan:before { + content: '\f2e4'; +} +.zmdi-collection-add:before { + content: '\f14e'; +} +.zmdi-format-playlist-add:before { + content: '\f3ac'; +} +.zmdi-format-queue-music:before { + content: '\f3ab'; +} +.zmdi-plus-box:before { + content: '\f277'; +} +.zmdi-tag-backspace:before { + content: '\f1d9'; +} +.zmdi-alarm-add:before { + content: '\f32b'; +} +.zmdi-battery-charging:before { + content: '\f114'; +} +.zmdi-daydream-setting:before { + content: '\f217'; +} +.zmdi-more-horiz:before { + content: '\f19c'; +} +.zmdi-book-photo:before { + content: '\f11b'; +} +.zmdi-incandescent:before { + content: '\f189'; +} +.zmdi-wb-iridescent:before { + content: '\f38c'; +} +.zmdi-calendar-remove:before { + content: '\f330'; +} +.zmdi-refresh-sync-disabled:before { + content: '\f1b7'; +} +.zmdi-refresh-sync-problem:before { + content: '\f1b6'; +} +.zmdi-crop-original:before { + content: '\f17e'; +} +.zmdi-power-off:before { + content: '\f1af'; +} +.zmdi-power-off-setting:before { + content: '\f1ae'; +} +.zmdi-leak-remove:before { + content: '\f38d'; +} +.zmdi-star-border:before { + content: '\f27c'; +} +.zmdi-brightness-low:before { + content: '\f36d'; +} +.zmdi-brightness-medium:before { + content: '\f36e'; +} +.zmdi-brightness-high:before { + content: '\f36f'; +} +.zmdi-smartphone-portrait:before { + content: '\f2d4'; +} +.zmdi-live-tv:before { + content: '\f2d9'; +} +.zmdi-format-textdirection-l-to-r:before { + content: '\f249'; +} +.zmdi-format-textdirection-r-to-l:before { + content: '\f24a'; +} +.zmdi-arrow-back:before { + content: '\f2ea'; +} +.zmdi-arrow-forward:before { + content: '\f2ee'; +} +.zmdi-arrow-in:before { + content: '\f2e9'; +} +.zmdi-arrow-out:before { + content: '\f2ed'; +} +.zmdi-rotate-90-degrees-ccw:before { + content: '\f304'; +} +.zmdi-adb:before { + content: '\f33a'; +} +.zmdi-network-wifi:before { + content: '\f2e8'; +} +.zmdi-network-wifi-alt:before { + content: '\f2e3'; +} +.zmdi-network-wifi-lock:before { + content: '\f2e5'; +} +.zmdi-network-wifi-off:before { + content: '\f2e6'; +} +.zmdi-network-wifi-outline:before { + content: '\f2e7'; +} +.zmdi-network-wifi-info:before { + content: '\f2e4'; +} +.zmdi-layers-clear:before { + content: '\f18b'; +} +.zmdi-colorize:before { + content: '\f15d'; +} +.zmdi-format-paint:before { + content: '\f1ba'; +} +.zmdi-format-quote:before { + content: '\f1b2'; +} +.zmdi-camera-monochrome-photos:before { + content: '\f285'; +} +.zmdi-sort-by-alpha:before { + content: '\f1cf'; +} +.zmdi-folder-shared:before { + content: '\f225'; +} +.zmdi-folder-special:before { + content: '\f226'; +} +.zmdi-comment-dots:before { + content: '\f260'; +} +.zmdi-reorder:before { + content: '\f31e'; +} +.zmdi-dehaze:before { + content: '\f197'; +} +.zmdi-sort:before { + content: '\f1ce'; +} +.zmdi-pages:before { + content: '\f34a'; +} +.zmdi-stack-overflow:before { + content: '\f35c'; +} +.zmdi-calendar-account:before { + content: '\f204'; +} +.zmdi-paste:before { + content: '\f109'; +} +.zmdi-cut:before { + content: '\f1bc'; +} +.zmdi-save:before { + content: '\f297'; +} +.zmdi-smartphone-code:before { + content: '\f139'; +} +.zmdi-directions-bike:before { + content: '\f117'; +} +.zmdi-directions-boat:before { + content: '\f11a'; +} +.zmdi-directions-bus:before { + content: '\f121'; +} +.zmdi-directions-car:before { + content: '\f125'; +} +.zmdi-directions-railway:before { + content: '\f1b3'; +} +.zmdi-directions-run:before { + content: '\f215'; +} +.zmdi-directions-subway:before { + content: '\f1d5'; +} +.zmdi-directions-walk:before { + content: '\f216'; +} +.zmdi-local-hotel:before { + content: '\f178'; +} +.zmdi-local-activity:before { + content: '\f1df'; +} +.zmdi-local-play:before { + content: '\f1df'; +} +.zmdi-local-airport:before { + content: '\f103'; +} +.zmdi-local-atm:before { + content: '\f198'; +} +.zmdi-local-bar:before { + content: '\f137'; +} +.zmdi-local-cafe:before { + content: '\f13b'; +} +.zmdi-local-car-wash:before { + content: '\f124'; +} +.zmdi-local-convenience-store:before { + content: '\f1d3'; +} +.zmdi-local-dining:before { + content: '\f153'; +} +.zmdi-local-drink:before { + content: '\f157'; +} +.zmdi-local-florist:before { + content: '\f168'; +} +.zmdi-local-gas-station:before { + content: '\f16f'; +} +.zmdi-local-grocery-store:before { + content: '\f1cb'; +} +.zmdi-local-hospital:before { + content: '\f177'; +} +.zmdi-local-laundry-service:before { + content: '\f1e9'; +} +.zmdi-local-library:before { + content: '\f18d'; +} +.zmdi-local-mall:before { + content: '\f195'; +} +.zmdi-local-movies:before { + content: '\f19d'; +} +.zmdi-local-offer:before { + content: '\f187'; +} +.zmdi-local-parking:before { + content: '\f1a5'; +} +.zmdi-local-parking:before { + content: '\f1a5'; +} +.zmdi-local-pharmacy:before { + content: '\f176'; +} +.zmdi-local-phone:before { + content: '\f2be'; +} +.zmdi-local-pizza:before { + content: '\f1ac'; +} +.zmdi-local-post-office:before { + content: '\f15a'; +} +.zmdi-local-printshop:before { + content: '\f1b0'; +} +.zmdi-local-see:before { + content: '\f28c'; +} +.zmdi-local-shipping:before { + content: '\f1e6'; +} +.zmdi-local-store:before { + content: '\f1d4'; +} +.zmdi-local-taxi:before { + content: '\f123'; +} +.zmdi-local-wc:before { + content: '\f211'; +} +.zmdi-my-location:before { + content: '\f299'; +} +.zmdi-directions:before { + content: '\f1e7'; +} diff --git a/dockers/web/html/colorlib-regform-16/fonts/material-icon/css/material-design-iconic-font.min.css b/dockers/web/html/colorlib-regform-16/fonts/material-icon/css/material-design-iconic-font.min.css new file mode 100755 index 0000000..e1a58fe --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/fonts/material-icon/css/material-design-iconic-font.min.css @@ -0,0 +1 @@ +@font-face{font-family:Material-Design-Iconic-Font;src:url(../fonts/Material-Design-Iconic-Font.woff2?v=2.2.0) format('woff2'),url(../fonts/Material-Design-Iconic-Font.woff?v=2.2.0) format('woff'),url(../fonts/Material-Design-Iconic-Font.ttf?v=2.2.0) format('truetype')}.zmdi{display:inline-block;font:normal normal normal 14px/1 'Material-Design-Iconic-Font';font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.zmdi-hc-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.zmdi-hc-2x{font-size:2em}.zmdi-hc-3x{font-size:3em}.zmdi-hc-4x{font-size:4em}.zmdi-hc-5x{font-size:5em}.zmdi-hc-fw{width:1.28571429em;text-align:center}.zmdi-hc-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.zmdi-hc-ul>li{position:relative}.zmdi-hc-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.zmdi-hc-li.zmdi-hc-lg{left:-1.85714286em}.zmdi-hc-border{padding:.1em .25em;border:solid .1em #9e9e9e;border-radius:2px}.zmdi-hc-border-circle{padding:.1em .25em;border:solid .1em #9e9e9e;border-radius:50%}.zmdi.pull-left{float:left;margin-right:.15em}.zmdi.pull-right{float:right;margin-left:.15em}.zmdi-hc-spin{-webkit-animation:zmdi-spin 1.5s infinite linear;animation:zmdi-spin 1.5s infinite linear}.zmdi-hc-spin-reverse{-webkit-animation:zmdi-spin-reverse 1.5s infinite linear;animation:zmdi-spin-reverse 1.5s infinite linear}@-webkit-keyframes zmdi-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes zmdi-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@-webkit-keyframes zmdi-spin-reverse{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(-359deg);transform:rotate(-359deg)}}@keyframes zmdi-spin-reverse{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(-359deg);transform:rotate(-359deg)}}.zmdi-hc-rotate-90{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.zmdi-hc-rotate-180{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.zmdi-hc-rotate-270{-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.zmdi-hc-flip-horizontal{-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.zmdi-hc-flip-vertical{-webkit-transform:scale(1,-1);-ms-transform:scale(1,-1);transform:scale(1,-1)}.zmdi-hc-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.zmdi-hc-stack-1x,.zmdi-hc-stack-2x{position:absolute;left:0;width:100%;text-align:center}.zmdi-hc-stack-1x{line-height:inherit}.zmdi-hc-stack-2x{font-size:2em}.zmdi-hc-inverse{color:#fff}.zmdi-3d-rotation:before{content:'\f101'}.zmdi-airplane-off:before{content:'\f102'}.zmdi-airplane:before{content:'\f103'}.zmdi-album:before{content:'\f104'}.zmdi-archive:before{content:'\f105'}.zmdi-assignment-account:before{content:'\f106'}.zmdi-assignment-alert:before{content:'\f107'}.zmdi-assignment-check:before{content:'\f108'}.zmdi-assignment-o:before{content:'\f109'}.zmdi-assignment-return:before{content:'\f10a'}.zmdi-assignment-returned:before{content:'\f10b'}.zmdi-assignment:before{content:'\f10c'}.zmdi-attachment-alt:before{content:'\f10d'}.zmdi-attachment:before{content:'\f10e'}.zmdi-audio:before{content:'\f10f'}.zmdi-badge-check:before{content:'\f110'}.zmdi-balance-wallet:before{content:'\f111'}.zmdi-balance:before{content:'\f112'}.zmdi-battery-alert:before{content:'\f113'}.zmdi-battery-flash:before{content:'\f114'}.zmdi-battery-unknown:before{content:'\f115'}.zmdi-battery:before{content:'\f116'}.zmdi-bike:before{content:'\f117'}.zmdi-block-alt:before{content:'\f118'}.zmdi-block:before{content:'\f119'}.zmdi-boat:before{content:'\f11a'}.zmdi-book-image:before{content:'\f11b'}.zmdi-book:before{content:'\f11c'}.zmdi-bookmark-outline:before{content:'\f11d'}.zmdi-bookmark:before{content:'\f11e'}.zmdi-brush:before{content:'\f11f'}.zmdi-bug:before{content:'\f120'}.zmdi-bus:before{content:'\f121'}.zmdi-cake:before{content:'\f122'}.zmdi-car-taxi:before{content:'\f123'}.zmdi-car-wash:before{content:'\f124'}.zmdi-car:before{content:'\f125'}.zmdi-card-giftcard:before{content:'\f126'}.zmdi-card-membership:before{content:'\f127'}.zmdi-card-travel:before{content:'\f128'}.zmdi-card:before{content:'\f129'}.zmdi-case-check:before{content:'\f12a'}.zmdi-case-download:before{content:'\f12b'}.zmdi-case-play:before{content:'\f12c'}.zmdi-case:before{content:'\f12d'}.zmdi-cast-connected:before{content:'\f12e'}.zmdi-cast:before{content:'\f12f'}.zmdi-chart-donut:before{content:'\f130'}.zmdi-chart:before{content:'\f131'}.zmdi-city-alt:before{content:'\f132'}.zmdi-city:before{content:'\f133'}.zmdi-close-circle-o:before{content:'\f134'}.zmdi-close-circle:before{content:'\f135'}.zmdi-close:before{content:'\f136'}.zmdi-cocktail:before{content:'\f137'}.zmdi-code-setting:before{content:'\f138'}.zmdi-code-smartphone:before{content:'\f139'}.zmdi-code:before{content:'\f13a'}.zmdi-coffee:before{content:'\f13b'}.zmdi-collection-bookmark:before{content:'\f13c'}.zmdi-collection-case-play:before{content:'\f13d'}.zmdi-collection-folder-image:before{content:'\f13e'}.zmdi-collection-image-o:before{content:'\f13f'}.zmdi-collection-image:before{content:'\f140'}.zmdi-collection-item-1:before{content:'\f141'}.zmdi-collection-item-2:before{content:'\f142'}.zmdi-collection-item-3:before{content:'\f143'}.zmdi-collection-item-4:before{content:'\f144'}.zmdi-collection-item-5:before{content:'\f145'}.zmdi-collection-item-6:before{content:'\f146'}.zmdi-collection-item-7:before{content:'\f147'}.zmdi-collection-item-8:before{content:'\f148'}.zmdi-collection-item-9-plus:before{content:'\f149'}.zmdi-collection-item-9:before{content:'\f14a'}.zmdi-collection-item:before{content:'\f14b'}.zmdi-collection-music:before{content:'\f14c'}.zmdi-collection-pdf:before{content:'\f14d'}.zmdi-collection-plus:before{content:'\f14e'}.zmdi-collection-speaker:before{content:'\f14f'}.zmdi-collection-text:before{content:'\f150'}.zmdi-collection-video:before{content:'\f151'}.zmdi-compass:before{content:'\f152'}.zmdi-cutlery:before{content:'\f153'}.zmdi-delete:before{content:'\f154'}.zmdi-dialpad:before{content:'\f155'}.zmdi-dns:before{content:'\f156'}.zmdi-drink:before{content:'\f157'}.zmdi-edit:before{content:'\f158'}.zmdi-email-open:before{content:'\f159'}.zmdi-email:before{content:'\f15a'}.zmdi-eye-off:before{content:'\f15b'}.zmdi-eye:before{content:'\f15c'}.zmdi-eyedropper:before{content:'\f15d'}.zmdi-favorite-outline:before{content:'\f15e'}.zmdi-favorite:before{content:'\f15f'}.zmdi-filter-list:before{content:'\f160'}.zmdi-fire:before{content:'\f161'}.zmdi-flag:before{content:'\f162'}.zmdi-flare:before{content:'\f163'}.zmdi-flash-auto:before{content:'\f164'}.zmdi-flash-off:before{content:'\f165'}.zmdi-flash:before{content:'\f166'}.zmdi-flip:before{content:'\f167'}.zmdi-flower-alt:before{content:'\f168'}.zmdi-flower:before{content:'\f169'}.zmdi-font:before{content:'\f16a'}.zmdi-fullscreen-alt:before{content:'\f16b'}.zmdi-fullscreen-exit:before{content:'\f16c'}.zmdi-fullscreen:before{content:'\f16d'}.zmdi-functions:before{content:'\f16e'}.zmdi-gas-station:before{content:'\f16f'}.zmdi-gesture:before{content:'\f170'}.zmdi-globe-alt:before{content:'\f171'}.zmdi-globe-lock:before{content:'\f172'}.zmdi-globe:before{content:'\f173'}.zmdi-graduation-cap:before{content:'\f174'}.zmdi-home:before{content:'\f175'}.zmdi-hospital-alt:before{content:'\f176'}.zmdi-hospital:before{content:'\f177'}.zmdi-hotel:before{content:'\f178'}.zmdi-hourglass-alt:before{content:'\f179'}.zmdi-hourglass-outline:before{content:'\f17a'}.zmdi-hourglass:before{content:'\f17b'}.zmdi-http:before{content:'\f17c'}.zmdi-image-alt:before{content:'\f17d'}.zmdi-image-o:before{content:'\f17e'}.zmdi-image:before{content:'\f17f'}.zmdi-inbox:before{content:'\f180'}.zmdi-invert-colors-off:before{content:'\f181'}.zmdi-invert-colors:before{content:'\f182'}.zmdi-key:before{content:'\f183'}.zmdi-label-alt-outline:before{content:'\f184'}.zmdi-label-alt:before{content:'\f185'}.zmdi-label-heart:before{content:'\f186'}.zmdi-label:before{content:'\f187'}.zmdi-labels:before{content:'\f188'}.zmdi-lamp:before{content:'\f189'}.zmdi-landscape:before{content:'\f18a'}.zmdi-layers-off:before{content:'\f18b'}.zmdi-layers:before{content:'\f18c'}.zmdi-library:before{content:'\f18d'}.zmdi-link:before{content:'\f18e'}.zmdi-lock-open:before{content:'\f18f'}.zmdi-lock-outline:before{content:'\f190'}.zmdi-lock:before{content:'\f191'}.zmdi-mail-reply-all:before{content:'\f192'}.zmdi-mail-reply:before{content:'\f193'}.zmdi-mail-send:before{content:'\f194'}.zmdi-mall:before{content:'\f195'}.zmdi-map:before{content:'\f196'}.zmdi-menu:before{content:'\f197'}.zmdi-money-box:before{content:'\f198'}.zmdi-money-off:before{content:'\f199'}.zmdi-money:before{content:'\f19a'}.zmdi-more-vert:before{content:'\f19b'}.zmdi-more:before{content:'\f19c'}.zmdi-movie-alt:before{content:'\f19d'}.zmdi-movie:before{content:'\f19e'}.zmdi-nature-people:before{content:'\f19f'}.zmdi-nature:before{content:'\f1a0'}.zmdi-navigation:before{content:'\f1a1'}.zmdi-open-in-browser:before{content:'\f1a2'}.zmdi-open-in-new:before{content:'\f1a3'}.zmdi-palette:before{content:'\f1a4'}.zmdi-parking:before{content:'\f1a5'}.zmdi-pin-account:before{content:'\f1a6'}.zmdi-pin-assistant:before{content:'\f1a7'}.zmdi-pin-drop:before{content:'\f1a8'}.zmdi-pin-help:before{content:'\f1a9'}.zmdi-pin-off:before{content:'\f1aa'}.zmdi-pin:before{content:'\f1ab'}.zmdi-pizza:before{content:'\f1ac'}.zmdi-plaster:before{content:'\f1ad'}.zmdi-power-setting:before{content:'\f1ae'}.zmdi-power:before{content:'\f1af'}.zmdi-print:before{content:'\f1b0'}.zmdi-puzzle-piece:before{content:'\f1b1'}.zmdi-quote:before{content:'\f1b2'}.zmdi-railway:before{content:'\f1b3'}.zmdi-receipt:before{content:'\f1b4'}.zmdi-refresh-alt:before{content:'\f1b5'}.zmdi-refresh-sync-alert:before{content:'\f1b6'}.zmdi-refresh-sync-off:before{content:'\f1b7'}.zmdi-refresh-sync:before{content:'\f1b8'}.zmdi-refresh:before{content:'\f1b9'}.zmdi-roller:before{content:'\f1ba'}.zmdi-ruler:before{content:'\f1bb'}.zmdi-scissors:before{content:'\f1bc'}.zmdi-screen-rotation-lock:before{content:'\f1bd'}.zmdi-screen-rotation:before{content:'\f1be'}.zmdi-search-for:before{content:'\f1bf'}.zmdi-search-in-file:before{content:'\f1c0'}.zmdi-search-in-page:before{content:'\f1c1'}.zmdi-search-replace:before{content:'\f1c2'}.zmdi-search:before{content:'\f1c3'}.zmdi-seat:before{content:'\f1c4'}.zmdi-settings-square:before{content:'\f1c5'}.zmdi-settings:before{content:'\f1c6'}.zmdi-shield-check:before{content:'\f1c7'}.zmdi-shield-security:before{content:'\f1c8'}.zmdi-shopping-basket:before{content:'\f1c9'}.zmdi-shopping-cart-plus:before{content:'\f1ca'}.zmdi-shopping-cart:before{content:'\f1cb'}.zmdi-sign-in:before{content:'\f1cc'}.zmdi-sort-amount-asc:before{content:'\f1cd'}.zmdi-sort-amount-desc:before{content:'\f1ce'}.zmdi-sort-asc:before{content:'\f1cf'}.zmdi-sort-desc:before{content:'\f1d0'}.zmdi-spellcheck:before{content:'\f1d1'}.zmdi-storage:before{content:'\f1d2'}.zmdi-store-24:before{content:'\f1d3'}.zmdi-store:before{content:'\f1d4'}.zmdi-subway:before{content:'\f1d5'}.zmdi-sun:before{content:'\f1d6'}.zmdi-tab-unselected:before{content:'\f1d7'}.zmdi-tab:before{content:'\f1d8'}.zmdi-tag-close:before{content:'\f1d9'}.zmdi-tag-more:before{content:'\f1da'}.zmdi-tag:before{content:'\f1db'}.zmdi-thumb-down:before{content:'\f1dc'}.zmdi-thumb-up-down:before{content:'\f1dd'}.zmdi-thumb-up:before{content:'\f1de'}.zmdi-ticket-star:before{content:'\f1df'}.zmdi-toll:before{content:'\f1e0'}.zmdi-toys:before{content:'\f1e1'}.zmdi-traffic:before{content:'\f1e2'}.zmdi-translate:before{content:'\f1e3'}.zmdi-triangle-down:before{content:'\f1e4'}.zmdi-triangle-up:before{content:'\f1e5'}.zmdi-truck:before{content:'\f1e6'}.zmdi-turning-sign:before{content:'\f1e7'}.zmdi-wallpaper:before{content:'\f1e8'}.zmdi-washing-machine:before{content:'\f1e9'}.zmdi-window-maximize:before{content:'\f1ea'}.zmdi-window-minimize:before{content:'\f1eb'}.zmdi-window-restore:before{content:'\f1ec'}.zmdi-wrench:before{content:'\f1ed'}.zmdi-zoom-in:before{content:'\f1ee'}.zmdi-zoom-out:before{content:'\f1ef'}.zmdi-alert-circle-o:before{content:'\f1f0'}.zmdi-alert-circle:before{content:'\f1f1'}.zmdi-alert-octagon:before{content:'\f1f2'}.zmdi-alert-polygon:before{content:'\f1f3'}.zmdi-alert-triangle:before{content:'\f1f4'}.zmdi-help-outline:before{content:'\f1f5'}.zmdi-help:before{content:'\f1f6'}.zmdi-info-outline:before{content:'\f1f7'}.zmdi-info:before{content:'\f1f8'}.zmdi-notifications-active:before{content:'\f1f9'}.zmdi-notifications-add:before{content:'\f1fa'}.zmdi-notifications-none:before{content:'\f1fb'}.zmdi-notifications-off:before{content:'\f1fc'}.zmdi-notifications-paused:before{content:'\f1fd'}.zmdi-notifications:before{content:'\f1fe'}.zmdi-account-add:before{content:'\f1ff'}.zmdi-account-box-mail:before{content:'\f200'}.zmdi-account-box-o:before{content:'\f201'}.zmdi-account-box-phone:before{content:'\f202'}.zmdi-account-box:before{content:'\f203'}.zmdi-account-calendar:before{content:'\f204'}.zmdi-account-circle:before{content:'\f205'}.zmdi-account-o:before{content:'\f206'}.zmdi-account:before{content:'\f207'}.zmdi-accounts-add:before{content:'\f208'}.zmdi-accounts-alt:before{content:'\f209'}.zmdi-accounts-list-alt:before{content:'\f20a'}.zmdi-accounts-list:before{content:'\f20b'}.zmdi-accounts-outline:before{content:'\f20c'}.zmdi-accounts:before{content:'\f20d'}.zmdi-face:before{content:'\f20e'}.zmdi-female:before{content:'\f20f'}.zmdi-male-alt:before{content:'\f210'}.zmdi-male-female:before{content:'\f211'}.zmdi-male:before{content:'\f212'}.zmdi-mood-bad:before{content:'\f213'}.zmdi-mood:before{content:'\f214'}.zmdi-run:before{content:'\f215'}.zmdi-walk:before{content:'\f216'}.zmdi-cloud-box:before{content:'\f217'}.zmdi-cloud-circle:before{content:'\f218'}.zmdi-cloud-done:before{content:'\f219'}.zmdi-cloud-download:before{content:'\f21a'}.zmdi-cloud-off:before{content:'\f21b'}.zmdi-cloud-outline-alt:before{content:'\f21c'}.zmdi-cloud-outline:before{content:'\f21d'}.zmdi-cloud-upload:before{content:'\f21e'}.zmdi-cloud:before{content:'\f21f'}.zmdi-download:before{content:'\f220'}.zmdi-file-plus:before{content:'\f221'}.zmdi-file-text:before{content:'\f222'}.zmdi-file:before{content:'\f223'}.zmdi-folder-outline:before{content:'\f224'}.zmdi-folder-person:before{content:'\f225'}.zmdi-folder-star-alt:before{content:'\f226'}.zmdi-folder-star:before{content:'\f227'}.zmdi-folder:before{content:'\f228'}.zmdi-gif:before{content:'\f229'}.zmdi-upload:before{content:'\f22a'}.zmdi-border-all:before{content:'\f22b'}.zmdi-border-bottom:before{content:'\f22c'}.zmdi-border-clear:before{content:'\f22d'}.zmdi-border-color:before{content:'\f22e'}.zmdi-border-horizontal:before{content:'\f22f'}.zmdi-border-inner:before{content:'\f230'}.zmdi-border-left:before{content:'\f231'}.zmdi-border-outer:before{content:'\f232'}.zmdi-border-right:before{content:'\f233'}.zmdi-border-style:before{content:'\f234'}.zmdi-border-top:before{content:'\f235'}.zmdi-border-vertical:before{content:'\f236'}.zmdi-copy:before{content:'\f237'}.zmdi-crop:before{content:'\f238'}.zmdi-format-align-center:before{content:'\f239'}.zmdi-format-align-justify:before{content:'\f23a'}.zmdi-format-align-left:before{content:'\f23b'}.zmdi-format-align-right:before{content:'\f23c'}.zmdi-format-bold:before{content:'\f23d'}.zmdi-format-clear-all:before{content:'\f23e'}.zmdi-format-clear:before{content:'\f23f'}.zmdi-format-color-fill:before{content:'\f240'}.zmdi-format-color-reset:before{content:'\f241'}.zmdi-format-color-text:before{content:'\f242'}.zmdi-format-indent-decrease:before{content:'\f243'}.zmdi-format-indent-increase:before{content:'\f244'}.zmdi-format-italic:before{content:'\f245'}.zmdi-format-line-spacing:before{content:'\f246'}.zmdi-format-list-bulleted:before{content:'\f247'}.zmdi-format-list-numbered:before{content:'\f248'}.zmdi-format-ltr:before{content:'\f249'}.zmdi-format-rtl:before{content:'\f24a'}.zmdi-format-size:before{content:'\f24b'}.zmdi-format-strikethrough-s:before{content:'\f24c'}.zmdi-format-strikethrough:before{content:'\f24d'}.zmdi-format-subject:before{content:'\f24e'}.zmdi-format-underlined:before{content:'\f24f'}.zmdi-format-valign-bottom:before{content:'\f250'}.zmdi-format-valign-center:before{content:'\f251'}.zmdi-format-valign-top:before{content:'\f252'}.zmdi-redo:before{content:'\f253'}.zmdi-select-all:before{content:'\f254'}.zmdi-space-bar:before{content:'\f255'}.zmdi-text-format:before{content:'\f256'}.zmdi-transform:before{content:'\f257'}.zmdi-undo:before{content:'\f258'}.zmdi-wrap-text:before{content:'\f259'}.zmdi-comment-alert:before{content:'\f25a'}.zmdi-comment-alt-text:before{content:'\f25b'}.zmdi-comment-alt:before{content:'\f25c'}.zmdi-comment-edit:before{content:'\f25d'}.zmdi-comment-image:before{content:'\f25e'}.zmdi-comment-list:before{content:'\f25f'}.zmdi-comment-more:before{content:'\f260'}.zmdi-comment-outline:before{content:'\f261'}.zmdi-comment-text-alt:before{content:'\f262'}.zmdi-comment-text:before{content:'\f263'}.zmdi-comment-video:before{content:'\f264'}.zmdi-comment:before{content:'\f265'}.zmdi-comments:before{content:'\f266'}.zmdi-check-all:before{content:'\f267'}.zmdi-check-circle-u:before{content:'\f268'}.zmdi-check-circle:before{content:'\f269'}.zmdi-check-square:before{content:'\f26a'}.zmdi-check:before{content:'\f26b'}.zmdi-circle-o:before{content:'\f26c'}.zmdi-circle:before{content:'\f26d'}.zmdi-dot-circle-alt:before{content:'\f26e'}.zmdi-dot-circle:before{content:'\f26f'}.zmdi-minus-circle-outline:before{content:'\f270'}.zmdi-minus-circle:before{content:'\f271'}.zmdi-minus-square:before{content:'\f272'}.zmdi-minus:before{content:'\f273'}.zmdi-plus-circle-o-duplicate:before{content:'\f274'}.zmdi-plus-circle-o:before{content:'\f275'}.zmdi-plus-circle:before{content:'\f276'}.zmdi-plus-square:before{content:'\f277'}.zmdi-plus:before{content:'\f278'}.zmdi-square-o:before{content:'\f279'}.zmdi-star-circle:before{content:'\f27a'}.zmdi-star-half:before{content:'\f27b'}.zmdi-star-outline:before{content:'\f27c'}.zmdi-star:before{content:'\f27d'}.zmdi-bluetooth-connected:before{content:'\f27e'}.zmdi-bluetooth-off:before{content:'\f27f'}.zmdi-bluetooth-search:before{content:'\f280'}.zmdi-bluetooth-setting:before{content:'\f281'}.zmdi-bluetooth:before{content:'\f282'}.zmdi-camera-add:before{content:'\f283'}.zmdi-camera-alt:before{content:'\f284'}.zmdi-camera-bw:before{content:'\f285'}.zmdi-camera-front:before{content:'\f286'}.zmdi-camera-mic:before{content:'\f287'}.zmdi-camera-party-mode:before{content:'\f288'}.zmdi-camera-rear:before{content:'\f289'}.zmdi-camera-roll:before{content:'\f28a'}.zmdi-camera-switch:before{content:'\f28b'}.zmdi-camera:before{content:'\f28c'}.zmdi-card-alert:before{content:'\f28d'}.zmdi-card-off:before{content:'\f28e'}.zmdi-card-sd:before{content:'\f28f'}.zmdi-card-sim:before{content:'\f290'}.zmdi-desktop-mac:before{content:'\f291'}.zmdi-desktop-windows:before{content:'\f292'}.zmdi-device-hub:before{content:'\f293'}.zmdi-devices-off:before{content:'\f294'}.zmdi-devices:before{content:'\f295'}.zmdi-dock:before{content:'\f296'}.zmdi-floppy:before{content:'\f297'}.zmdi-gamepad:before{content:'\f298'}.zmdi-gps-dot:before{content:'\f299'}.zmdi-gps-off:before{content:'\f29a'}.zmdi-gps:before{content:'\f29b'}.zmdi-headset-mic:before{content:'\f29c'}.zmdi-headset:before{content:'\f29d'}.zmdi-input-antenna:before{content:'\f29e'}.zmdi-input-composite:before{content:'\f29f'}.zmdi-input-hdmi:before{content:'\f2a0'}.zmdi-input-power:before{content:'\f2a1'}.zmdi-input-svideo:before{content:'\f2a2'}.zmdi-keyboard-hide:before{content:'\f2a3'}.zmdi-keyboard:before{content:'\f2a4'}.zmdi-laptop-chromebook:before{content:'\f2a5'}.zmdi-laptop-mac:before{content:'\f2a6'}.zmdi-laptop:before{content:'\f2a7'}.zmdi-mic-off:before{content:'\f2a8'}.zmdi-mic-outline:before{content:'\f2a9'}.zmdi-mic-setting:before{content:'\f2aa'}.zmdi-mic:before{content:'\f2ab'}.zmdi-mouse:before{content:'\f2ac'}.zmdi-network-alert:before{content:'\f2ad'}.zmdi-network-locked:before{content:'\f2ae'}.zmdi-network-off:before{content:'\f2af'}.zmdi-network-outline:before{content:'\f2b0'}.zmdi-network-setting:before{content:'\f2b1'}.zmdi-network:before{content:'\f2b2'}.zmdi-phone-bluetooth:before{content:'\f2b3'}.zmdi-phone-end:before{content:'\f2b4'}.zmdi-phone-forwarded:before{content:'\f2b5'}.zmdi-phone-in-talk:before{content:'\f2b6'}.zmdi-phone-locked:before{content:'\f2b7'}.zmdi-phone-missed:before{content:'\f2b8'}.zmdi-phone-msg:before{content:'\f2b9'}.zmdi-phone-paused:before{content:'\f2ba'}.zmdi-phone-ring:before{content:'\f2bb'}.zmdi-phone-setting:before{content:'\f2bc'}.zmdi-phone-sip:before{content:'\f2bd'}.zmdi-phone:before{content:'\f2be'}.zmdi-portable-wifi-changes:before{content:'\f2bf'}.zmdi-portable-wifi-off:before{content:'\f2c0'}.zmdi-portable-wifi:before{content:'\f2c1'}.zmdi-radio:before{content:'\f2c2'}.zmdi-reader:before{content:'\f2c3'}.zmdi-remote-control-alt:before{content:'\f2c4'}.zmdi-remote-control:before{content:'\f2c5'}.zmdi-router:before{content:'\f2c6'}.zmdi-scanner:before{content:'\f2c7'}.zmdi-smartphone-android:before{content:'\f2c8'}.zmdi-smartphone-download:before{content:'\f2c9'}.zmdi-smartphone-erase:before{content:'\f2ca'}.zmdi-smartphone-info:before{content:'\f2cb'}.zmdi-smartphone-iphone:before{content:'\f2cc'}.zmdi-smartphone-landscape-lock:before{content:'\f2cd'}.zmdi-smartphone-landscape:before{content:'\f2ce'}.zmdi-smartphone-lock:before{content:'\f2cf'}.zmdi-smartphone-portrait-lock:before{content:'\f2d0'}.zmdi-smartphone-ring:before{content:'\f2d1'}.zmdi-smartphone-setting:before{content:'\f2d2'}.zmdi-smartphone-setup:before{content:'\f2d3'}.zmdi-smartphone:before{content:'\f2d4'}.zmdi-speaker:before{content:'\f2d5'}.zmdi-tablet-android:before{content:'\f2d6'}.zmdi-tablet-mac:before{content:'\f2d7'}.zmdi-tablet:before{content:'\f2d8'}.zmdi-tv-alt-play:before{content:'\f2d9'}.zmdi-tv-list:before{content:'\f2da'}.zmdi-tv-play:before{content:'\f2db'}.zmdi-tv:before{content:'\f2dc'}.zmdi-usb:before{content:'\f2dd'}.zmdi-videocam-off:before{content:'\f2de'}.zmdi-videocam-switch:before{content:'\f2df'}.zmdi-videocam:before{content:'\f2e0'}.zmdi-watch:before{content:'\f2e1'}.zmdi-wifi-alt-2:before{content:'\f2e2'}.zmdi-wifi-alt:before{content:'\f2e3'}.zmdi-wifi-info:before{content:'\f2e4'}.zmdi-wifi-lock:before{content:'\f2e5'}.zmdi-wifi-off:before{content:'\f2e6'}.zmdi-wifi-outline:before{content:'\f2e7'}.zmdi-wifi:before{content:'\f2e8'}.zmdi-arrow-left-bottom:before{content:'\f2e9'}.zmdi-arrow-left:before{content:'\f2ea'}.zmdi-arrow-merge:before{content:'\f2eb'}.zmdi-arrow-missed:before{content:'\f2ec'}.zmdi-arrow-right-top:before{content:'\f2ed'}.zmdi-arrow-right:before{content:'\f2ee'}.zmdi-arrow-split:before{content:'\f2ef'}.zmdi-arrows:before{content:'\f2f0'}.zmdi-caret-down-circle:before{content:'\f2f1'}.zmdi-caret-down:before{content:'\f2f2'}.zmdi-caret-left-circle:before{content:'\f2f3'}.zmdi-caret-left:before{content:'\f2f4'}.zmdi-caret-right-circle:before{content:'\f2f5'}.zmdi-caret-right:before{content:'\f2f6'}.zmdi-caret-up-circle:before{content:'\f2f7'}.zmdi-caret-up:before{content:'\f2f8'}.zmdi-chevron-down:before{content:'\f2f9'}.zmdi-chevron-left:before{content:'\f2fa'}.zmdi-chevron-right:before{content:'\f2fb'}.zmdi-chevron-up:before{content:'\f2fc'}.zmdi-forward:before{content:'\f2fd'}.zmdi-long-arrow-down:before{content:'\f2fe'}.zmdi-long-arrow-left:before{content:'\f2ff'}.zmdi-long-arrow-return:before{content:'\f300'}.zmdi-long-arrow-right:before{content:'\f301'}.zmdi-long-arrow-tab:before{content:'\f302'}.zmdi-long-arrow-up:before{content:'\f303'}.zmdi-rotate-ccw:before{content:'\f304'}.zmdi-rotate-cw:before{content:'\f305'}.zmdi-rotate-left:before{content:'\f306'}.zmdi-rotate-right:before{content:'\f307'}.zmdi-square-down:before{content:'\f308'}.zmdi-square-right:before{content:'\f309'}.zmdi-swap-alt:before{content:'\f30a'}.zmdi-swap-vertical-circle:before{content:'\f30b'}.zmdi-swap-vertical:before{content:'\f30c'}.zmdi-swap:before{content:'\f30d'}.zmdi-trending-down:before{content:'\f30e'}.zmdi-trending-flat:before{content:'\f30f'}.zmdi-trending-up:before{content:'\f310'}.zmdi-unfold-less:before{content:'\f311'}.zmdi-unfold-more:before{content:'\f312'}.zmdi-apps:before{content:'\f313'}.zmdi-grid-off:before{content:'\f314'}.zmdi-grid:before{content:'\f315'}.zmdi-view-agenda:before{content:'\f316'}.zmdi-view-array:before{content:'\f317'}.zmdi-view-carousel:before{content:'\f318'}.zmdi-view-column:before{content:'\f319'}.zmdi-view-comfy:before{content:'\f31a'}.zmdi-view-compact:before{content:'\f31b'}.zmdi-view-dashboard:before{content:'\f31c'}.zmdi-view-day:before{content:'\f31d'}.zmdi-view-headline:before{content:'\f31e'}.zmdi-view-list-alt:before{content:'\f31f'}.zmdi-view-list:before{content:'\f320'}.zmdi-view-module:before{content:'\f321'}.zmdi-view-quilt:before{content:'\f322'}.zmdi-view-stream:before{content:'\f323'}.zmdi-view-subtitles:before{content:'\f324'}.zmdi-view-toc:before{content:'\f325'}.zmdi-view-web:before{content:'\f326'}.zmdi-view-week:before{content:'\f327'}.zmdi-widgets:before{content:'\f328'}.zmdi-alarm-check:before{content:'\f329'}.zmdi-alarm-off:before{content:'\f32a'}.zmdi-alarm-plus:before{content:'\f32b'}.zmdi-alarm-snooze:before{content:'\f32c'}.zmdi-alarm:before{content:'\f32d'}.zmdi-calendar-alt:before{content:'\f32e'}.zmdi-calendar-check:before{content:'\f32f'}.zmdi-calendar-close:before{content:'\f330'}.zmdi-calendar-note:before{content:'\f331'}.zmdi-calendar:before{content:'\f332'}.zmdi-time-countdown:before{content:'\f333'}.zmdi-time-interval:before{content:'\f334'}.zmdi-time-restore-setting:before{content:'\f335'}.zmdi-time-restore:before{content:'\f336'}.zmdi-time:before{content:'\f337'}.zmdi-timer-off:before{content:'\f338'}.zmdi-timer:before{content:'\f339'}.zmdi-android-alt:before{content:'\f33a'}.zmdi-android:before{content:'\f33b'}.zmdi-apple:before{content:'\f33c'}.zmdi-behance:before{content:'\f33d'}.zmdi-codepen:before{content:'\f33e'}.zmdi-dribbble:before{content:'\f33f'}.zmdi-dropbox:before{content:'\f340'}.zmdi-evernote:before{content:'\f341'}.zmdi-facebook-box:before{content:'\f342'}.zmdi-facebook:before{content:'\f343'}.zmdi-github-box:before{content:'\f344'}.zmdi-github:before{content:'\f345'}.zmdi-google-drive:before{content:'\f346'}.zmdi-google-earth:before{content:'\f347'}.zmdi-google-glass:before{content:'\f348'}.zmdi-google-maps:before{content:'\f349'}.zmdi-google-pages:before{content:'\f34a'}.zmdi-google-play:before{content:'\f34b'}.zmdi-google-plus-box:before{content:'\f34c'}.zmdi-google-plus:before{content:'\f34d'}.zmdi-google:before{content:'\f34e'}.zmdi-instagram:before{content:'\f34f'}.zmdi-language-css3:before{content:'\f350'}.zmdi-language-html5:before{content:'\f351'}.zmdi-language-javascript:before{content:'\f352'}.zmdi-language-python-alt:before{content:'\f353'}.zmdi-language-python:before{content:'\f354'}.zmdi-lastfm:before{content:'\f355'}.zmdi-linkedin-box:before{content:'\f356'}.zmdi-paypal:before{content:'\f357'}.zmdi-pinterest-box:before{content:'\f358'}.zmdi-pocket:before{content:'\f359'}.zmdi-polymer:before{content:'\f35a'}.zmdi-share:before{content:'\f35b'}.zmdi-stackoverflow:before{content:'\f35c'}.zmdi-steam-square:before{content:'\f35d'}.zmdi-steam:before{content:'\f35e'}.zmdi-twitter-box:before{content:'\f35f'}.zmdi-twitter:before{content:'\f360'}.zmdi-vk:before{content:'\f361'}.zmdi-wikipedia:before{content:'\f362'}.zmdi-windows:before{content:'\f363'}.zmdi-aspect-ratio-alt:before{content:'\f364'}.zmdi-aspect-ratio:before{content:'\f365'}.zmdi-blur-circular:before{content:'\f366'}.zmdi-blur-linear:before{content:'\f367'}.zmdi-blur-off:before{content:'\f368'}.zmdi-blur:before{content:'\f369'}.zmdi-brightness-2:before{content:'\f36a'}.zmdi-brightness-3:before{content:'\f36b'}.zmdi-brightness-4:before{content:'\f36c'}.zmdi-brightness-5:before{content:'\f36d'}.zmdi-brightness-6:before{content:'\f36e'}.zmdi-brightness-7:before{content:'\f36f'}.zmdi-brightness-auto:before{content:'\f370'}.zmdi-brightness-setting:before{content:'\f371'}.zmdi-broken-image:before{content:'\f372'}.zmdi-center-focus-strong:before{content:'\f373'}.zmdi-center-focus-weak:before{content:'\f374'}.zmdi-compare:before{content:'\f375'}.zmdi-crop-16-9:before{content:'\f376'}.zmdi-crop-3-2:before{content:'\f377'}.zmdi-crop-5-4:before{content:'\f378'}.zmdi-crop-7-5:before{content:'\f379'}.zmdi-crop-din:before{content:'\f37a'}.zmdi-crop-free:before{content:'\f37b'}.zmdi-crop-landscape:before{content:'\f37c'}.zmdi-crop-portrait:before{content:'\f37d'}.zmdi-crop-square:before{content:'\f37e'}.zmdi-exposure-alt:before{content:'\f37f'}.zmdi-exposure:before{content:'\f380'}.zmdi-filter-b-and-w:before{content:'\f381'}.zmdi-filter-center-focus:before{content:'\f382'}.zmdi-filter-frames:before{content:'\f383'}.zmdi-filter-tilt-shift:before{content:'\f384'}.zmdi-gradient:before{content:'\f385'}.zmdi-grain:before{content:'\f386'}.zmdi-graphic-eq:before{content:'\f387'}.zmdi-hdr-off:before{content:'\f388'}.zmdi-hdr-strong:before{content:'\f389'}.zmdi-hdr-weak:before{content:'\f38a'}.zmdi-hdr:before{content:'\f38b'}.zmdi-iridescent:before{content:'\f38c'}.zmdi-leak-off:before{content:'\f38d'}.zmdi-leak:before{content:'\f38e'}.zmdi-looks:before{content:'\f38f'}.zmdi-loupe:before{content:'\f390'}.zmdi-panorama-horizontal:before{content:'\f391'}.zmdi-panorama-vertical:before{content:'\f392'}.zmdi-panorama-wide-angle:before{content:'\f393'}.zmdi-photo-size-select-large:before{content:'\f394'}.zmdi-photo-size-select-small:before{content:'\f395'}.zmdi-picture-in-picture:before{content:'\f396'}.zmdi-slideshow:before{content:'\f397'}.zmdi-texture:before{content:'\f398'}.zmdi-tonality:before{content:'\f399'}.zmdi-vignette:before{content:'\f39a'}.zmdi-wb-auto:before{content:'\f39b'}.zmdi-eject-alt:before{content:'\f39c'}.zmdi-eject:before{content:'\f39d'}.zmdi-equalizer:before{content:'\f39e'}.zmdi-fast-forward:before{content:'\f39f'}.zmdi-fast-rewind:before{content:'\f3a0'}.zmdi-forward-10:before{content:'\f3a1'}.zmdi-forward-30:before{content:'\f3a2'}.zmdi-forward-5:before{content:'\f3a3'}.zmdi-hearing:before{content:'\f3a4'}.zmdi-pause-circle-outline:before{content:'\f3a5'}.zmdi-pause-circle:before{content:'\f3a6'}.zmdi-pause:before{content:'\f3a7'}.zmdi-play-circle-outline:before{content:'\f3a8'}.zmdi-play-circle:before{content:'\f3a9'}.zmdi-play:before{content:'\f3aa'}.zmdi-playlist-audio:before{content:'\f3ab'}.zmdi-playlist-plus:before{content:'\f3ac'}.zmdi-repeat-one:before{content:'\f3ad'}.zmdi-repeat:before{content:'\f3ae'}.zmdi-replay-10:before{content:'\f3af'}.zmdi-replay-30:before{content:'\f3b0'}.zmdi-replay-5:before{content:'\f3b1'}.zmdi-replay:before{content:'\f3b2'}.zmdi-shuffle:before{content:'\f3b3'}.zmdi-skip-next:before{content:'\f3b4'}.zmdi-skip-previous:before{content:'\f3b5'}.zmdi-stop:before{content:'\f3b6'}.zmdi-surround-sound:before{content:'\f3b7'}.zmdi-tune:before{content:'\f3b8'}.zmdi-volume-down:before{content:'\f3b9'}.zmdi-volume-mute:before{content:'\f3ba'}.zmdi-volume-off:before{content:'\f3bb'}.zmdi-volume-up:before{content:'\f3bc'}.zmdi-n-1-square:before{content:'\f3bd'}.zmdi-n-2-square:before{content:'\f3be'}.zmdi-n-3-square:before{content:'\f3bf'}.zmdi-n-4-square:before{content:'\f3c0'}.zmdi-n-5-square:before{content:'\f3c1'}.zmdi-n-6-square:before{content:'\f3c2'}.zmdi-neg-1:before{content:'\f3c3'}.zmdi-neg-2:before{content:'\f3c4'}.zmdi-plus-1:before{content:'\f3c5'}.zmdi-plus-2:before{content:'\f3c6'}.zmdi-sec-10:before{content:'\f3c7'}.zmdi-sec-3:before{content:'\f3c8'}.zmdi-zero:before{content:'\f3c9'}.zmdi-airline-seat-flat-angled:before{content:'\f3ca'}.zmdi-airline-seat-flat:before{content:'\f3cb'}.zmdi-airline-seat-individual-suite:before{content:'\f3cc'}.zmdi-airline-seat-legroom-extra:before{content:'\f3cd'}.zmdi-airline-seat-legroom-normal:before{content:'\f3ce'}.zmdi-airline-seat-legroom-reduced:before{content:'\f3cf'}.zmdi-airline-seat-recline-extra:before{content:'\f3d0'}.zmdi-airline-seat-recline-normal:before{content:'\f3d1'}.zmdi-airplay:before{content:'\f3d2'}.zmdi-closed-caption:before{content:'\f3d3'}.zmdi-confirmation-number:before{content:'\f3d4'}.zmdi-developer-board:before{content:'\f3d5'}.zmdi-disc-full:before{content:'\f3d6'}.zmdi-explicit:before{content:'\f3d7'}.zmdi-flight-land:before{content:'\f3d8'}.zmdi-flight-takeoff:before{content:'\f3d9'}.zmdi-flip-to-back:before{content:'\f3da'}.zmdi-flip-to-front:before{content:'\f3db'}.zmdi-group-work:before{content:'\f3dc'}.zmdi-hd:before{content:'\f3dd'}.zmdi-hq:before{content:'\f3de'}.zmdi-markunread-mailbox:before{content:'\f3df'}.zmdi-memory:before{content:'\f3e0'}.zmdi-nfc:before{content:'\f3e1'}.zmdi-play-for-work:before{content:'\f3e2'}.zmdi-power-input:before{content:'\f3e3'}.zmdi-present-to-all:before{content:'\f3e4'}.zmdi-satellite:before{content:'\f3e5'}.zmdi-tap-and-play:before{content:'\f3e6'}.zmdi-vibration:before{content:'\f3e7'}.zmdi-voicemail:before{content:'\f3e8'}.zmdi-group:before{content:'\f3e9'}.zmdi-rss:before{content:'\f3ea'}.zmdi-shape:before{content:'\f3eb'}.zmdi-spinner:before{content:'\f3ec'}.zmdi-ungroup:before{content:'\f3ed'}.zmdi-500px:before{content:'\f3ee'}.zmdi-8tracks:before{content:'\f3ef'}.zmdi-amazon:before{content:'\f3f0'}.zmdi-blogger:before{content:'\f3f1'}.zmdi-delicious:before{content:'\f3f2'}.zmdi-disqus:before{content:'\f3f3'}.zmdi-flattr:before{content:'\f3f4'}.zmdi-flickr:before{content:'\f3f5'}.zmdi-github-alt:before{content:'\f3f6'}.zmdi-google-old:before{content:'\f3f7'}.zmdi-linkedin:before{content:'\f3f8'}.zmdi-odnoklassniki:before{content:'\f3f9'}.zmdi-outlook:before{content:'\f3fa'}.zmdi-paypal-alt:before{content:'\f3fb'}.zmdi-pinterest:before{content:'\f3fc'}.zmdi-playstation:before{content:'\f3fd'}.zmdi-reddit:before{content:'\f3fe'}.zmdi-skype:before{content:'\f3ff'}.zmdi-slideshare:before{content:'\f400'}.zmdi-soundcloud:before{content:'\f401'}.zmdi-tumblr:before{content:'\f402'}.zmdi-twitch:before{content:'\f403'}.zmdi-vimeo:before{content:'\f404'}.zmdi-whatsapp:before{content:'\f405'}.zmdi-xbox:before{content:'\f406'}.zmdi-yahoo:before{content:'\f407'}.zmdi-youtube-play:before{content:'\f408'}.zmdi-youtube:before{content:'\f409'}.zmdi-3d-rotation:before{content:'\f101'}.zmdi-airplane-off:before{content:'\f102'}.zmdi-airplane:before{content:'\f103'}.zmdi-album:before{content:'\f104'}.zmdi-archive:before{content:'\f105'}.zmdi-assignment-account:before{content:'\f106'}.zmdi-assignment-alert:before{content:'\f107'}.zmdi-assignment-check:before{content:'\f108'}.zmdi-assignment-o:before{content:'\f109'}.zmdi-assignment-return:before{content:'\f10a'}.zmdi-assignment-returned:before{content:'\f10b'}.zmdi-assignment:before{content:'\f10c'}.zmdi-attachment-alt:before{content:'\f10d'}.zmdi-attachment:before{content:'\f10e'}.zmdi-audio:before{content:'\f10f'}.zmdi-badge-check:before{content:'\f110'}.zmdi-balance-wallet:before{content:'\f111'}.zmdi-balance:before{content:'\f112'}.zmdi-battery-alert:before{content:'\f113'}.zmdi-battery-flash:before{content:'\f114'}.zmdi-battery-unknown:before{content:'\f115'}.zmdi-battery:before{content:'\f116'}.zmdi-bike:before{content:'\f117'}.zmdi-block-alt:before{content:'\f118'}.zmdi-block:before{content:'\f119'}.zmdi-boat:before{content:'\f11a'}.zmdi-book-image:before{content:'\f11b'}.zmdi-book:before{content:'\f11c'}.zmdi-bookmark-outline:before{content:'\f11d'}.zmdi-bookmark:before{content:'\f11e'}.zmdi-brush:before{content:'\f11f'}.zmdi-bug:before{content:'\f120'}.zmdi-bus:before{content:'\f121'}.zmdi-cake:before{content:'\f122'}.zmdi-car-taxi:before{content:'\f123'}.zmdi-car-wash:before{content:'\f124'}.zmdi-car:before{content:'\f125'}.zmdi-card-giftcard:before{content:'\f126'}.zmdi-card-membership:before{content:'\f127'}.zmdi-card-travel:before{content:'\f128'}.zmdi-card:before{content:'\f129'}.zmdi-case-check:before{content:'\f12a'}.zmdi-case-download:before{content:'\f12b'}.zmdi-case-play:before{content:'\f12c'}.zmdi-case:before{content:'\f12d'}.zmdi-cast-connected:before{content:'\f12e'}.zmdi-cast:before{content:'\f12f'}.zmdi-chart-donut:before{content:'\f130'}.zmdi-chart:before{content:'\f131'}.zmdi-city-alt:before{content:'\f132'}.zmdi-city:before{content:'\f133'}.zmdi-close-circle-o:before{content:'\f134'}.zmdi-close-circle:before{content:'\f135'}.zmdi-close:before{content:'\f136'}.zmdi-cocktail:before{content:'\f137'}.zmdi-code-setting:before{content:'\f138'}.zmdi-code-smartphone:before{content:'\f139'}.zmdi-code:before{content:'\f13a'}.zmdi-coffee:before{content:'\f13b'}.zmdi-collection-bookmark:before{content:'\f13c'}.zmdi-collection-case-play:before{content:'\f13d'}.zmdi-collection-folder-image:before{content:'\f13e'}.zmdi-collection-image-o:before{content:'\f13f'}.zmdi-collection-image:before{content:'\f140'}.zmdi-collection-item-1:before{content:'\f141'}.zmdi-collection-item-2:before{content:'\f142'}.zmdi-collection-item-3:before{content:'\f143'}.zmdi-collection-item-4:before{content:'\f144'}.zmdi-collection-item-5:before{content:'\f145'}.zmdi-collection-item-6:before{content:'\f146'}.zmdi-collection-item-7:before{content:'\f147'}.zmdi-collection-item-8:before{content:'\f148'}.zmdi-collection-item-9-plus:before{content:'\f149'}.zmdi-collection-item-9:before{content:'\f14a'}.zmdi-collection-item:before{content:'\f14b'}.zmdi-collection-music:before{content:'\f14c'}.zmdi-collection-pdf:before{content:'\f14d'}.zmdi-collection-plus:before{content:'\f14e'}.zmdi-collection-speaker:before{content:'\f14f'}.zmdi-collection-text:before{content:'\f150'}.zmdi-collection-video:before{content:'\f151'}.zmdi-compass:before{content:'\f152'}.zmdi-cutlery:before{content:'\f153'}.zmdi-delete:before{content:'\f154'}.zmdi-dialpad:before{content:'\f155'}.zmdi-dns:before{content:'\f156'}.zmdi-drink:before{content:'\f157'}.zmdi-edit:before{content:'\f158'}.zmdi-email-open:before{content:'\f159'}.zmdi-email:before{content:'\f15a'}.zmdi-eye-off:before{content:'\f15b'}.zmdi-eye:before{content:'\f15c'}.zmdi-eyedropper:before{content:'\f15d'}.zmdi-favorite-outline:before{content:'\f15e'}.zmdi-favorite:before{content:'\f15f'}.zmdi-filter-list:before{content:'\f160'}.zmdi-fire:before{content:'\f161'}.zmdi-flag:before{content:'\f162'}.zmdi-flare:before{content:'\f163'}.zmdi-flash-auto:before{content:'\f164'}.zmdi-flash-off:before{content:'\f165'}.zmdi-flash:before{content:'\f166'}.zmdi-flip:before{content:'\f167'}.zmdi-flower-alt:before{content:'\f168'}.zmdi-flower:before{content:'\f169'}.zmdi-font:before{content:'\f16a'}.zmdi-fullscreen-alt:before{content:'\f16b'}.zmdi-fullscreen-exit:before{content:'\f16c'}.zmdi-fullscreen:before{content:'\f16d'}.zmdi-functions:before{content:'\f16e'}.zmdi-gas-station:before{content:'\f16f'}.zmdi-gesture:before{content:'\f170'}.zmdi-globe-alt:before{content:'\f171'}.zmdi-globe-lock:before{content:'\f172'}.zmdi-globe:before{content:'\f173'}.zmdi-graduation-cap:before{content:'\f174'}.zmdi-home:before{content:'\f175'}.zmdi-hospital-alt:before{content:'\f176'}.zmdi-hospital:before{content:'\f177'}.zmdi-hotel:before{content:'\f178'}.zmdi-hourglass-alt:before{content:'\f179'}.zmdi-hourglass-outline:before{content:'\f17a'}.zmdi-hourglass:before{content:'\f17b'}.zmdi-http:before{content:'\f17c'}.zmdi-image-alt:before{content:'\f17d'}.zmdi-image-o:before{content:'\f17e'}.zmdi-image:before{content:'\f17f'}.zmdi-inbox:before{content:'\f180'}.zmdi-invert-colors-off:before{content:'\f181'}.zmdi-invert-colors:before{content:'\f182'}.zmdi-key:before{content:'\f183'}.zmdi-label-alt-outline:before{content:'\f184'}.zmdi-label-alt:before{content:'\f185'}.zmdi-label-heart:before{content:'\f186'}.zmdi-label:before{content:'\f187'}.zmdi-labels:before{content:'\f188'}.zmdi-lamp:before{content:'\f189'}.zmdi-landscape:before{content:'\f18a'}.zmdi-layers-off:before{content:'\f18b'}.zmdi-layers:before{content:'\f18c'}.zmdi-library:before{content:'\f18d'}.zmdi-link:before{content:'\f18e'}.zmdi-lock-open:before{content:'\f18f'}.zmdi-lock-outline:before{content:'\f190'}.zmdi-lock:before{content:'\f191'}.zmdi-mail-reply-all:before{content:'\f192'}.zmdi-mail-reply:before{content:'\f193'}.zmdi-mail-send:before{content:'\f194'}.zmdi-mall:before{content:'\f195'}.zmdi-map:before{content:'\f196'}.zmdi-menu:before{content:'\f197'}.zmdi-money-box:before{content:'\f198'}.zmdi-money-off:before{content:'\f199'}.zmdi-money:before{content:'\f19a'}.zmdi-more-vert:before{content:'\f19b'}.zmdi-more:before{content:'\f19c'}.zmdi-movie-alt:before{content:'\f19d'}.zmdi-movie:before{content:'\f19e'}.zmdi-nature-people:before{content:'\f19f'}.zmdi-nature:before{content:'\f1a0'}.zmdi-navigation:before{content:'\f1a1'}.zmdi-open-in-browser:before{content:'\f1a2'}.zmdi-open-in-new:before{content:'\f1a3'}.zmdi-palette:before{content:'\f1a4'}.zmdi-parking:before{content:'\f1a5'}.zmdi-pin-account:before{content:'\f1a6'}.zmdi-pin-assistant:before{content:'\f1a7'}.zmdi-pin-drop:before{content:'\f1a8'}.zmdi-pin-help:before{content:'\f1a9'}.zmdi-pin-off:before{content:'\f1aa'}.zmdi-pin:before{content:'\f1ab'}.zmdi-pizza:before{content:'\f1ac'}.zmdi-plaster:before{content:'\f1ad'}.zmdi-power-setting:before{content:'\f1ae'}.zmdi-power:before{content:'\f1af'}.zmdi-print:before{content:'\f1b0'}.zmdi-puzzle-piece:before{content:'\f1b1'}.zmdi-quote:before{content:'\f1b2'}.zmdi-railway:before{content:'\f1b3'}.zmdi-receipt:before{content:'\f1b4'}.zmdi-refresh-alt:before{content:'\f1b5'}.zmdi-refresh-sync-alert:before{content:'\f1b6'}.zmdi-refresh-sync-off:before{content:'\f1b7'}.zmdi-refresh-sync:before{content:'\f1b8'}.zmdi-refresh:before{content:'\f1b9'}.zmdi-roller:before{content:'\f1ba'}.zmdi-ruler:before{content:'\f1bb'}.zmdi-scissors:before{content:'\f1bc'}.zmdi-screen-rotation-lock:before{content:'\f1bd'}.zmdi-screen-rotation:before{content:'\f1be'}.zmdi-search-for:before{content:'\f1bf'}.zmdi-search-in-file:before{content:'\f1c0'}.zmdi-search-in-page:before{content:'\f1c1'}.zmdi-search-replace:before{content:'\f1c2'}.zmdi-search:before{content:'\f1c3'}.zmdi-seat:before{content:'\f1c4'}.zmdi-settings-square:before{content:'\f1c5'}.zmdi-settings:before{content:'\f1c6'}.zmdi-shield-check:before{content:'\f1c7'}.zmdi-shield-security:before{content:'\f1c8'}.zmdi-shopping-basket:before{content:'\f1c9'}.zmdi-shopping-cart-plus:before{content:'\f1ca'}.zmdi-shopping-cart:before{content:'\f1cb'}.zmdi-sign-in:before{content:'\f1cc'}.zmdi-sort-amount-asc:before{content:'\f1cd'}.zmdi-sort-amount-desc:before{content:'\f1ce'}.zmdi-sort-asc:before{content:'\f1cf'}.zmdi-sort-desc:before{content:'\f1d0'}.zmdi-spellcheck:before{content:'\f1d1'}.zmdi-storage:before{content:'\f1d2'}.zmdi-store-24:before{content:'\f1d3'}.zmdi-store:before{content:'\f1d4'}.zmdi-subway:before{content:'\f1d5'}.zmdi-sun:before{content:'\f1d6'}.zmdi-tab-unselected:before{content:'\f1d7'}.zmdi-tab:before{content:'\f1d8'}.zmdi-tag-close:before{content:'\f1d9'}.zmdi-tag-more:before{content:'\f1da'}.zmdi-tag:before{content:'\f1db'}.zmdi-thumb-down:before{content:'\f1dc'}.zmdi-thumb-up-down:before{content:'\f1dd'}.zmdi-thumb-up:before{content:'\f1de'}.zmdi-ticket-star:before{content:'\f1df'}.zmdi-toll:before{content:'\f1e0'}.zmdi-toys:before{content:'\f1e1'}.zmdi-traffic:before{content:'\f1e2'}.zmdi-translate:before{content:'\f1e3'}.zmdi-triangle-down:before{content:'\f1e4'}.zmdi-triangle-up:before{content:'\f1e5'}.zmdi-truck:before{content:'\f1e6'}.zmdi-turning-sign:before{content:'\f1e7'}.zmdi-wallpaper:before{content:'\f1e8'}.zmdi-washing-machine:before{content:'\f1e9'}.zmdi-window-maximize:before{content:'\f1ea'}.zmdi-window-minimize:before{content:'\f1eb'}.zmdi-window-restore:before{content:'\f1ec'}.zmdi-wrench:before{content:'\f1ed'}.zmdi-zoom-in:before{content:'\f1ee'}.zmdi-zoom-out:before{content:'\f1ef'}.zmdi-alert-circle-o:before{content:'\f1f0'}.zmdi-alert-circle:before{content:'\f1f1'}.zmdi-alert-octagon:before{content:'\f1f2'}.zmdi-alert-polygon:before{content:'\f1f3'}.zmdi-alert-triangle:before{content:'\f1f4'}.zmdi-help-outline:before{content:'\f1f5'}.zmdi-help:before{content:'\f1f6'}.zmdi-info-outline:before{content:'\f1f7'}.zmdi-info:before{content:'\f1f8'}.zmdi-notifications-active:before{content:'\f1f9'}.zmdi-notifications-add:before{content:'\f1fa'}.zmdi-notifications-none:before{content:'\f1fb'}.zmdi-notifications-off:before{content:'\f1fc'}.zmdi-notifications-paused:before{content:'\f1fd'}.zmdi-notifications:before{content:'\f1fe'}.zmdi-account-add:before{content:'\f1ff'}.zmdi-account-box-mail:before{content:'\f200'}.zmdi-account-box-o:before{content:'\f201'}.zmdi-account-box-phone:before{content:'\f202'}.zmdi-account-box:before{content:'\f203'}.zmdi-account-calendar:before{content:'\f204'}.zmdi-account-circle:before{content:'\f205'}.zmdi-account-o:before{content:'\f206'}.zmdi-account:before{content:'\f207'}.zmdi-accounts-add:before{content:'\f208'}.zmdi-accounts-alt:before{content:'\f209'}.zmdi-accounts-list-alt:before{content:'\f20a'}.zmdi-accounts-list:before{content:'\f20b'}.zmdi-accounts-outline:before{content:'\f20c'}.zmdi-accounts:before{content:'\f20d'}.zmdi-face:before{content:'\f20e'}.zmdi-female:before{content:'\f20f'}.zmdi-male-alt:before{content:'\f210'}.zmdi-male-female:before{content:'\f211'}.zmdi-male:before{content:'\f212'}.zmdi-mood-bad:before{content:'\f213'}.zmdi-mood:before{content:'\f214'}.zmdi-run:before{content:'\f215'}.zmdi-walk:before{content:'\f216'}.zmdi-cloud-box:before{content:'\f217'}.zmdi-cloud-circle:before{content:'\f218'}.zmdi-cloud-done:before{content:'\f219'}.zmdi-cloud-download:before{content:'\f21a'}.zmdi-cloud-off:before{content:'\f21b'}.zmdi-cloud-outline-alt:before{content:'\f21c'}.zmdi-cloud-outline:before{content:'\f21d'}.zmdi-cloud-upload:before{content:'\f21e'}.zmdi-cloud:before{content:'\f21f'}.zmdi-download:before{content:'\f220'}.zmdi-file-plus:before{content:'\f221'}.zmdi-file-text:before{content:'\f222'}.zmdi-file:before{content:'\f223'}.zmdi-folder-outline:before{content:'\f224'}.zmdi-folder-person:before{content:'\f225'}.zmdi-folder-star-alt:before{content:'\f226'}.zmdi-folder-star:before{content:'\f227'}.zmdi-folder:before{content:'\f228'}.zmdi-gif:before{content:'\f229'}.zmdi-upload:before{content:'\f22a'}.zmdi-border-all:before{content:'\f22b'}.zmdi-border-bottom:before{content:'\f22c'}.zmdi-border-clear:before{content:'\f22d'}.zmdi-border-color:before{content:'\f22e'}.zmdi-border-horizontal:before{content:'\f22f'}.zmdi-border-inner:before{content:'\f230'}.zmdi-border-left:before{content:'\f231'}.zmdi-border-outer:before{content:'\f232'}.zmdi-border-right:before{content:'\f233'}.zmdi-border-style:before{content:'\f234'}.zmdi-border-top:before{content:'\f235'}.zmdi-border-vertical:before{content:'\f236'}.zmdi-copy:before{content:'\f237'}.zmdi-crop:before{content:'\f238'}.zmdi-format-align-center:before{content:'\f239'}.zmdi-format-align-justify:before{content:'\f23a'}.zmdi-format-align-left:before{content:'\f23b'}.zmdi-format-align-right:before{content:'\f23c'}.zmdi-format-bold:before{content:'\f23d'}.zmdi-format-clear-all:before{content:'\f23e'}.zmdi-format-clear:before{content:'\f23f'}.zmdi-format-color-fill:before{content:'\f240'}.zmdi-format-color-reset:before{content:'\f241'}.zmdi-format-color-text:before{content:'\f242'}.zmdi-format-indent-decrease:before{content:'\f243'}.zmdi-format-indent-increase:before{content:'\f244'}.zmdi-format-italic:before{content:'\f245'}.zmdi-format-line-spacing:before{content:'\f246'}.zmdi-format-list-bulleted:before{content:'\f247'}.zmdi-format-list-numbered:before{content:'\f248'}.zmdi-format-ltr:before{content:'\f249'}.zmdi-format-rtl:before{content:'\f24a'}.zmdi-format-size:before{content:'\f24b'}.zmdi-format-strikethrough-s:before{content:'\f24c'}.zmdi-format-strikethrough:before{content:'\f24d'}.zmdi-format-subject:before{content:'\f24e'}.zmdi-format-underlined:before{content:'\f24f'}.zmdi-format-valign-bottom:before{content:'\f250'}.zmdi-format-valign-center:before{content:'\f251'}.zmdi-format-valign-top:before{content:'\f252'}.zmdi-redo:before{content:'\f253'}.zmdi-select-all:before{content:'\f254'}.zmdi-space-bar:before{content:'\f255'}.zmdi-text-format:before{content:'\f256'}.zmdi-transform:before{content:'\f257'}.zmdi-undo:before{content:'\f258'}.zmdi-wrap-text:before{content:'\f259'}.zmdi-comment-alert:before{content:'\f25a'}.zmdi-comment-alt-text:before{content:'\f25b'}.zmdi-comment-alt:before{content:'\f25c'}.zmdi-comment-edit:before{content:'\f25d'}.zmdi-comment-image:before{content:'\f25e'}.zmdi-comment-list:before{content:'\f25f'}.zmdi-comment-more:before{content:'\f260'}.zmdi-comment-outline:before{content:'\f261'}.zmdi-comment-text-alt:before{content:'\f262'}.zmdi-comment-text:before{content:'\f263'}.zmdi-comment-video:before{content:'\f264'}.zmdi-comment:before{content:'\f265'}.zmdi-comments:before{content:'\f266'}.zmdi-check-all:before{content:'\f267'}.zmdi-check-circle-u:before{content:'\f268'}.zmdi-check-circle:before{content:'\f269'}.zmdi-check-square:before{content:'\f26a'}.zmdi-check:before{content:'\f26b'}.zmdi-circle-o:before{content:'\f26c'}.zmdi-circle:before{content:'\f26d'}.zmdi-dot-circle-alt:before{content:'\f26e'}.zmdi-dot-circle:before{content:'\f26f'}.zmdi-minus-circle-outline:before{content:'\f270'}.zmdi-minus-circle:before{content:'\f271'}.zmdi-minus-square:before{content:'\f272'}.zmdi-minus:before{content:'\f273'}.zmdi-plus-circle-o-duplicate:before{content:'\f274'}.zmdi-plus-circle-o:before{content:'\f275'}.zmdi-plus-circle:before{content:'\f276'}.zmdi-plus-square:before{content:'\f277'}.zmdi-plus:before{content:'\f278'}.zmdi-square-o:before{content:'\f279'}.zmdi-star-circle:before{content:'\f27a'}.zmdi-star-half:before{content:'\f27b'}.zmdi-star-outline:before{content:'\f27c'}.zmdi-star:before{content:'\f27d'}.zmdi-bluetooth-connected:before{content:'\f27e'}.zmdi-bluetooth-off:before{content:'\f27f'}.zmdi-bluetooth-search:before{content:'\f280'}.zmdi-bluetooth-setting:before{content:'\f281'}.zmdi-bluetooth:before{content:'\f282'}.zmdi-camera-add:before{content:'\f283'}.zmdi-camera-alt:before{content:'\f284'}.zmdi-camera-bw:before{content:'\f285'}.zmdi-camera-front:before{content:'\f286'}.zmdi-camera-mic:before{content:'\f287'}.zmdi-camera-party-mode:before{content:'\f288'}.zmdi-camera-rear:before{content:'\f289'}.zmdi-camera-roll:before{content:'\f28a'}.zmdi-camera-switch:before{content:'\f28b'}.zmdi-camera:before{content:'\f28c'}.zmdi-card-alert:before{content:'\f28d'}.zmdi-card-off:before{content:'\f28e'}.zmdi-card-sd:before{content:'\f28f'}.zmdi-card-sim:before{content:'\f290'}.zmdi-desktop-mac:before{content:'\f291'}.zmdi-desktop-windows:before{content:'\f292'}.zmdi-device-hub:before{content:'\f293'}.zmdi-devices-off:before{content:'\f294'}.zmdi-devices:before{content:'\f295'}.zmdi-dock:before{content:'\f296'}.zmdi-floppy:before{content:'\f297'}.zmdi-gamepad:before{content:'\f298'}.zmdi-gps-dot:before{content:'\f299'}.zmdi-gps-off:before{content:'\f29a'}.zmdi-gps:before{content:'\f29b'}.zmdi-headset-mic:before{content:'\f29c'}.zmdi-headset:before{content:'\f29d'}.zmdi-input-antenna:before{content:'\f29e'}.zmdi-input-composite:before{content:'\f29f'}.zmdi-input-hdmi:before{content:'\f2a0'}.zmdi-input-power:before{content:'\f2a1'}.zmdi-input-svideo:before{content:'\f2a2'}.zmdi-keyboard-hide:before{content:'\f2a3'}.zmdi-keyboard:before{content:'\f2a4'}.zmdi-laptop-chromebook:before{content:'\f2a5'}.zmdi-laptop-mac:before{content:'\f2a6'}.zmdi-laptop:before{content:'\f2a7'}.zmdi-mic-off:before{content:'\f2a8'}.zmdi-mic-outline:before{content:'\f2a9'}.zmdi-mic-setting:before{content:'\f2aa'}.zmdi-mic:before{content:'\f2ab'}.zmdi-mouse:before{content:'\f2ac'}.zmdi-network-alert:before{content:'\f2ad'}.zmdi-network-locked:before{content:'\f2ae'}.zmdi-network-off:before{content:'\f2af'}.zmdi-network-outline:before{content:'\f2b0'}.zmdi-network-setting:before{content:'\f2b1'}.zmdi-network:before{content:'\f2b2'}.zmdi-phone-bluetooth:before{content:'\f2b3'}.zmdi-phone-end:before{content:'\f2b4'}.zmdi-phone-forwarded:before{content:'\f2b5'}.zmdi-phone-in-talk:before{content:'\f2b6'}.zmdi-phone-locked:before{content:'\f2b7'}.zmdi-phone-missed:before{content:'\f2b8'}.zmdi-phone-msg:before{content:'\f2b9'}.zmdi-phone-paused:before{content:'\f2ba'}.zmdi-phone-ring:before{content:'\f2bb'}.zmdi-phone-setting:before{content:'\f2bc'}.zmdi-phone-sip:before{content:'\f2bd'}.zmdi-phone:before{content:'\f2be'}.zmdi-portable-wifi-changes:before{content:'\f2bf'}.zmdi-portable-wifi-off:before{content:'\f2c0'}.zmdi-portable-wifi:before{content:'\f2c1'}.zmdi-radio:before{content:'\f2c2'}.zmdi-reader:before{content:'\f2c3'}.zmdi-remote-control-alt:before{content:'\f2c4'}.zmdi-remote-control:before{content:'\f2c5'}.zmdi-router:before{content:'\f2c6'}.zmdi-scanner:before{content:'\f2c7'}.zmdi-smartphone-android:before{content:'\f2c8'}.zmdi-smartphone-download:before{content:'\f2c9'}.zmdi-smartphone-erase:before{content:'\f2ca'}.zmdi-smartphone-info:before{content:'\f2cb'}.zmdi-smartphone-iphone:before{content:'\f2cc'}.zmdi-smartphone-landscape-lock:before{content:'\f2cd'}.zmdi-smartphone-landscape:before{content:'\f2ce'}.zmdi-smartphone-lock:before{content:'\f2cf'}.zmdi-smartphone-portrait-lock:before{content:'\f2d0'}.zmdi-smartphone-ring:before{content:'\f2d1'}.zmdi-smartphone-setting:before{content:'\f2d2'}.zmdi-smartphone-setup:before{content:'\f2d3'}.zmdi-smartphone:before{content:'\f2d4'}.zmdi-speaker:before{content:'\f2d5'}.zmdi-tablet-android:before{content:'\f2d6'}.zmdi-tablet-mac:before{content:'\f2d7'}.zmdi-tablet:before{content:'\f2d8'}.zmdi-tv-alt-play:before{content:'\f2d9'}.zmdi-tv-list:before{content:'\f2da'}.zmdi-tv-play:before{content:'\f2db'}.zmdi-tv:before{content:'\f2dc'}.zmdi-usb:before{content:'\f2dd'}.zmdi-videocam-off:before{content:'\f2de'}.zmdi-videocam-switch:before{content:'\f2df'}.zmdi-videocam:before{content:'\f2e0'}.zmdi-watch:before{content:'\f2e1'}.zmdi-wifi-alt-2:before{content:'\f2e2'}.zmdi-wifi-alt:before{content:'\f2e3'}.zmdi-wifi-info:before{content:'\f2e4'}.zmdi-wifi-lock:before{content:'\f2e5'}.zmdi-wifi-off:before{content:'\f2e6'}.zmdi-wifi-outline:before{content:'\f2e7'}.zmdi-wifi:before{content:'\f2e8'}.zmdi-arrow-left-bottom:before{content:'\f2e9'}.zmdi-arrow-left:before{content:'\f2ea'}.zmdi-arrow-merge:before{content:'\f2eb'}.zmdi-arrow-missed:before{content:'\f2ec'}.zmdi-arrow-right-top:before{content:'\f2ed'}.zmdi-arrow-right:before{content:'\f2ee'}.zmdi-arrow-split:before{content:'\f2ef'}.zmdi-arrows:before{content:'\f2f0'}.zmdi-caret-down-circle:before{content:'\f2f1'}.zmdi-caret-down:before{content:'\f2f2'}.zmdi-caret-left-circle:before{content:'\f2f3'}.zmdi-caret-left:before{content:'\f2f4'}.zmdi-caret-right-circle:before{content:'\f2f5'}.zmdi-caret-right:before{content:'\f2f6'}.zmdi-caret-up-circle:before{content:'\f2f7'}.zmdi-caret-up:before{content:'\f2f8'}.zmdi-chevron-down:before{content:'\f2f9'}.zmdi-chevron-left:before{content:'\f2fa'}.zmdi-chevron-right:before{content:'\f2fb'}.zmdi-chevron-up:before{content:'\f2fc'}.zmdi-forward:before{content:'\f2fd'}.zmdi-long-arrow-down:before{content:'\f2fe'}.zmdi-long-arrow-left:before{content:'\f2ff'}.zmdi-long-arrow-return:before{content:'\f300'}.zmdi-long-arrow-right:before{content:'\f301'}.zmdi-long-arrow-tab:before{content:'\f302'}.zmdi-long-arrow-up:before{content:'\f303'}.zmdi-rotate-ccw:before{content:'\f304'}.zmdi-rotate-cw:before{content:'\f305'}.zmdi-rotate-left:before{content:'\f306'}.zmdi-rotate-right:before{content:'\f307'}.zmdi-square-down:before{content:'\f308'}.zmdi-square-right:before{content:'\f309'}.zmdi-swap-alt:before{content:'\f30a'}.zmdi-swap-vertical-circle:before{content:'\f30b'}.zmdi-swap-vertical:before{content:'\f30c'}.zmdi-swap:before{content:'\f30d'}.zmdi-trending-down:before{content:'\f30e'}.zmdi-trending-flat:before{content:'\f30f'}.zmdi-trending-up:before{content:'\f310'}.zmdi-unfold-less:before{content:'\f311'}.zmdi-unfold-more:before{content:'\f312'}.zmdi-apps:before{content:'\f313'}.zmdi-grid-off:before{content:'\f314'}.zmdi-grid:before{content:'\f315'}.zmdi-view-agenda:before{content:'\f316'}.zmdi-view-array:before{content:'\f317'}.zmdi-view-carousel:before{content:'\f318'}.zmdi-view-column:before{content:'\f319'}.zmdi-view-comfy:before{content:'\f31a'}.zmdi-view-compact:before{content:'\f31b'}.zmdi-view-dashboard:before{content:'\f31c'}.zmdi-view-day:before{content:'\f31d'}.zmdi-view-headline:before{content:'\f31e'}.zmdi-view-list-alt:before{content:'\f31f'}.zmdi-view-list:before{content:'\f320'}.zmdi-view-module:before{content:'\f321'}.zmdi-view-quilt:before{content:'\f322'}.zmdi-view-stream:before{content:'\f323'}.zmdi-view-subtitles:before{content:'\f324'}.zmdi-view-toc:before{content:'\f325'}.zmdi-view-web:before{content:'\f326'}.zmdi-view-week:before{content:'\f327'}.zmdi-widgets:before{content:'\f328'}.zmdi-alarm-check:before{content:'\f329'}.zmdi-alarm-off:before{content:'\f32a'}.zmdi-alarm-plus:before{content:'\f32b'}.zmdi-alarm-snooze:before{content:'\f32c'}.zmdi-alarm:before{content:'\f32d'}.zmdi-calendar-alt:before{content:'\f32e'}.zmdi-calendar-check:before{content:'\f32f'}.zmdi-calendar-close:before{content:'\f330'}.zmdi-calendar-note:before{content:'\f331'}.zmdi-calendar:before{content:'\f332'}.zmdi-time-countdown:before{content:'\f333'}.zmdi-time-interval:before{content:'\f334'}.zmdi-time-restore-setting:before{content:'\f335'}.zmdi-time-restore:before{content:'\f336'}.zmdi-time:before{content:'\f337'}.zmdi-timer-off:before{content:'\f338'}.zmdi-timer:before{content:'\f339'}.zmdi-android-alt:before{content:'\f33a'}.zmdi-android:before{content:'\f33b'}.zmdi-apple:before{content:'\f33c'}.zmdi-behance:before{content:'\f33d'}.zmdi-codepen:before{content:'\f33e'}.zmdi-dribbble:before{content:'\f33f'}.zmdi-dropbox:before{content:'\f340'}.zmdi-evernote:before{content:'\f341'}.zmdi-facebook-box:before{content:'\f342'}.zmdi-facebook:before{content:'\f343'}.zmdi-github-box:before{content:'\f344'}.zmdi-github:before{content:'\f345'}.zmdi-google-drive:before{content:'\f346'}.zmdi-google-earth:before{content:'\f347'}.zmdi-google-glass:before{content:'\f348'}.zmdi-google-maps:before{content:'\f349'}.zmdi-google-pages:before{content:'\f34a'}.zmdi-google-play:before{content:'\f34b'}.zmdi-google-plus-box:before{content:'\f34c'}.zmdi-google-plus:before{content:'\f34d'}.zmdi-google:before{content:'\f34e'}.zmdi-instagram:before{content:'\f34f'}.zmdi-language-css3:before{content:'\f350'}.zmdi-language-html5:before{content:'\f351'}.zmdi-language-javascript:before{content:'\f352'}.zmdi-language-python-alt:before{content:'\f353'}.zmdi-language-python:before{content:'\f354'}.zmdi-lastfm:before{content:'\f355'}.zmdi-linkedin-box:before{content:'\f356'}.zmdi-paypal:before{content:'\f357'}.zmdi-pinterest-box:before{content:'\f358'}.zmdi-pocket:before{content:'\f359'}.zmdi-polymer:before{content:'\f35a'}.zmdi-share:before{content:'\f35b'}.zmdi-stackoverflow:before{content:'\f35c'}.zmdi-steam-square:before{content:'\f35d'}.zmdi-steam:before{content:'\f35e'}.zmdi-twitter-box:before{content:'\f35f'}.zmdi-twitter:before{content:'\f360'}.zmdi-vk:before{content:'\f361'}.zmdi-wikipedia:before{content:'\f362'}.zmdi-windows:before{content:'\f363'}.zmdi-aspect-ratio-alt:before{content:'\f364'}.zmdi-aspect-ratio:before{content:'\f365'}.zmdi-blur-circular:before{content:'\f366'}.zmdi-blur-linear:before{content:'\f367'}.zmdi-blur-off:before{content:'\f368'}.zmdi-blur:before{content:'\f369'}.zmdi-brightness-2:before{content:'\f36a'}.zmdi-brightness-3:before{content:'\f36b'}.zmdi-brightness-4:before{content:'\f36c'}.zmdi-brightness-5:before{content:'\f36d'}.zmdi-brightness-6:before{content:'\f36e'}.zmdi-brightness-7:before{content:'\f36f'}.zmdi-brightness-auto:before{content:'\f370'}.zmdi-brightness-setting:before{content:'\f371'}.zmdi-broken-image:before{content:'\f372'}.zmdi-center-focus-strong:before{content:'\f373'}.zmdi-center-focus-weak:before{content:'\f374'}.zmdi-compare:before{content:'\f375'}.zmdi-crop-16-9:before{content:'\f376'}.zmdi-crop-3-2:before{content:'\f377'}.zmdi-crop-5-4:before{content:'\f378'}.zmdi-crop-7-5:before{content:'\f379'}.zmdi-crop-din:before{content:'\f37a'}.zmdi-crop-free:before{content:'\f37b'}.zmdi-crop-landscape:before{content:'\f37c'}.zmdi-crop-portrait:before{content:'\f37d'}.zmdi-crop-square:before{content:'\f37e'}.zmdi-exposure-alt:before{content:'\f37f'}.zmdi-exposure:before{content:'\f380'}.zmdi-filter-b-and-w:before{content:'\f381'}.zmdi-filter-center-focus:before{content:'\f382'}.zmdi-filter-frames:before{content:'\f383'}.zmdi-filter-tilt-shift:before{content:'\f384'}.zmdi-gradient:before{content:'\f385'}.zmdi-grain:before{content:'\f386'}.zmdi-graphic-eq:before{content:'\f387'}.zmdi-hdr-off:before{content:'\f388'}.zmdi-hdr-strong:before{content:'\f389'}.zmdi-hdr-weak:before{content:'\f38a'}.zmdi-hdr:before{content:'\f38b'}.zmdi-iridescent:before{content:'\f38c'}.zmdi-leak-off:before{content:'\f38d'}.zmdi-leak:before{content:'\f38e'}.zmdi-looks:before{content:'\f38f'}.zmdi-loupe:before{content:'\f390'}.zmdi-panorama-horizontal:before{content:'\f391'}.zmdi-panorama-vertical:before{content:'\f392'}.zmdi-panorama-wide-angle:before{content:'\f393'}.zmdi-photo-size-select-large:before{content:'\f394'}.zmdi-photo-size-select-small:before{content:'\f395'}.zmdi-picture-in-picture:before{content:'\f396'}.zmdi-slideshow:before{content:'\f397'}.zmdi-texture:before{content:'\f398'}.zmdi-tonality:before{content:'\f399'}.zmdi-vignette:before{content:'\f39a'}.zmdi-wb-auto:before{content:'\f39b'}.zmdi-eject-alt:before{content:'\f39c'}.zmdi-eject:before{content:'\f39d'}.zmdi-equalizer:before{content:'\f39e'}.zmdi-fast-forward:before{content:'\f39f'}.zmdi-fast-rewind:before{content:'\f3a0'}.zmdi-forward-10:before{content:'\f3a1'}.zmdi-forward-30:before{content:'\f3a2'}.zmdi-forward-5:before{content:'\f3a3'}.zmdi-hearing:before{content:'\f3a4'}.zmdi-pause-circle-outline:before{content:'\f3a5'}.zmdi-pause-circle:before{content:'\f3a6'}.zmdi-pause:before{content:'\f3a7'}.zmdi-play-circle-outline:before{content:'\f3a8'}.zmdi-play-circle:before{content:'\f3a9'}.zmdi-play:before{content:'\f3aa'}.zmdi-playlist-audio:before{content:'\f3ab'}.zmdi-playlist-plus:before{content:'\f3ac'}.zmdi-repeat-one:before{content:'\f3ad'}.zmdi-repeat:before{content:'\f3ae'}.zmdi-replay-10:before{content:'\f3af'}.zmdi-replay-30:before{content:'\f3b0'}.zmdi-replay-5:before{content:'\f3b1'}.zmdi-replay:before{content:'\f3b2'}.zmdi-shuffle:before{content:'\f3b3'}.zmdi-skip-next:before{content:'\f3b4'}.zmdi-skip-previous:before{content:'\f3b5'}.zmdi-stop:before{content:'\f3b6'}.zmdi-surround-sound:before{content:'\f3b7'}.zmdi-tune:before{content:'\f3b8'}.zmdi-volume-down:before{content:'\f3b9'}.zmdi-volume-mute:before{content:'\f3ba'}.zmdi-volume-off:before{content:'\f3bb'}.zmdi-volume-up:before{content:'\f3bc'}.zmdi-n-1-square:before{content:'\f3bd'}.zmdi-n-2-square:before{content:'\f3be'}.zmdi-n-3-square:before{content:'\f3bf'}.zmdi-n-4-square:before{content:'\f3c0'}.zmdi-n-5-square:before{content:'\f3c1'}.zmdi-n-6-square:before{content:'\f3c2'}.zmdi-neg-1:before{content:'\f3c3'}.zmdi-neg-2:before{content:'\f3c4'}.zmdi-plus-1:before{content:'\f3c5'}.zmdi-plus-2:before{content:'\f3c6'}.zmdi-sec-10:before{content:'\f3c7'}.zmdi-sec-3:before{content:'\f3c8'}.zmdi-zero:before{content:'\f3c9'}.zmdi-airline-seat-flat-angled:before{content:'\f3ca'}.zmdi-airline-seat-flat:before{content:'\f3cb'}.zmdi-airline-seat-individual-suite:before{content:'\f3cc'}.zmdi-airline-seat-legroom-extra:before{content:'\f3cd'}.zmdi-airline-seat-legroom-normal:before{content:'\f3ce'}.zmdi-airline-seat-legroom-reduced:before{content:'\f3cf'}.zmdi-airline-seat-recline-extra:before{content:'\f3d0'}.zmdi-airline-seat-recline-normal:before{content:'\f3d1'}.zmdi-airplay:before{content:'\f3d2'}.zmdi-closed-caption:before{content:'\f3d3'}.zmdi-confirmation-number:before{content:'\f3d4'}.zmdi-developer-board:before{content:'\f3d5'}.zmdi-disc-full:before{content:'\f3d6'}.zmdi-explicit:before{content:'\f3d7'}.zmdi-flight-land:before{content:'\f3d8'}.zmdi-flight-takeoff:before{content:'\f3d9'}.zmdi-flip-to-back:before{content:'\f3da'}.zmdi-flip-to-front:before{content:'\f3db'}.zmdi-group-work:before{content:'\f3dc'}.zmdi-hd:before{content:'\f3dd'}.zmdi-hq:before{content:'\f3de'}.zmdi-markunread-mailbox:before{content:'\f3df'}.zmdi-memory:before{content:'\f3e0'}.zmdi-nfc:before{content:'\f3e1'}.zmdi-play-for-work:before{content:'\f3e2'}.zmdi-power-input:before{content:'\f3e3'}.zmdi-present-to-all:before{content:'\f3e4'}.zmdi-satellite:before{content:'\f3e5'}.zmdi-tap-and-play:before{content:'\f3e6'}.zmdi-vibration:before{content:'\f3e7'}.zmdi-voicemail:before{content:'\f3e8'}.zmdi-group:before{content:'\f3e9'}.zmdi-rss:before{content:'\f3ea'}.zmdi-shape:before{content:'\f3eb'}.zmdi-spinner:before{content:'\f3ec'}.zmdi-ungroup:before{content:'\f3ed'}.zmdi-500px:before{content:'\f3ee'}.zmdi-8tracks:before{content:'\f3ef'}.zmdi-amazon:before{content:'\f3f0'}.zmdi-blogger:before{content:'\f3f1'}.zmdi-delicious:before{content:'\f3f2'}.zmdi-disqus:before{content:'\f3f3'}.zmdi-flattr:before{content:'\f3f4'}.zmdi-flickr:before{content:'\f3f5'}.zmdi-github-alt:before{content:'\f3f6'}.zmdi-google-old:before{content:'\f3f7'}.zmdi-linkedin:before{content:'\f3f8'}.zmdi-odnoklassniki:before{content:'\f3f9'}.zmdi-outlook:before{content:'\f3fa'}.zmdi-paypal-alt:before{content:'\f3fb'}.zmdi-pinterest:before{content:'\f3fc'}.zmdi-playstation:before{content:'\f3fd'}.zmdi-reddit:before{content:'\f3fe'}.zmdi-skype:before{content:'\f3ff'}.zmdi-slideshare:before{content:'\f400'}.zmdi-soundcloud:before{content:'\f401'}.zmdi-tumblr:before{content:'\f402'}.zmdi-twitch:before{content:'\f403'}.zmdi-vimeo:before{content:'\f404'}.zmdi-whatsapp:before{content:'\f405'}.zmdi-xbox:before{content:'\f406'}.zmdi-yahoo:before{content:'\f407'}.zmdi-youtube-play:before{content:'\f408'}.zmdi-youtube:before{content:'\f409'}.zmdi-import-export:before{content:'\f30c'}.zmdi-swap-vertical-:before{content:'\f30c'}.zmdi-airplanemode-inactive:before{content:'\f102'}.zmdi-airplanemode-active:before{content:'\f103'}.zmdi-rate-review:before{content:'\f103'}.zmdi-comment-sign:before{content:'\f25a'}.zmdi-network-warning:before{content:'\f2ad'}.zmdi-shopping-cart-add:before{content:'\f1ca'}.zmdi-file-add:before{content:'\f221'}.zmdi-network-wifi-scan:before{content:'\f2e4'}.zmdi-collection-add:before{content:'\f14e'}.zmdi-format-playlist-add:before{content:'\f3ac'}.zmdi-format-queue-music:before{content:'\f3ab'}.zmdi-plus-box:before{content:'\f277'}.zmdi-tag-backspace:before{content:'\f1d9'}.zmdi-alarm-add:before{content:'\f32b'}.zmdi-battery-charging:before{content:'\f114'}.zmdi-daydream-setting:before{content:'\f217'}.zmdi-more-horiz:before{content:'\f19c'}.zmdi-book-photo:before{content:'\f11b'}.zmdi-incandescent:before{content:'\f189'}.zmdi-wb-iridescent:before{content:'\f38c'}.zmdi-calendar-remove:before{content:'\f330'}.zmdi-refresh-sync-disabled:before{content:'\f1b7'}.zmdi-refresh-sync-problem:before{content:'\f1b6'}.zmdi-crop-original:before{content:'\f17e'}.zmdi-power-off:before{content:'\f1af'}.zmdi-power-off-setting:before{content:'\f1ae'}.zmdi-leak-remove:before{content:'\f38d'}.zmdi-star-border:before{content:'\f27c'}.zmdi-brightness-low:before{content:'\f36d'}.zmdi-brightness-medium:before{content:'\f36e'}.zmdi-brightness-high:before{content:'\f36f'}.zmdi-smartphone-portrait:before{content:'\f2d4'}.zmdi-live-tv:before{content:'\f2d9'}.zmdi-format-textdirection-l-to-r:before{content:'\f249'}.zmdi-format-textdirection-r-to-l:before{content:'\f24a'}.zmdi-arrow-back:before{content:'\f2ea'}.zmdi-arrow-forward:before{content:'\f2ee'}.zmdi-arrow-in:before{content:'\f2e9'}.zmdi-arrow-out:before{content:'\f2ed'}.zmdi-rotate-90-degrees-ccw:before{content:'\f304'}.zmdi-adb:before{content:'\f33a'}.zmdi-network-wifi:before{content:'\f2e8'}.zmdi-network-wifi-alt:before{content:'\f2e3'}.zmdi-network-wifi-lock:before{content:'\f2e5'}.zmdi-network-wifi-off:before{content:'\f2e6'}.zmdi-network-wifi-outline:before{content:'\f2e7'}.zmdi-network-wifi-info:before{content:'\f2e4'}.zmdi-layers-clear:before{content:'\f18b'}.zmdi-colorize:before{content:'\f15d'}.zmdi-format-paint:before{content:'\f1ba'}.zmdi-format-quote:before{content:'\f1b2'}.zmdi-camera-monochrome-photos:before{content:'\f285'}.zmdi-sort-by-alpha:before{content:'\f1cf'}.zmdi-folder-shared:before{content:'\f225'}.zmdi-folder-special:before{content:'\f226'}.zmdi-comment-dots:before{content:'\f260'}.zmdi-reorder:before{content:'\f31e'}.zmdi-dehaze:before{content:'\f197'}.zmdi-sort:before{content:'\f1ce'}.zmdi-pages:before{content:'\f34a'}.zmdi-stack-overflow:before{content:'\f35c'}.zmdi-calendar-account:before{content:'\f204'}.zmdi-paste:before{content:'\f109'}.zmdi-cut:before{content:'\f1bc'}.zmdi-save:before{content:'\f297'}.zmdi-smartphone-code:before{content:'\f139'}.zmdi-directions-bike:before{content:'\f117'}.zmdi-directions-boat:before{content:'\f11a'}.zmdi-directions-bus:before{content:'\f121'}.zmdi-directions-car:before{content:'\f125'}.zmdi-directions-railway:before{content:'\f1b3'}.zmdi-directions-run:before{content:'\f215'}.zmdi-directions-subway:before{content:'\f1d5'}.zmdi-directions-walk:before{content:'\f216'}.zmdi-local-hotel:before{content:'\f178'}.zmdi-local-activity:before{content:'\f1df'}.zmdi-local-play:before{content:'\f1df'}.zmdi-local-airport:before{content:'\f103'}.zmdi-local-atm:before{content:'\f198'}.zmdi-local-bar:before{content:'\f137'}.zmdi-local-cafe:before{content:'\f13b'}.zmdi-local-car-wash:before{content:'\f124'}.zmdi-local-convenience-store:before{content:'\f1d3'}.zmdi-local-dining:before{content:'\f153'}.zmdi-local-drink:before{content:'\f157'}.zmdi-local-florist:before{content:'\f168'}.zmdi-local-gas-station:before{content:'\f16f'}.zmdi-local-grocery-store:before{content:'\f1cb'}.zmdi-local-hospital:before{content:'\f177'}.zmdi-local-laundry-service:before{content:'\f1e9'}.zmdi-local-library:before{content:'\f18d'}.zmdi-local-mall:before{content:'\f195'}.zmdi-local-movies:before{content:'\f19d'}.zmdi-local-offer:before{content:'\f187'}.zmdi-local-parking:before{content:'\f1a5'}.zmdi-local-parking:before{content:'\f1a5'}.zmdi-local-pharmacy:before{content:'\f176'}.zmdi-local-phone:before{content:'\f2be'}.zmdi-local-pizza:before{content:'\f1ac'}.zmdi-local-post-office:before{content:'\f15a'}.zmdi-local-printshop:before{content:'\f1b0'}.zmdi-local-see:before{content:'\f28c'}.zmdi-local-shipping:before{content:'\f1e6'}.zmdi-local-store:before{content:'\f1d4'}.zmdi-local-taxi:before{content:'\f123'}.zmdi-local-wc:before{content:'\f211'}.zmdi-my-location:before{content:'\f299'}.zmdi-directions:before{content:'\f1e7'} \ No newline at end of file diff --git a/dockers/web/html/colorlib-regform-16/fonts/material-icon/fonts/Material-Design-Iconic-Font.eot b/dockers/web/html/colorlib-regform-16/fonts/material-icon/fonts/Material-Design-Iconic-Font.eot new file mode 100755 index 0000000..5e25191 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/material-icon/fonts/Material-Design-Iconic-Font.eot differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/material-icon/fonts/Material-Design-Iconic-Font.svg b/dockers/web/html/colorlib-regform-16/fonts/material-icon/fonts/Material-Design-Iconic-Font.svg new file mode 100755 index 0000000..1d3d2ea --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/fonts/material-icon/fonts/Material-Design-Iconic-Font.svg @@ -0,0 +1,787 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dockers/web/html/colorlib-regform-16/fonts/material-icon/fonts/Material-Design-Iconic-Font.ttf b/dockers/web/html/colorlib-regform-16/fonts/material-icon/fonts/Material-Design-Iconic-Font.ttf new file mode 100755 index 0000000..5d489fd Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/material-icon/fonts/Material-Design-Iconic-Font.ttf differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/material-icon/fonts/Material-Design-Iconic-Font.woff b/dockers/web/html/colorlib-regform-16/fonts/material-icon/fonts/Material-Design-Iconic-Font.woff new file mode 100755 index 0000000..933b2bf Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/material-icon/fonts/Material-Design-Iconic-Font.woff differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/material-icon/fonts/Material-Design-Iconic-Font.woff2 b/dockers/web/html/colorlib-regform-16/fonts/material-icon/fonts/Material-Design-Iconic-Font.woff2 new file mode 100755 index 0000000..35970e2 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/material-icon/fonts/Material-Design-Iconic-Font.woff2 differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300.eot b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300.eot new file mode 100755 index 0000000..a34e08f Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300.eot differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300.svg b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300.svg new file mode 100755 index 0000000..bda92bb --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300.svg @@ -0,0 +1,320 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300.ttf b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300.ttf new file mode 100755 index 0000000..755cac6 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300.ttf differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300.woff b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300.woff new file mode 100755 index 0000000..9d48006 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300.woff differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300.woff2 b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300.woff2 new file mode 100755 index 0000000..ee94627 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300.woff2 differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300italic.eot b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300italic.eot new file mode 100755 index 0000000..1fc068d Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300italic.eot differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300italic.svg b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300italic.svg new file mode 100755 index 0000000..ddb13d9 --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300italic.svg @@ -0,0 +1,342 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300italic.ttf b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300italic.ttf new file mode 100755 index 0000000..2d422ea Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300italic.ttf differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300italic.woff b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300italic.woff new file mode 100755 index 0000000..7754dab Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300italic.woff differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300italic.woff2 b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300italic.woff2 new file mode 100755 index 0000000..074e36c Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-300italic.woff2 differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500.eot b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500.eot new file mode 100755 index 0000000..78f77dc Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500.eot differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500.svg b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500.svg new file mode 100755 index 0000000..2e9e363 --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500.svg @@ -0,0 +1,322 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500.ttf b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500.ttf new file mode 100755 index 0000000..c624d5e Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500.ttf differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500.woff b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500.woff new file mode 100755 index 0000000..decd7fe Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500.woff differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500.woff2 b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500.woff2 new file mode 100755 index 0000000..b57f095 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500.woff2 differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500italic.eot b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500italic.eot new file mode 100755 index 0000000..3e544e9 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500italic.eot differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500italic.svg b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500italic.svg new file mode 100755 index 0000000..d4c3c56 --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500italic.svg @@ -0,0 +1,341 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500italic.ttf b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500italic.ttf new file mode 100755 index 0000000..b2eb100 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500italic.ttf differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500italic.woff b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500italic.woff new file mode 100755 index 0000000..37995fa Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500italic.woff differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500italic.woff2 b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500italic.woff2 new file mode 100755 index 0000000..766570c Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-500italic.woff2 differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-600.eot b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-600.eot new file mode 100755 index 0000000..539a16b Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-600.eot differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-600.svg b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-600.svg new file mode 100755 index 0000000..ee34278 --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-600.svg @@ -0,0 +1,321 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-600.ttf b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-600.ttf new file mode 100755 index 0000000..eb25875 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-600.ttf differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-600.woff b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-600.woff new file mode 100755 index 0000000..b7f4279 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-600.woff differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-600.woff2 b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-600.woff2 new file mode 100755 index 0000000..b418923 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-600.woff2 differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700.eot b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700.eot new file mode 100755 index 0000000..5efd4c8 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700.eot differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700.svg b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700.svg new file mode 100755 index 0000000..6c4c4a5 --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700.svg @@ -0,0 +1,321 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700.ttf b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700.ttf new file mode 100755 index 0000000..9b6e988 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700.ttf differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700.woff b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700.woff new file mode 100755 index 0000000..ed5b743 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700.woff differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700.woff2 b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700.woff2 new file mode 100755 index 0000000..a2a876f Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700.woff2 differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700italic.eot b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700italic.eot new file mode 100755 index 0000000..82ae0a0 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700italic.eot differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700italic.svg b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700italic.svg new file mode 100755 index 0000000..7d45c96 --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700italic.svg @@ -0,0 +1,340 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700italic.ttf b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700italic.ttf new file mode 100755 index 0000000..549aba1 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700italic.ttf differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700italic.woff b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700italic.woff new file mode 100755 index 0000000..cd9dfe3 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700italic.woff differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700italic.woff2 b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700italic.woff2 new file mode 100755 index 0000000..7580d15 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-700italic.woff2 differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800.eot b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800.eot new file mode 100755 index 0000000..e9977cd Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800.eot differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800.svg b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800.svg new file mode 100755 index 0000000..29f08c8 --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800.svg @@ -0,0 +1,321 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800.ttf b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800.ttf new file mode 100755 index 0000000..6be78c0 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800.ttf differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800.woff b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800.woff new file mode 100755 index 0000000..53a3afa Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800.woff differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800.woff2 b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800.woff2 new file mode 100755 index 0000000..bd102a2 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800.woff2 differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800italic.eot b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800italic.eot new file mode 100755 index 0000000..8d796b3 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800italic.eot differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800italic.svg b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800italic.svg new file mode 100755 index 0000000..68446ce --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800italic.svg @@ -0,0 +1,341 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800italic.ttf b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800italic.ttf new file mode 100755 index 0000000..0acf306 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800italic.ttf differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800italic.woff b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800italic.woff new file mode 100755 index 0000000..092e40d Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800italic.woff differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800italic.woff2 b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800italic.woff2 new file mode 100755 index 0000000..4da0c36 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-800italic.woff2 differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-900.eot b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-900.eot new file mode 100755 index 0000000..2985e07 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-900.eot differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-900.svg b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-900.svg new file mode 100755 index 0000000..e981031 --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-900.svg @@ -0,0 +1,322 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-900.ttf b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-900.ttf new file mode 100755 index 0000000..243c472 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-900.ttf differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-900.woff b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-900.woff new file mode 100755 index 0000000..0e3f44f Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-900.woff differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-900.woff2 b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-900.woff2 new file mode 100755 index 0000000..4fa8946 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-900.woff2 differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-italic.eot b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-italic.eot new file mode 100755 index 0000000..94ea117 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-italic.eot differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-italic.svg b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-italic.svg new file mode 100755 index 0000000..e00cd94 --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-italic.svg @@ -0,0 +1,340 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-italic.ttf b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-italic.ttf new file mode 100755 index 0000000..6ec9da3 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-italic.ttf differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-italic.woff b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-italic.woff new file mode 100755 index 0000000..56c5ee4 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-italic.woff differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-italic.woff2 b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-italic.woff2 new file mode 100755 index 0000000..5ccb3e4 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-italic.woff2 differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-regular.eot b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-regular.eot new file mode 100755 index 0000000..ff32634 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-regular.eot differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-regular.svg b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-regular.svg new file mode 100755 index 0000000..c3d485c --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-regular.svg @@ -0,0 +1,323 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-regular.ttf b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-regular.ttf new file mode 100755 index 0000000..5f2caab Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-regular.ttf differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-regular.woff b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-regular.woff new file mode 100755 index 0000000..cfb9c3d Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-regular.woff differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-regular.woff2 b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-regular.woff2 new file mode 100755 index 0000000..19f70c6 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/poppins-v5-latin-regular.woff2 differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700.eot b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700.eot new file mode 100755 index 0000000..2e96810 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700.eot differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700.svg b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700.svg new file mode 100755 index 0000000..ab6491b --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700.svg @@ -0,0 +1,309 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700.ttf b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700.ttf new file mode 100755 index 0000000..c99c240 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700.ttf differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700.woff b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700.woff new file mode 100755 index 0000000..4aa4935 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700.woff differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700.woff2 b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700.woff2 new file mode 100755 index 0000000..ba3d4a4 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700.woff2 differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700italic.eot b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700italic.eot new file mode 100755 index 0000000..ff460fb Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700italic.eot differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700italic.svg b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700italic.svg new file mode 100755 index 0000000..fb2e82e --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700italic.svg @@ -0,0 +1,322 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700italic.ttf b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700italic.ttf new file mode 100755 index 0000000..17567f2 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700italic.ttf differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700italic.woff b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700italic.woff new file mode 100755 index 0000000..a778b54 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700italic.woff differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700italic.woff2 b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700italic.woff2 new file mode 100755 index 0000000..ddfaeac Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-700italic.woff2 differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-italic.eot b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-italic.eot new file mode 100755 index 0000000..64f0cd0 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-italic.eot differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-italic.svg b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-italic.svg new file mode 100755 index 0000000..7a01f8d --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-italic.svg @@ -0,0 +1,326 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-italic.ttf b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-italic.ttf new file mode 100755 index 0000000..c1d1377 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-italic.ttf differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-italic.woff b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-italic.woff new file mode 100755 index 0000000..1cd704c Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-italic.woff differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-italic.woff2 b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-italic.woff2 new file mode 100755 index 0000000..a19afd4 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-italic.woff2 differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-regular.eot b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-regular.eot new file mode 100755 index 0000000..ce6c273 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-regular.eot differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-regular.svg b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-regular.svg new file mode 100755 index 0000000..803c3b8 --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-regular.svg @@ -0,0 +1,306 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-regular.ttf b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-regular.ttf new file mode 100755 index 0000000..053201b Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-regular.ttf differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-regular.woff b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-regular.woff new file mode 100755 index 0000000..613f5de Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-regular.woff differ diff --git a/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-regular.woff2 b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-regular.woff2 new file mode 100755 index 0000000..e90965b Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/fonts/poppins/roboto-condensed-v16-latin-regular.woff2 differ diff --git a/dockers/web/html/colorlib-regform-16/images/form-img.jpg b/dockers/web/html/colorlib-regform-16/images/form-img.jpg new file mode 100755 index 0000000..98802c4 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/images/form-img.jpg differ diff --git a/dockers/web/html/colorlib-regform-16/images/form-img.jpg.kaz b/dockers/web/html/colorlib-regform-16/images/form-img.jpg.kaz new file mode 100755 index 0000000..929bf58 Binary files /dev/null and b/dockers/web/html/colorlib-regform-16/images/form-img.jpg.kaz differ diff --git a/dockers/web/html/colorlib-regform-16/index.html b/dockers/web/html/colorlib-regform-16/index.html new file mode 100755 index 0000000..070372d --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/index.html @@ -0,0 +1,139 @@ + + + + + + + Sign Up Form by Colorlib + + + + + + + + + + +
+ +
+ +
+ +
+ + + + + + + + + + diff --git a/dockers/web/html/colorlib-regform-16/js/main.js b/dockers/web/html/colorlib-regform-16/js/main.js new file mode 100755 index 0000000..42d6ff2 --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/js/main.js @@ -0,0 +1,80 @@ +(function($) { + + $('#meal_preference').parent().append('
    '); + $('#meal_preference option').each(function(){ + $('#newmeal_preference').append('
  • '+$(this).text()+'
  • '); + }); + $('#meal_preference').remove(); + $('#newmeal_preference').attr('id', 'meal_preference'); + $('#meal_preference li').first().addClass('init'); + $("#meal_preference").on("click", ".init", function() { + $(this).closest("#meal_preference").children('li:not(.init)').toggle(); + }); + + var allOptions = $("#meal_preference").children('li:not(.init)'); + $("#meal_preference").on("click", "li:not(.init)", function() { + allOptions.removeClass('selected'); + $(this).addClass('selected'); + $("#meal_preference").children('.init').html($(this).html()); + allOptions.toggle(); + }); + + var marginSlider = document.getElementById('slider-margin'); + if (marginSlider != undefined) { + noUiSlider.create(marginSlider, { + start: [500], + step: 10, + connect: [true, false], + tooltips: [true], + range: { + 'min': 0, + 'max': 1000 + }, + format: wNumb({ + decimals: 0, + thousand: ',', + prefix: '$ ', + }) + }); + } + $('#reset').on('click', function(){ + $('#register-form').reset(); + }); + + $('#register-form').validate({ + rules : { + first_name : { + required: true, + }, + last_name : { + required: true, + }, + company : { + required: true + }, + email : { + required: true, + email : true + }, + phone_number : { + required: true, + } + }, + onfocusout: function(element) { + $(element).valid(); + }, +}); + + jQuery.extend(jQuery.validator.messages, { + required: "", + remote: "", + email: "", + url: "", + date: "", + dateISO: "", + number: "", + digits: "", + creditcard: "", + equalTo: "" + }); +})(jQuery); \ No newline at end of file diff --git a/dockers/web/html/colorlib-regform-16/readme.txt b/dockers/web/html/colorlib-regform-16/readme.txt new file mode 100755 index 0000000..0ffa5e9 --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/readme.txt @@ -0,0 +1,2 @@ +This template was made by Colorlib (https://colorlib.com) +Please visit our website for more awesome templates, themes and tools. diff --git a/dockers/web/html/colorlib-regform-16/scss/common/extend.scss b/dockers/web/html/colorlib-regform-16/scss/common/extend.scss new file mode 100755 index 0000000..d18c0e0 --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/scss/common/extend.scss @@ -0,0 +1,11 @@ +/* @extend display-flex; */ +display-flex { + display: flex; + display: -webkit-flex; +} +/* @extend list-type-ulli; */ +list-type-ulli { + list-style-type: none; + margin: 0; + padding: 0; +} diff --git a/dockers/web/html/colorlib-regform-16/scss/common/fonts.scss b/dockers/web/html/colorlib-regform-16/scss/common/fonts.scss new file mode 100755 index 0000000..ec65bcb --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/scss/common/fonts.scss @@ -0,0 +1,156 @@ +/* poppins-300 - latin */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 300; + src: url('../fonts/poppins/poppins-v5-latin-300.eot'); /* IE9 Compat Modes */ + src: local('Poppins Light'), local('Poppins-Light'), + url('../fonts/poppins/poppins-v5-latin-300.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('../fonts/poppins/poppins-v5-latin-300.woff2') format('woff2'), /* Super Modern Browsers */ + url('../fonts/poppins/poppins-v5-latin-300.woff') format('woff'), /* Modern Browsers */ + url('../fonts/poppins/poppins-v5-latin-300.ttf') format('truetype'), /* Safari, Android, iOS */ + url('../fonts/poppins/poppins-v5-latin-300.svg#Poppins') format('svg'); /* Legacy iOS */ + } + /* poppins-300italic - latin */ + @font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 300; + src: url('../fonts/poppins/poppins-v5-latin-300italic.eot'); /* IE9 Compat Modes */ + src: local('Poppins Light Italic'), local('Poppins-LightItalic'), + url('../fonts/poppins/poppins-v5-latin-300italic.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('../fonts/poppins/poppins-v5-latin-300italic.woff2') format('woff2'), /* Super Modern Browsers */ + url('../fonts/poppins/poppins-v5-latin-300italic.woff') format('woff'), /* Modern Browsers */ + url('../fonts/poppins/poppins-v5-latin-300italic.ttf') format('truetype'), /* Safari, Android, iOS */ + url('../fonts/poppins/poppins-v5-latin-300italic.svg#Poppins') format('svg'); /* Legacy iOS */ + } + /* poppins-regular - latin */ + @font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 400; + src: url('../fonts/poppins/poppins-v5-latin-regular.eot'); /* IE9 Compat Modes */ + src: local('Poppins Regular'), local('Poppins-Regular'), + url('../fonts/poppins/poppins-v5-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('../fonts/poppins/poppins-v5-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */ + url('../fonts/poppins/poppins-v5-latin-regular.woff') format('woff'), /* Modern Browsers */ + url('../fonts/poppins/poppins-v5-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */ + url('../fonts/poppins/poppins-v5-latin-regular.svg#Poppins') format('svg'); /* Legacy iOS */ + } + /* poppins-italic - latin */ + @font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 400; + src: url('../fonts/poppins/poppins-v5-latin-italic.eot'); /* IE9 Compat Modes */ + src: local('Poppins Italic'), local('Poppins-Italic'), + url('../fonts/poppins/poppins-v5-latin-italic.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('../fonts/poppins/poppins-v5-latin-italic.woff2') format('woff2'), /* Super Modern Browsers */ + url('../fonts/poppins/poppins-v5-latin-italic.woff') format('woff'), /* Modern Browsers */ + url('../fonts/poppins/poppins-v5-latin-italic.ttf') format('truetype'), /* Safari, Android, iOS */ + url('../fonts/poppins/poppins-v5-latin-italic.svg#Poppins') format('svg'); /* Legacy iOS */ + } + /* poppins-500 - latin */ + @font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 500; + src: url('../fonts/poppins/poppins-v5-latin-500.eot'); /* IE9 Compat Modes */ + src: local('Poppins Medium'), local('Poppins-Medium'), + url('../fonts/poppins/poppins-v5-latin-500.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('../fonts/poppins/poppins-v5-latin-500.woff2') format('woff2'), /* Super Modern Browsers */ + url('../fonts/poppins/poppins-v5-latin-500.woff') format('woff'), /* Modern Browsers */ + url('../fonts/poppins/poppins-v5-latin-500.ttf') format('truetype'), /* Safari, Android, iOS */ + url('../fonts/poppins/poppins-v5-latin-500.svg#Poppins') format('svg'); /* Legacy iOS */ + } + /* poppins-500italic - latin */ + @font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 500; + src: url('../fonts/poppins/poppins-v5-latin-500italic.eot'); /* IE9 Compat Modes */ + src: local('Poppins Medium Italic'), local('Poppins-MediumItalic'), + url('../fonts/poppins/poppins-v5-latin-500italic.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('../fonts/poppins/poppins-v5-latin-500italic.woff2') format('woff2'), /* Super Modern Browsers */ + url('../fonts/poppins/poppins-v5-latin-500italic.woff') format('woff'), /* Modern Browsers */ + url('../fonts/poppins/poppins-v5-latin-500italic.ttf') format('truetype'), /* Safari, Android, iOS */ + url('../fonts/poppins/poppins-v5-latin-500italic.svg#Poppins') format('svg'); /* Legacy iOS */ + } + /* poppins-600 - latin */ + @font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 600; + src: url('../fonts/poppins/poppins-v5-latin-600.eot'); /* IE9 Compat Modes */ + src: local('Poppins SemiBold'), local('Poppins-SemiBold'), + url('../fonts/poppins/poppins-v5-latin-600.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('../fonts/poppins/poppins-v5-latin-600.woff2') format('woff2'), /* Super Modern Browsers */ + url('../fonts/poppins/poppins-v5-latin-600.woff') format('woff'), /* Modern Browsers */ + url('../fonts/poppins/poppins-v5-latin-600.ttf') format('truetype'), /* Safari, Android, iOS */ + url('../fonts/poppins/poppins-v5-latin-600.svg#Poppins') format('svg'); /* Legacy iOS */ + } + /* poppins-700 - latin */ + @font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 700; + src: url('../fonts/poppins/poppins-v5-latin-700.eot'); /* IE9 Compat Modes */ + src: local('Poppins Bold'), local('Poppins-Bold'), + url('../fonts/poppins/poppins-v5-latin-700.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('../fonts/poppins/poppins-v5-latin-700.woff2') format('woff2'), /* Super Modern Browsers */ + url('../fonts/poppins/poppins-v5-latin-700.woff') format('woff'), /* Modern Browsers */ + url('../fonts/poppins/poppins-v5-latin-700.ttf') format('truetype'), /* Safari, Android, iOS */ + url('../fonts/poppins/poppins-v5-latin-700.svg#Poppins') format('svg'); /* Legacy iOS */ + } + /* poppins-700italic - latin */ + @font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 700; + src: url('../fonts/poppins/poppins-v5-latin-700italic.eot'); /* IE9 Compat Modes */ + src: local('Poppins Bold Italic'), local('Poppins-BoldItalic'), + url('../fonts/poppins/poppins-v5-latin-700italic.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('../fonts/poppins/poppins-v5-latin-700italic.woff2') format('woff2'), /* Super Modern Browsers */ + url('../fonts/poppins/poppins-v5-latin-700italic.woff') format('woff'), /* Modern Browsers */ + url('../fonts/poppins/poppins-v5-latin-700italic.ttf') format('truetype'), /* Safari, Android, iOS */ + url('../fonts/poppins/poppins-v5-latin-700italic.svg#Poppins') format('svg'); /* Legacy iOS */ + } + /* poppins-800 - latin */ + @font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 800; + src: url('../fonts/poppins/poppins-v5-latin-800.eot'); /* IE9 Compat Modes */ + src: local('Poppins ExtraBold'), local('Poppins-ExtraBold'), + url('../fonts/poppins/poppins-v5-latin-800.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('../fonts/poppins/poppins-v5-latin-800.woff2') format('woff2'), /* Super Modern Browsers */ + url('../fonts/poppins/poppins-v5-latin-800.woff') format('woff'), /* Modern Browsers */ + url('../fonts/poppins/poppins-v5-latin-800.ttf') format('truetype'), /* Safari, Android, iOS */ + url('../fonts/poppins/poppins-v5-latin-800.svg#Poppins') format('svg'); /* Legacy iOS */ + } + /* poppins-800italic - latin */ + @font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 800; + src: url('../fonts/poppins/poppins-v5-latin-800italic.eot'); /* IE9 Compat Modes */ + src: local('Poppins ExtraBold Italic'), local('Poppins-ExtraBoldItalic'), + url('../fonts/poppins/poppins-v5-latin-800italic.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('../fonts/poppins/poppins-v5-latin-800italic.woff2') format('woff2'), /* Super Modern Browsers */ + url('../fonts/poppins/poppins-v5-latin-800italic.woff') format('woff'), /* Modern Browsers */ + url('../fonts/poppins/poppins-v5-latin-800italic.ttf') format('truetype'), /* Safari, Android, iOS */ + url('../fonts/poppins/poppins-v5-latin-800italic.svg#Poppins') format('svg'); /* Legacy iOS */ + } + /* poppins-900 - latin */ + @font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 900; + src: url('../fonts/poppins/poppins-v5-latin-900.eot'); /* IE9 Compat Modes */ + src: local('Poppins Black'), local('Poppins-Black'), + url('../fonts/poppins/poppins-v5-latin-900.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('../fonts/poppins/poppins-v5-latin-900.woff2') format('woff2'), /* Super Modern Browsers */ + url('../fonts/poppins/poppins-v5-latin-900.woff') format('woff'), /* Modern Browsers */ + url('../fonts/poppins/poppins-v5-latin-900.ttf') format('truetype'), /* Safari, Android, iOS */ + url('../fonts/poppins/poppins-v5-latin-900.svg#Poppins') format('svg'); /* Legacy iOS */ + } \ No newline at end of file diff --git a/dockers/web/html/colorlib-regform-16/scss/common/global.scss b/dockers/web/html/colorlib-regform-16/scss/common/global.scss new file mode 100755 index 0000000..fb0695b --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/scss/common/global.scss @@ -0,0 +1,52 @@ + a:focus, a:active { text-decoration: none; outline: none; @include transition(all 300ms ease 0s); } +input, select, textarea { outline: none; @include appearance( unset ); } +input::-webkit-outer-spin-button, input::-webkit-inner-spin-button { + @include appearance( none ); + margin: 0; +} +input:focus, select:focus, textarea:focus { outline: none; @include box-shadow-input(none); } +input[type=checkbox] { @include appearance( checkbox ); } +input[type=radio] { @include appearance( radio ); } +input:-webkit-autofill { + @include box-shadow(0 0 0 30px transparent inset); + background-color: transparent !important; +} +img { max-width: 100%; height: auto; } +figure { margin: 0; } +p { + margin: 0px; + font-weight: 600; + color: #fff; +} +h2 { + line-height: 1.2; + margin: 0; + padding: 0; + font-weight: 900; + color: #fff; + font-family: 'Poppins'; + font-size: 26px; + text-transform: uppercase; + margin-bottom: 10px; +} +.clear { clear: both; } +body { + font-size: 14px; + line-height: 1.8; + color: $black-color; + font-weight: 500; + font-family: 'Poppins'; + margin: 0px; + background: #282828; + padding: 50px 0; + // padding: 150px 0; +} +.main { + position: relative; +} +.container { + width: 1400px; + margin: 0 auto; + background: #fff; +} + diff --git a/dockers/web/html/colorlib-regform-16/scss/common/minxi.scss b/dockers/web/html/colorlib-regform-16/scss/common/minxi.scss new file mode 100755 index 0000000..79fdc09 --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/scss/common/minxi.scss @@ -0,0 +1,152 @@ +// mixin +@mixin border-radius($value) { + border-radius: $value; + -moz-border-radius: $value; + -webkit-border-radius: $value; + -o-border-radius: $value; + -ms-border-radius: $value; +} +@mixin background-size($value) { + background-size: $value; + -moz-background-size: $value; + -webkit-background-size: $value; + -o-background-size: $value; + -ms-background-size: $value; +} +@mixin background-gradient-three($color1, $color2, $color3) { + background: linear-gradient($color1, $color2, $color3); + background: -moz-linear-gradient($color1, $color2, $color3); + background: -webkit-linear-gradient($color1, $color2, $color3); + background: -o-linear-gradient($color1, $color2, $color3); + background: -ms-linear-gradient($color1, $color2, $color3); +} +@mixin background-gradient-six($color1, $color2, $color3, $color4, $color5, $color6) { + background: linear-gradient($color1, $color2, $color3, $color4, $color5, $color6); + background: -moz-linear-gradient($color1, $color2, $color3, $color4, $color5, $color6); + background: -webkit-linear-gradient($color1, $color2, $color3, $color4, $color5, $color6); + background: -o-linear-gradient($color1, $color2, $color3, $color4, $color5, $color6); + background: -ms-linear-gradient($color1, $color2, $color3, $color4, $color5, $color6); +} +@mixin transform($value) { + transform: $value; + -moz-transform: $value; + -webkit-transform: $value; + -o-transform: $value; + -ms-transform: $value; +} +@mixin box-shadow($value) { + box-shadow: $value; + -moz-box-shadow: $value; + -webkit-box-shadow: $value; + -o-box-shadow: $value; + -ms-box-shadow: $value; +} +@mixin filter($value) { + filter: $value; + -moz-filter: $value; + -webkit-filter: $value; + -o-filter: $value; + -ms-filter: $value; +} +@mixin transition($value) { + transition: $value; + -moz-transition: $value; + -webkit-transition: $value; + -o-transition: $value; + -ms-transition: $value; +} +@mixin animation($value) { + animation: $value; + -moz-animation: $value; + -webkit-animation: $value; + -o-animation: $value; + -ms-animation: $value; +} +@mixin transform-origin($value) { + transform-origin: $value; + -moz-transform-origin: $value; + -webkit-transform-origin: $value; + -o-transform-origin: $value; + -ms-transform-origin: $value; +} +@mixin appearance($value) { + appearance: $value !important; + -moz-appearance: $value !important; + -webkit-appearance: $value !important; + -o-appearance: $value !important; + -ms-appearance: $value !important; +} +@mixin box-shadow-input($value) { + box-shadow: $value !important; + -moz-box-shadow: $value !important; + -webkit-box-shadow: $value !important; + -o-box-shadow: $value !important; + -ms-box-shadow: $value !important; +} + +//flex +@mixin flex-wrap($value) { + flex-wrap: $value; + -moz-flex-wrap: $value; + -webkit-flex-wrap: $value; + -o-flex-wrap: $value; + -ms-flex-wrap: $value; +} +@mixin flex-direction($value) { + flex-direction: $value; + -moz-flex-direction: $value; + -webkit-flex-direction: $value; + -o-flex-direction: $value; + -ms-flex-direction: $value; +} +@mixin flex-flow($value) { + flex-flow: $value; + -moz-flex-flow: $value; + -webkit-flex-flow: $value; + -o-flex-flow: $value; + -ms-flex-flow: $value; +} +@mixin justify-content($value) { + justify-content: $value; + -moz-justify-content: $value; + -webkit-justify-content: $value; + -o-justify-content: $value; + -ms-justify-content: $value; +} +@mixin align-items($value) { + align-items: $value; + -moz-align-items: $value; + -webkit-align-items: $value; + -o-align-items: $value; + -ms-align-items: $value; +} +@mixin order($value) { + order: $value; + -moz-order: $value; + -webkit-order: $value; + -o-order: $value; + -ms-order: $value; +} +@mixin flex($value) { + flex: $value; + -moz-flex: $value; + -webkit-flex: $value; + -o-flex: $value; + -ms-flex: $value; +} + + +@mixin input-placeholder($value) { + &::-webkit-input-placeholder { + color: $value; + } + &::-moz-placeholder { + color: $value; + } + &:-ms-input-placeholder { + color: $value; + } + &:-moz-placeholder { + color: $value; + } +} \ No newline at end of file diff --git a/dockers/web/html/colorlib-regform-16/scss/common/variables.scss b/dockers/web/html/colorlib-regform-16/scss/common/variables.scss new file mode 100755 index 0000000..3c8de78 --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/scss/common/variables.scss @@ -0,0 +1,3 @@ +$green-color : #329e5e; +$border-color: #ebebeb; +$black-color: #222; \ No newline at end of file diff --git a/dockers/web/html/colorlib-regform-16/scss/layouts/main.scss b/dockers/web/html/colorlib-regform-16/scss/layouts/main.scss new file mode 100755 index 0000000..f4a5e9f --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/scss/layouts/main.scss @@ -0,0 +1,332 @@ +.signup-img { + position: relative; + width: 385px; + margin-bottom: -8px; +} +.signup-form { + width: 1015px; + margin-top: -2px; +} +.signup-img-content { + position: absolute; + top: 50%; + left: 50%; + @include transform(translate(-50%, -50%)); + text-align: center; + width: 100%; +} +.signup-content { + @extend display-flex; +} + +.register-form { + padding: 93px 115px 90px 80px; + margin-bottom: -8px; +} +.form-row { + @extend display-flex; + margin: 0 -30px; + .form-group { + width: 50%; + padding: 0 30px; + } +} +.form-input, .form-select, .form-radio { + margin-bottom: 23px; +} +label, input { + display: block; + width: 100%; +} +label { + font-weight: bold; + text-transform: uppercase; + margin-bottom: 7px; +} +label.required { + position: relative; + &:after { + content: '*'; + margin-left: 2px; + color: #b90000; + + } +} +input { + box-sizing: border-box; + border: 1px solid $border-color; + padding: 14px 20px; + @include border-radius(5px); + font-size: 14px; + font-family: 'Poppins'; + &:focus { + border: 1px solid $green-color; + } +} + +.label-flex { + @extend display-flex; + @include justify-content(space-between); + + label { + width: auto; + } +} +.form-link { + font-size: 12px; + color: $black-color; + text-decoration: none; + position: relative; + &:after { + position: absolute; + content: ""; + width: 100%; + height: 2px; + background: #d7d7d7; + left: 0; + bottom: 12px; + } +} + +.form-radio { + // @extend display-flex; + margin-bottom: 18px; + input { + width: auto; + display: inline-block; + } +} +.radio-label { + padding-right: 72px; +} +.form-radio-group { + @extend display-flex; + padding-bottom: 10px; + padding-top: 12px; +} +.form-radio-item { + position: relative; + margin-right: 30px; + label { + font-weight: 500; + padding-left: 30px; + position: relative; + z-index: 9; + display: block; + cursor: pointer; + text-transform: none; + } +} + +.check{ + display: inline-block; + position: absolute; + border: 1px solid $border-color; + @include border-radius(50%); + height: 18px; + width: 18px; + top: 2px; + left: 0px; + z-index: 5; + transition: border .25s linear; + -webkit-transition: border .25s linear; + &:before { + position: absolute; + display: block; + content: ''; + width: 12px; + height: 12px; + @include border-radius(50%); + top: 3px; + left: 3px; + margin: auto; + transition: background 0.25s linear; + -webkit-transition: background 0.25s linear; + } +} +input[type=radio] { + position: absolute; + visibility: hidden; + &:checked ~ .check { + border: 1px solid $green-color; + } + &:checked ~ .check::before { + background: $green-color; + } +} +ul { + @extend list-type-ulli; + background: 0 0; + position: relative; + z-index: 9; + border: 1px solid $border-color; + @include border-radius(5px); +} +ul li { + padding: 13px 20px; + z-index: 2; + color: $black-color; +} +ul li:not(.init) { + display: none; + background: #fff; + color: $black-color; + padding: 5px 10px; +} +ul li:not(.init):hover, ul li.selected:not(.init) { + background: $green-color; + color: #fff; +} +li.init { + cursor: pointer; + position: relative; + &:after { + position: absolute; + right: 20px; + top: 50%; + @include transform(translateY(-50%)); + font-size: 22px; + color: $black-color; + font-family: 'Material-Design-Iconic-Font'; + content: '\f2f2'; + } +} + +#slider-margin { + height: 5px; + border: none; + @include box-shadow(none); + background: #f8f8f8; + @include border-radius(2.5px); + .noUi-connect { + background: $green-color; + } + .noUi-handle { + width: 100px; + height: 30px; + top: -12px; + background: $green-color; + @include box-shadow(0px 3px 2.85px 0.15px rgba(0, 0, 0, 0.1)); + @include border-radius(5px); + outline: none; + border: none; + // left: 50%; + // @include transform(translateX(-50%)); + // right: auto; + right: -50px; + &:after, &:before { + background: transparent; + height: 0px; + width: 20px; + top: -2px; + font-size: 18px; + outline: none; + } + &:before { + font-family: 'Material-Design-Iconic-Font'; + color: #fff; + content: '\f2fa'; + left: 10px; + } + &:after { + font-family: 'Material-Design-Iconic-Font'; + color: #fff; + content: '\f2fb'; + left: auto; + right: -5px; + } + .noUi-tooltip { + bottom: 2px; + border: none; + background: transparent; + font-weight: bold; + color: #fff; + padding: 0px; + } + } +} +.donate-us { + margin-bottom: 93px; + margin-top: 30px; + label { + margin-bottom: 32px; + } +} +.price_slider { + position: relative; +} +.donate-value { + position: absolute; + top: -9px; + left: 50%; + z-index: 99; + @include transform(translateX(-50%)); +} +.form-submit { + text-align: right; +} +.submit { + width: 150px; + height: 50px; + display: inline-block; + font-family: 'Poppins'; + font-weight: bold; + font-size: 14px; + padding: 10px; + border: none; + cursor: pointer; + text-transform: uppercase; + @include border-radius(5px); +} +#reset { + background: #fff; + color: #999; + + border: 2px solid $border-color; + &:hover { + border: 2px solid $green-color; + background: $green-color; + color: #fff; + } +} +#submit { + background: $green-color; + color: #fff; + margin-right: 25px; + &:hover { + background-color: darken($green-color, 10%) + } +} +.form-input { + position: relative; +} +label.error { + display: block; + position: absolute; + top: 0px; + right: 0; + &:after { + font-family: 'Material-Design-Iconic-Font'; + position: absolute; + content: '\f1f8'; + right: 20px; + top: 37px; + font-size: 23px; + color: #c70000; + } +} +input.error { + border: 1px solid #c70000; +} +.select-list { + position: relative; + display: inline-block; + width: 100%; + margin-bottom: 47px; +} +.list-item { + position: absolute; + width: 100%; + z-index: 99; +} + \ No newline at end of file diff --git a/dockers/web/html/colorlib-regform-16/scss/layouts/responsive.scss b/dockers/web/html/colorlib-regform-16/scss/layouts/responsive.scss new file mode 100755 index 0000000..43c1d7e --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/scss/layouts/responsive.scss @@ -0,0 +1,55 @@ +@media screen and (max-width: 1024px) { + .container { + width: calc( 100% - 30px); + max-width: 100%; + margin: 0 auto; + } + .signup-img { + display: none; + } + .signup-form { + width: 100%; + } + .register-form { + padding: 93px 80px 90px 80px; + } +} + +@media screen and (max-width: 992px) { + + .signup-content { + width: 100%; + } + .form-radio-item { + margin-right: 15px; + } + .register-form { + padding: 93px 30px 90px 30px; + } +} + +@media screen and (max-width: 768px) { + .form-row { + @include flex-direction(column); + margin: 0px; + } + .form-row .form-group { + width: 100%; + padding: 0 0px; + } +} + + +//end 480px +@media screen and (max-width: 480px) { + .submit { + width: 100%; + } + #submit { + margin-bottom: 20px; + margin-right: 0px; + } + .form-radio-group { + @include flex-direction(column); + } +} \ No newline at end of file diff --git a/dockers/web/html/colorlib-regform-16/scss/style.scss b/dockers/web/html/colorlib-regform-16/scss/style.scss new file mode 100755 index 0000000..5002405 --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/scss/style.scss @@ -0,0 +1,12 @@ +//Common +@import 'common/extend.scss'; +@import 'common/fonts.scss'; +@import 'common/minxi.scss'; +@import 'common/variables.scss'; +@import 'common/global.scss'; + +//layout +@import 'layouts/main.scss'; + +// Responsive +@import 'layouts/responsive.scss'; \ No newline at end of file diff --git a/dockers/web/html/colorlib-regform-16/vendor/jquery-validation/dist/additional-methods.js b/dockers/web/html/colorlib-regform-16/vendor/jquery-validation/dist/additional-methods.js new file mode 100755 index 0000000..187b90d --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/vendor/jquery-validation/dist/additional-methods.js @@ -0,0 +1,1158 @@ +/*! + * jQuery Validation Plugin v1.17.0 + * + * https://jqueryvalidation.org/ + * + * Copyright (c) 2017 Jörn Zaefferer + * Released under the MIT license + */ +(function( factory ) { + if ( typeof define === "function" && define.amd ) { + define( ["jquery", "./jquery.validate"], factory ); + } else if (typeof module === "object" && module.exports) { + module.exports = factory( require( "jquery" ) ); + } else { + factory( jQuery ); + } +}(function( $ ) { + +( function() { + + function stripHtml( value ) { + + // Remove html tags and space chars + return value.replace( /<.[^<>]*?>/g, " " ).replace( / | /gi, " " ) + + // Remove punctuation + .replace( /[.(),;:!?%#$'\"_+=\/\-“”’]*/g, "" ); + } + + $.validator.addMethod( "maxWords", function( value, element, params ) { + return this.optional( element ) || stripHtml( value ).match( /\b\w+\b/g ).length <= params; + }, $.validator.format( "Please enter {0} words or less." ) ); + + $.validator.addMethod( "minWords", function( value, element, params ) { + return this.optional( element ) || stripHtml( value ).match( /\b\w+\b/g ).length >= params; + }, $.validator.format( "Please enter at least {0} words." ) ); + + $.validator.addMethod( "rangeWords", function( value, element, params ) { + var valueStripped = stripHtml( value ), + regex = /\b\w+\b/g; + return this.optional( element ) || valueStripped.match( regex ).length >= params[ 0 ] && valueStripped.match( regex ).length <= params[ 1 ]; + }, $.validator.format( "Please enter between {0} and {1} words." ) ); + +}() ); + +// Accept a value from a file input based on a required mimetype +$.validator.addMethod( "accept", function( value, element, param ) { + + // Split mime on commas in case we have multiple types we can accept + var typeParam = typeof param === "string" ? param.replace( /\s/g, "" ) : "image/*", + optionalValue = this.optional( element ), + i, file, regex; + + // Element is optional + if ( optionalValue ) { + return optionalValue; + } + + if ( $( element ).attr( "type" ) === "file" ) { + + // Escape string to be used in the regex + // see: https://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex + // Escape also "/*" as "/.*" as a wildcard + typeParam = typeParam + .replace( /[\-\[\]\/\{\}\(\)\+\?\.\\\^\$\|]/g, "\\$&" ) + .replace( /,/g, "|" ) + .replace( /\/\*/g, "/.*" ); + + // Check if the element has a FileList before checking each file + if ( element.files && element.files.length ) { + regex = new RegExp( ".?(" + typeParam + ")$", "i" ); + for ( i = 0; i < element.files.length; i++ ) { + file = element.files[ i ]; + + // Grab the mimetype from the loaded file, verify it matches + if ( !file.type.match( regex ) ) { + return false; + } + } + } + } + + // Either return true because we've validated each file, or because the + // browser does not support element.files and the FileList feature + return true; +}, $.validator.format( "Please enter a value with a valid mimetype." ) ); + +$.validator.addMethod( "alphanumeric", function( value, element ) { + return this.optional( element ) || /^\w+$/i.test( value ); +}, "Letters, numbers, and underscores only please" ); + +/* + * Dutch bank account numbers (not 'giro' numbers) have 9 digits + * and pass the '11 check'. + * We accept the notation with spaces, as that is common. + * acceptable: 123456789 or 12 34 56 789 + */ +$.validator.addMethod( "bankaccountNL", function( value, element ) { + if ( this.optional( element ) ) { + return true; + } + if ( !( /^[0-9]{9}|([0-9]{2} ){3}[0-9]{3}$/.test( value ) ) ) { + return false; + } + + // Now '11 check' + var account = value.replace( / /g, "" ), // Remove spaces + sum = 0, + len = account.length, + pos, factor, digit; + for ( pos = 0; pos < len; pos++ ) { + factor = len - pos; + digit = account.substring( pos, pos + 1 ); + sum = sum + factor * digit; + } + return sum % 11 === 0; +}, "Please specify a valid bank account number" ); + +$.validator.addMethod( "bankorgiroaccountNL", function( value, element ) { + return this.optional( element ) || + ( $.validator.methods.bankaccountNL.call( this, value, element ) ) || + ( $.validator.methods.giroaccountNL.call( this, value, element ) ); +}, "Please specify a valid bank or giro account number" ); + +/** + * BIC is the business identifier code (ISO 9362). This BIC check is not a guarantee for authenticity. + * + * BIC pattern: BBBBCCLLbbb (8 or 11 characters long; bbb is optional) + * + * Validation is case-insensitive. Please make sure to normalize input yourself. + * + * BIC definition in detail: + * - First 4 characters - bank code (only letters) + * - Next 2 characters - ISO 3166-1 alpha-2 country code (only letters) + * - Next 2 characters - location code (letters and digits) + * a. shall not start with '0' or '1' + * b. second character must be a letter ('O' is not allowed) or digit ('0' for test (therefore not allowed), '1' denoting passive participant, '2' typically reverse-billing) + * - Last 3 characters - branch code, optional (shall not start with 'X' except in case of 'XXX' for primary office) (letters and digits) + */ +$.validator.addMethod( "bic", function( value, element ) { + return this.optional( element ) || /^([A-Z]{6}[A-Z2-9][A-NP-Z1-9])(X{3}|[A-WY-Z0-9][A-Z0-9]{2})?$/.test( value.toUpperCase() ); +}, "Please specify a valid BIC code" ); + +/* + * Código de identificación fiscal ( CIF ) is the tax identification code for Spanish legal entities + * Further rules can be found in Spanish on http://es.wikipedia.org/wiki/C%C3%B3digo_de_identificaci%C3%B3n_fiscal + * + * Spanish CIF structure: + * + * [ T ][ P ][ P ][ N ][ N ][ N ][ N ][ N ][ C ] + * + * Where: + * + * T: 1 character. Kind of Organization Letter: [ABCDEFGHJKLMNPQRSUVW] + * P: 2 characters. Province. + * N: 5 characters. Secuencial Number within the province. + * C: 1 character. Control Digit: [0-9A-J]. + * + * [ T ]: Kind of Organizations. Possible values: + * + * A. Corporations + * B. LLCs + * C. General partnerships + * D. Companies limited partnerships + * E. Communities of goods + * F. Cooperative Societies + * G. Associations + * H. Communities of homeowners in horizontal property regime + * J. Civil Societies + * K. Old format + * L. Old format + * M. Old format + * N. Nonresident entities + * P. Local authorities + * Q. Autonomous bodies, state or not, and the like, and congregations and religious institutions + * R. Congregations and religious institutions (since 2008 ORDER EHA/451/2008) + * S. Organs of State Administration and regions + * V. Agrarian Transformation + * W. Permanent establishments of non-resident in Spain + * + * [ C ]: Control Digit. It can be a number or a letter depending on T value: + * [ T ] --> [ C ] + * ------ ---------- + * A Number + * B Number + * E Number + * H Number + * K Letter + * P Letter + * Q Letter + * S Letter + * + */ +$.validator.addMethod( "cifES", function( value, element ) { + "use strict"; + + if ( this.optional( element ) ) { + return true; + } + + var cifRegEx = new RegExp( /^([ABCDEFGHJKLMNPQRSUVW])(\d{7})([0-9A-J])$/gi ); + var letter = value.substring( 0, 1 ), // [ T ] + number = value.substring( 1, 8 ), // [ P ][ P ][ N ][ N ][ N ][ N ][ N ] + control = value.substring( 8, 9 ), // [ C ] + all_sum = 0, + even_sum = 0, + odd_sum = 0, + i, n, + control_digit, + control_letter; + + function isOdd( n ) { + return n % 2 === 0; + } + + // Quick format test + if ( value.length !== 9 || !cifRegEx.test( value ) ) { + return false; + } + + for ( i = 0; i < number.length; i++ ) { + n = parseInt( number[ i ], 10 ); + + // Odd positions + if ( isOdd( i ) ) { + + // Odd positions are multiplied first. + n *= 2; + + // If the multiplication is bigger than 10 we need to adjust + odd_sum += n < 10 ? n : n - 9; + + // Even positions + // Just sum them + } else { + even_sum += n; + } + } + + all_sum = even_sum + odd_sum; + control_digit = ( 10 - ( all_sum ).toString().substr( -1 ) ).toString(); + control_digit = parseInt( control_digit, 10 ) > 9 ? "0" : control_digit; + control_letter = "JABCDEFGHI".substr( control_digit, 1 ).toString(); + + // Control must be a digit + if ( letter.match( /[ABEH]/ ) ) { + return control === control_digit; + + // Control must be a letter + } else if ( letter.match( /[KPQS]/ ) ) { + return control === control_letter; + } + + // Can be either + return control === control_digit || control === control_letter; + +}, "Please specify a valid CIF number." ); + +/* + * Brazillian CPF number (Cadastrado de Pessoas Físicas) is the equivalent of a Brazilian tax registration number. + * CPF numbers have 11 digits in total: 9 numbers followed by 2 check numbers that are being used for validation. + */ +$.validator.addMethod( "cpfBR", function( value ) { + + // Removing special characters from value + value = value.replace( /([~!@#$%^&*()_+=`{}\[\]\-|\\:;'<>,.\/? ])+/g, "" ); + + // Checking value to have 11 digits only + if ( value.length !== 11 ) { + return false; + } + + var sum = 0, + firstCN, secondCN, checkResult, i; + + firstCN = parseInt( value.substring( 9, 10 ), 10 ); + secondCN = parseInt( value.substring( 10, 11 ), 10 ); + + checkResult = function( sum, cn ) { + var result = ( sum * 10 ) % 11; + if ( ( result === 10 ) || ( result === 11 ) ) { + result = 0; + } + return ( result === cn ); + }; + + // Checking for dump data + if ( value === "" || + value === "00000000000" || + value === "11111111111" || + value === "22222222222" || + value === "33333333333" || + value === "44444444444" || + value === "55555555555" || + value === "66666666666" || + value === "77777777777" || + value === "88888888888" || + value === "99999999999" + ) { + return false; + } + + // Step 1 - using first Check Number: + for ( i = 1; i <= 9; i++ ) { + sum = sum + parseInt( value.substring( i - 1, i ), 10 ) * ( 11 - i ); + } + + // If first Check Number (CN) is valid, move to Step 2 - using second Check Number: + if ( checkResult( sum, firstCN ) ) { + sum = 0; + for ( i = 1; i <= 10; i++ ) { + sum = sum + parseInt( value.substring( i - 1, i ), 10 ) * ( 12 - i ); + } + return checkResult( sum, secondCN ); + } + return false; + +}, "Please specify a valid CPF number" ); + +// https://jqueryvalidation.org/creditcard-method/ +// based on https://en.wikipedia.org/wiki/Luhn_algorithm +$.validator.addMethod( "creditcard", function( value, element ) { + if ( this.optional( element ) ) { + return "dependency-mismatch"; + } + + // Accept only spaces, digits and dashes + if ( /[^0-9 \-]+/.test( value ) ) { + return false; + } + + var nCheck = 0, + nDigit = 0, + bEven = false, + n, cDigit; + + value = value.replace( /\D/g, "" ); + + // Basing min and max length on + // https://developer.ean.com/general_info/Valid_Credit_Card_Types + if ( value.length < 13 || value.length > 19 ) { + return false; + } + + for ( n = value.length - 1; n >= 0; n-- ) { + cDigit = value.charAt( n ); + nDigit = parseInt( cDigit, 10 ); + if ( bEven ) { + if ( ( nDigit *= 2 ) > 9 ) { + nDigit -= 9; + } + } + + nCheck += nDigit; + bEven = !bEven; + } + + return ( nCheck % 10 ) === 0; +}, "Please enter a valid credit card number." ); + +/* NOTICE: Modified version of Castle.Components.Validator.CreditCardValidator + * Redistributed under the the Apache License 2.0 at http://www.apache.org/licenses/LICENSE-2.0 + * Valid Types: mastercard, visa, amex, dinersclub, enroute, discover, jcb, unknown, all (overrides all other settings) + */ +$.validator.addMethod( "creditcardtypes", function( value, element, param ) { + if ( /[^0-9\-]+/.test( value ) ) { + return false; + } + + value = value.replace( /\D/g, "" ); + + var validTypes = 0x0000; + + if ( param.mastercard ) { + validTypes |= 0x0001; + } + if ( param.visa ) { + validTypes |= 0x0002; + } + if ( param.amex ) { + validTypes |= 0x0004; + } + if ( param.dinersclub ) { + validTypes |= 0x0008; + } + if ( param.enroute ) { + validTypes |= 0x0010; + } + if ( param.discover ) { + validTypes |= 0x0020; + } + if ( param.jcb ) { + validTypes |= 0x0040; + } + if ( param.unknown ) { + validTypes |= 0x0080; + } + if ( param.all ) { + validTypes = 0x0001 | 0x0002 | 0x0004 | 0x0008 | 0x0010 | 0x0020 | 0x0040 | 0x0080; + } + if ( validTypes & 0x0001 && /^(5[12345])/.test( value ) ) { // Mastercard + return value.length === 16; + } + if ( validTypes & 0x0002 && /^(4)/.test( value ) ) { // Visa + return value.length === 16; + } + if ( validTypes & 0x0004 && /^(3[47])/.test( value ) ) { // Amex + return value.length === 15; + } + if ( validTypes & 0x0008 && /^(3(0[012345]|[68]))/.test( value ) ) { // Dinersclub + return value.length === 14; + } + if ( validTypes & 0x0010 && /^(2(014|149))/.test( value ) ) { // Enroute + return value.length === 15; + } + if ( validTypes & 0x0020 && /^(6011)/.test( value ) ) { // Discover + return value.length === 16; + } + if ( validTypes & 0x0040 && /^(3)/.test( value ) ) { // Jcb + return value.length === 16; + } + if ( validTypes & 0x0040 && /^(2131|1800)/.test( value ) ) { // Jcb + return value.length === 15; + } + if ( validTypes & 0x0080 ) { // Unknown + return true; + } + return false; +}, "Please enter a valid credit card number." ); + +/** + * Validates currencies with any given symbols by @jameslouiz + * Symbols can be optional or required. Symbols required by default + * + * Usage examples: + * currency: ["£", false] - Use false for soft currency validation + * currency: ["$", false] + * currency: ["RM", false] - also works with text based symbols such as "RM" - Malaysia Ringgit etc + * + * + * + * Soft symbol checking + * currencyInput: { + * currency: ["$", false] + * } + * + * Strict symbol checking (default) + * currencyInput: { + * currency: "$" + * //OR + * currency: ["$", true] + * } + * + * Multiple Symbols + * currencyInput: { + * currency: "$,£,¢" + * } + */ +$.validator.addMethod( "currency", function( value, element, param ) { + var isParamString = typeof param === "string", + symbol = isParamString ? param : param[ 0 ], + soft = isParamString ? true : param[ 1 ], + regex; + + symbol = symbol.replace( /,/g, "" ); + symbol = soft ? symbol + "]" : symbol + "]?"; + regex = "^[" + symbol + "([1-9]{1}[0-9]{0,2}(\\,[0-9]{3})*(\\.[0-9]{0,2})?|[1-9]{1}[0-9]{0,}(\\.[0-9]{0,2})?|0(\\.[0-9]{0,2})?|(\\.[0-9]{1,2})?)$"; + regex = new RegExp( regex ); + return this.optional( element ) || regex.test( value ); + +}, "Please specify a valid currency" ); + +$.validator.addMethod( "dateFA", function( value, element ) { + return this.optional( element ) || /^[1-4]\d{3}\/((0?[1-6]\/((3[0-1])|([1-2][0-9])|(0?[1-9])))|((1[0-2]|(0?[7-9]))\/(30|([1-2][0-9])|(0?[1-9]))))$/.test( value ); +}, $.validator.messages.date ); + +/** + * Return true, if the value is a valid date, also making this formal check dd/mm/yyyy. + * + * @example $.validator.methods.date("01/01/1900") + * @result true + * + * @example $.validator.methods.date("01/13/1990") + * @result false + * + * @example $.validator.methods.date("01.01.1900") + * @result false + * + * @example + * @desc Declares an optional input element whose value must be a valid date. + * + * @name $.validator.methods.dateITA + * @type Boolean + * @cat Plugins/Validate/Methods + */ +$.validator.addMethod( "dateITA", function( value, element ) { + var check = false, + re = /^\d{1,2}\/\d{1,2}\/\d{4}$/, + adata, gg, mm, aaaa, xdata; + if ( re.test( value ) ) { + adata = value.split( "/" ); + gg = parseInt( adata[ 0 ], 10 ); + mm = parseInt( adata[ 1 ], 10 ); + aaaa = parseInt( adata[ 2 ], 10 ); + xdata = new Date( Date.UTC( aaaa, mm - 1, gg, 12, 0, 0, 0 ) ); + if ( ( xdata.getUTCFullYear() === aaaa ) && ( xdata.getUTCMonth() === mm - 1 ) && ( xdata.getUTCDate() === gg ) ) { + check = true; + } else { + check = false; + } + } else { + check = false; + } + return this.optional( element ) || check; +}, $.validator.messages.date ); + +$.validator.addMethod( "dateNL", function( value, element ) { + return this.optional( element ) || /^(0?[1-9]|[12]\d|3[01])[\.\/\-](0?[1-9]|1[012])[\.\/\-]([12]\d)?(\d\d)$/.test( value ); +}, $.validator.messages.date ); + +// Older "accept" file extension method. Old docs: http://docs.jquery.com/Plugins/Validation/Methods/accept +$.validator.addMethod( "extension", function( value, element, param ) { + param = typeof param === "string" ? param.replace( /,/g, "|" ) : "png|jpe?g|gif"; + return this.optional( element ) || value.match( new RegExp( "\\.(" + param + ")$", "i" ) ); +}, $.validator.format( "Please enter a value with a valid extension." ) ); + +/** + * Dutch giro account numbers (not bank numbers) have max 7 digits + */ +$.validator.addMethod( "giroaccountNL", function( value, element ) { + return this.optional( element ) || /^[0-9]{1,7}$/.test( value ); +}, "Please specify a valid giro account number" ); + +/** + * IBAN is the international bank account number. + * It has a country - specific format, that is checked here too + * + * Validation is case-insensitive. Please make sure to normalize input yourself. + */ +$.validator.addMethod( "iban", function( value, element ) { + + // Some quick simple tests to prevent needless work + if ( this.optional( element ) ) { + return true; + } + + // Remove spaces and to upper case + var iban = value.replace( / /g, "" ).toUpperCase(), + ibancheckdigits = "", + leadingZeroes = true, + cRest = "", + cOperator = "", + countrycode, ibancheck, charAt, cChar, bbanpattern, bbancountrypatterns, ibanregexp, i, p; + + // Check for IBAN code length. + // It contains: + // country code ISO 3166-1 - two letters, + // two check digits, + // Basic Bank Account Number (BBAN) - up to 30 chars + var minimalIBANlength = 5; + if ( iban.length < minimalIBANlength ) { + return false; + } + + // Check the country code and find the country specific format + countrycode = iban.substring( 0, 2 ); + bbancountrypatterns = { + "AL": "\\d{8}[\\dA-Z]{16}", + "AD": "\\d{8}[\\dA-Z]{12}", + "AT": "\\d{16}", + "AZ": "[\\dA-Z]{4}\\d{20}", + "BE": "\\d{12}", + "BH": "[A-Z]{4}[\\dA-Z]{14}", + "BA": "\\d{16}", + "BR": "\\d{23}[A-Z][\\dA-Z]", + "BG": "[A-Z]{4}\\d{6}[\\dA-Z]{8}", + "CR": "\\d{17}", + "HR": "\\d{17}", + "CY": "\\d{8}[\\dA-Z]{16}", + "CZ": "\\d{20}", + "DK": "\\d{14}", + "DO": "[A-Z]{4}\\d{20}", + "EE": "\\d{16}", + "FO": "\\d{14}", + "FI": "\\d{14}", + "FR": "\\d{10}[\\dA-Z]{11}\\d{2}", + "GE": "[\\dA-Z]{2}\\d{16}", + "DE": "\\d{18}", + "GI": "[A-Z]{4}[\\dA-Z]{15}", + "GR": "\\d{7}[\\dA-Z]{16}", + "GL": "\\d{14}", + "GT": "[\\dA-Z]{4}[\\dA-Z]{20}", + "HU": "\\d{24}", + "IS": "\\d{22}", + "IE": "[\\dA-Z]{4}\\d{14}", + "IL": "\\d{19}", + "IT": "[A-Z]\\d{10}[\\dA-Z]{12}", + "KZ": "\\d{3}[\\dA-Z]{13}", + "KW": "[A-Z]{4}[\\dA-Z]{22}", + "LV": "[A-Z]{4}[\\dA-Z]{13}", + "LB": "\\d{4}[\\dA-Z]{20}", + "LI": "\\d{5}[\\dA-Z]{12}", + "LT": "\\d{16}", + "LU": "\\d{3}[\\dA-Z]{13}", + "MK": "\\d{3}[\\dA-Z]{10}\\d{2}", + "MT": "[A-Z]{4}\\d{5}[\\dA-Z]{18}", + "MR": "\\d{23}", + "MU": "[A-Z]{4}\\d{19}[A-Z]{3}", + "MC": "\\d{10}[\\dA-Z]{11}\\d{2}", + "MD": "[\\dA-Z]{2}\\d{18}", + "ME": "\\d{18}", + "NL": "[A-Z]{4}\\d{10}", + "NO": "\\d{11}", + "PK": "[\\dA-Z]{4}\\d{16}", + "PS": "[\\dA-Z]{4}\\d{21}", + "PL": "\\d{24}", + "PT": "\\d{21}", + "RO": "[A-Z]{4}[\\dA-Z]{16}", + "SM": "[A-Z]\\d{10}[\\dA-Z]{12}", + "SA": "\\d{2}[\\dA-Z]{18}", + "RS": "\\d{18}", + "SK": "\\d{20}", + "SI": "\\d{15}", + "ES": "\\d{20}", + "SE": "\\d{20}", + "CH": "\\d{5}[\\dA-Z]{12}", + "TN": "\\d{20}", + "TR": "\\d{5}[\\dA-Z]{17}", + "AE": "\\d{3}\\d{16}", + "GB": "[A-Z]{4}\\d{14}", + "VG": "[\\dA-Z]{4}\\d{16}" + }; + + bbanpattern = bbancountrypatterns[ countrycode ]; + + // As new countries will start using IBAN in the + // future, we only check if the countrycode is known. + // This prevents false negatives, while almost all + // false positives introduced by this, will be caught + // by the checksum validation below anyway. + // Strict checking should return FALSE for unknown + // countries. + if ( typeof bbanpattern !== "undefined" ) { + ibanregexp = new RegExp( "^[A-Z]{2}\\d{2}" + bbanpattern + "$", "" ); + if ( !( ibanregexp.test( iban ) ) ) { + return false; // Invalid country specific format + } + } + + // Now check the checksum, first convert to digits + ibancheck = iban.substring( 4, iban.length ) + iban.substring( 0, 4 ); + for ( i = 0; i < ibancheck.length; i++ ) { + charAt = ibancheck.charAt( i ); + if ( charAt !== "0" ) { + leadingZeroes = false; + } + if ( !leadingZeroes ) { + ibancheckdigits += "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".indexOf( charAt ); + } + } + + // Calculate the result of: ibancheckdigits % 97 + for ( p = 0; p < ibancheckdigits.length; p++ ) { + cChar = ibancheckdigits.charAt( p ); + cOperator = "" + cRest + "" + cChar; + cRest = cOperator % 97; + } + return cRest === 1; +}, "Please specify a valid IBAN" ); + +$.validator.addMethod( "integer", function( value, element ) { + return this.optional( element ) || /^-?\d+$/.test( value ); +}, "A positive or negative non-decimal number please" ); + +$.validator.addMethod( "ipv4", function( value, element ) { + return this.optional( element ) || /^(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)$/i.test( value ); +}, "Please enter a valid IP v4 address." ); + +$.validator.addMethod( "ipv6", function( value, element ) { + return this.optional( element ) || /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i.test( value ); +}, "Please enter a valid IP v6 address." ); + +$.validator.addMethod( "lettersonly", function( value, element ) { + return this.optional( element ) || /^[a-z]+$/i.test( value ); +}, "Letters only please" ); + +$.validator.addMethod( "letterswithbasicpunc", function( value, element ) { + return this.optional( element ) || /^[a-z\-.,()'"\s]+$/i.test( value ); +}, "Letters or punctuation only please" ); + +$.validator.addMethod( "mobileNL", function( value, element ) { + return this.optional( element ) || /^((\+|00(\s|\s?\-\s?)?)31(\s|\s?\-\s?)?(\(0\)[\-\s]?)?|0)6((\s|\s?\-\s?)?[0-9]){8}$/.test( value ); +}, "Please specify a valid mobile number" ); + +/* For UK phone functions, do the following server side processing: + * Compare original input with this RegEx pattern: + * ^\(?(?:(?:00\)?[\s\-]?\(?|\+)(44)\)?[\s\-]?\(?(?:0\)?[\s\-]?\(?)?|0)([1-9]\d{1,4}\)?[\s\d\-]+)$ + * Extract $1 and set $prefix to '+44' if $1 is '44', otherwise set $prefix to '0' + * Extract $2 and remove hyphens, spaces and parentheses. Phone number is combined $prefix and $2. + * A number of very detailed GB telephone number RegEx patterns can also be found at: + * http://www.aa-asterisk.org.uk/index.php/Regular_Expressions_for_Validating_and_Formatting_GB_Telephone_Numbers + */ +$.validator.addMethod( "mobileUK", function( phone_number, element ) { + phone_number = phone_number.replace( /\(|\)|\s+|-/g, "" ); + return this.optional( element ) || phone_number.length > 9 && + phone_number.match( /^(?:(?:(?:00\s?|\+)44\s?|0)7(?:[1345789]\d{2}|624)\s?\d{3}\s?\d{3})$/ ); +}, "Please specify a valid mobile number" ); + +$.validator.addMethod( "netmask", function( value, element ) { + return this.optional( element ) || /^(254|252|248|240|224|192|128)\.0\.0\.0|255\.(254|252|248|240|224|192|128|0)\.0\.0|255\.255\.(254|252|248|240|224|192|128|0)\.0|255\.255\.255\.(254|252|248|240|224|192|128|0)/i.test( value ); +}, "Please enter a valid netmask." ); + +/* + * The NIE (Número de Identificación de Extranjero) is a Spanish tax identification number assigned by the Spanish + * authorities to any foreigner. + * + * The NIE is the equivalent of a Spaniards Número de Identificación Fiscal (NIF) which serves as a fiscal + * identification number. The CIF number (Certificado de Identificación Fiscal) is equivalent to the NIF, but applies to + * companies rather than individuals. The NIE consists of an 'X' or 'Y' followed by 7 or 8 digits then another letter. + */ +$.validator.addMethod( "nieES", function( value, element ) { + "use strict"; + + if ( this.optional( element ) ) { + return true; + } + + var nieRegEx = new RegExp( /^[MXYZ]{1}[0-9]{7,8}[TRWAGMYFPDXBNJZSQVHLCKET]{1}$/gi ); + var validChars = "TRWAGMYFPDXBNJZSQVHLCKET", + letter = value.substr( value.length - 1 ).toUpperCase(), + number; + + value = value.toString().toUpperCase(); + + // Quick format test + if ( value.length > 10 || value.length < 9 || !nieRegEx.test( value ) ) { + return false; + } + + // X means same number + // Y means number + 10000000 + // Z means number + 20000000 + value = value.replace( /^[X]/, "0" ) + .replace( /^[Y]/, "1" ) + .replace( /^[Z]/, "2" ); + + number = value.length === 9 ? value.substr( 0, 8 ) : value.substr( 0, 9 ); + + return validChars.charAt( parseInt( number, 10 ) % 23 ) === letter; + +}, "Please specify a valid NIE number." ); + +/* + * The Número de Identificación Fiscal ( NIF ) is the way tax identification used in Spain for individuals + */ +$.validator.addMethod( "nifES", function( value, element ) { + "use strict"; + + if ( this.optional( element ) ) { + return true; + } + + value = value.toUpperCase(); + + // Basic format test + if ( !value.match( "((^[A-Z]{1}[0-9]{7}[A-Z0-9]{1}$|^[T]{1}[A-Z0-9]{8}$)|^[0-9]{8}[A-Z]{1}$)" ) ) { + return false; + } + + // Test NIF + if ( /^[0-9]{8}[A-Z]{1}$/.test( value ) ) { + return ( "TRWAGMYFPDXBNJZSQVHLCKE".charAt( value.substring( 8, 0 ) % 23 ) === value.charAt( 8 ) ); + } + + // Test specials NIF (starts with K, L or M) + if ( /^[KLM]{1}/.test( value ) ) { + return ( value[ 8 ] === "TRWAGMYFPDXBNJZSQVHLCKE".charAt( value.substring( 8, 1 ) % 23 ) ); + } + + return false; + +}, "Please specify a valid NIF number." ); + +/* + * Numer identyfikacji podatkowej ( NIP ) is the way tax identification used in Poland for companies + */ +$.validator.addMethod( "nipPL", function( value ) { + "use strict"; + + value = value.replace( /[^0-9]/g, "" ); + + if ( value.length !== 10 ) { + return false; + } + + var arrSteps = [ 6, 5, 7, 2, 3, 4, 5, 6, 7 ]; + var intSum = 0; + for ( var i = 0; i < 9; i++ ) { + intSum += arrSteps[ i ] * value[ i ]; + } + var int2 = intSum % 11; + var intControlNr = ( int2 === 10 ) ? 0 : int2; + + return ( intControlNr === parseInt( value[ 9 ], 10 ) ); +}, "Please specify a valid NIP number." ); + +$.validator.addMethod( "notEqualTo", function( value, element, param ) { + return this.optional( element ) || !$.validator.methods.equalTo.call( this, value, element, param ); +}, "Please enter a different value, values must not be the same." ); + +$.validator.addMethod( "nowhitespace", function( value, element ) { + return this.optional( element ) || /^\S+$/i.test( value ); +}, "No white space please" ); + +/** +* Return true if the field value matches the given format RegExp +* +* @example $.validator.methods.pattern("AR1004",element,/^AR\d{4}$/) +* @result true +* +* @example $.validator.methods.pattern("BR1004",element,/^AR\d{4}$/) +* @result false +* +* @name $.validator.methods.pattern +* @type Boolean +* @cat Plugins/Validate/Methods +*/ +$.validator.addMethod( "pattern", function( value, element, param ) { + if ( this.optional( element ) ) { + return true; + } + if ( typeof param === "string" ) { + param = new RegExp( "^(?:" + param + ")$" ); + } + return param.test( value ); +}, "Invalid format." ); + +/** + * Dutch phone numbers have 10 digits (or 11 and start with +31). + */ +$.validator.addMethod( "phoneNL", function( value, element ) { + return this.optional( element ) || /^((\+|00(\s|\s?\-\s?)?)31(\s|\s?\-\s?)?(\(0\)[\-\s]?)?|0)[1-9]((\s|\s?\-\s?)?[0-9]){8}$/.test( value ); +}, "Please specify a valid phone number." ); + +/* For UK phone functions, do the following server side processing: + * Compare original input with this RegEx pattern: + * ^\(?(?:(?:00\)?[\s\-]?\(?|\+)(44)\)?[\s\-]?\(?(?:0\)?[\s\-]?\(?)?|0)([1-9]\d{1,4}\)?[\s\d\-]+)$ + * Extract $1 and set $prefix to '+44' if $1 is '44', otherwise set $prefix to '0' + * Extract $2 and remove hyphens, spaces and parentheses. Phone number is combined $prefix and $2. + * A number of very detailed GB telephone number RegEx patterns can also be found at: + * http://www.aa-asterisk.org.uk/index.php/Regular_Expressions_for_Validating_and_Formatting_GB_Telephone_Numbers + */ + +// Matches UK landline + mobile, accepting only 01-3 for landline or 07 for mobile to exclude many premium numbers +$.validator.addMethod( "phonesUK", function( phone_number, element ) { + phone_number = phone_number.replace( /\(|\)|\s+|-/g, "" ); + return this.optional( element ) || phone_number.length > 9 && + phone_number.match( /^(?:(?:(?:00\s?|\+)44\s?|0)(?:1\d{8,9}|[23]\d{9}|7(?:[1345789]\d{8}|624\d{6})))$/ ); +}, "Please specify a valid uk phone number" ); + +/* For UK phone functions, do the following server side processing: + * Compare original input with this RegEx pattern: + * ^\(?(?:(?:00\)?[\s\-]?\(?|\+)(44)\)?[\s\-]?\(?(?:0\)?[\s\-]?\(?)?|0)([1-9]\d{1,4}\)?[\s\d\-]+)$ + * Extract $1 and set $prefix to '+44' if $1 is '44', otherwise set $prefix to '0' + * Extract $2 and remove hyphens, spaces and parentheses. Phone number is combined $prefix and $2. + * A number of very detailed GB telephone number RegEx patterns can also be found at: + * http://www.aa-asterisk.org.uk/index.php/Regular_Expressions_for_Validating_and_Formatting_GB_Telephone_Numbers + */ +$.validator.addMethod( "phoneUK", function( phone_number, element ) { + phone_number = phone_number.replace( /\(|\)|\s+|-/g, "" ); + return this.optional( element ) || phone_number.length > 9 && + phone_number.match( /^(?:(?:(?:00\s?|\+)44\s?)|(?:\(?0))(?:\d{2}\)?\s?\d{4}\s?\d{4}|\d{3}\)?\s?\d{3}\s?\d{3,4}|\d{4}\)?\s?(?:\d{5}|\d{3}\s?\d{3})|\d{5}\)?\s?\d{4,5})$/ ); +}, "Please specify a valid phone number" ); + +/** + * Matches US phone number format + * + * where the area code may not start with 1 and the prefix may not start with 1 + * allows '-' or ' ' as a separator and allows parens around area code + * some people may want to put a '1' in front of their number + * + * 1(212)-999-2345 or + * 212 999 2344 or + * 212-999-0983 + * + * but not + * 111-123-5434 + * and not + * 212 123 4567 + */ +$.validator.addMethod( "phoneUS", function( phone_number, element ) { + phone_number = phone_number.replace( /\s+/g, "" ); + return this.optional( element ) || phone_number.length > 9 && + phone_number.match( /^(\+?1-?)?(\([2-9]([02-9]\d|1[02-9])\)|[2-9]([02-9]\d|1[02-9]))-?[2-9]([02-9]\d|1[02-9])-?\d{4}$/ ); +}, "Please specify a valid phone number" ); + +/* +* Valida CEPs do brasileiros: +* +* Formatos aceitos: +* 99999-999 +* 99.999-999 +* 99999999 +*/ +$.validator.addMethod( "postalcodeBR", function( cep_value, element ) { + return this.optional( element ) || /^\d{2}.\d{3}-\d{3}?$|^\d{5}-?\d{3}?$/.test( cep_value ); +}, "Informe um CEP válido." ); + +/** + * Matches a valid Canadian Postal Code + * + * @example jQuery.validator.methods.postalCodeCA( "H0H 0H0", element ) + * @result true + * + * @example jQuery.validator.methods.postalCodeCA( "H0H0H0", element ) + * @result false + * + * @name jQuery.validator.methods.postalCodeCA + * @type Boolean + * @cat Plugins/Validate/Methods + */ +$.validator.addMethod( "postalCodeCA", function( value, element ) { + return this.optional( element ) || /^[ABCEGHJKLMNPRSTVXY]\d[ABCEGHJKLMNPRSTVWXYZ] *\d[ABCEGHJKLMNPRSTVWXYZ]\d$/i.test( value ); +}, "Please specify a valid postal code" ); + +/* Matches Italian postcode (CAP) */ +$.validator.addMethod( "postalcodeIT", function( value, element ) { + return this.optional( element ) || /^\d{5}$/.test( value ); +}, "Please specify a valid postal code" ); + +$.validator.addMethod( "postalcodeNL", function( value, element ) { + return this.optional( element ) || /^[1-9][0-9]{3}\s?[a-zA-Z]{2}$/.test( value ); +}, "Please specify a valid postal code" ); + +// Matches UK postcode. Does not match to UK Channel Islands that have their own postcodes (non standard UK) +$.validator.addMethod( "postcodeUK", function( value, element ) { + return this.optional( element ) || /^((([A-PR-UWYZ][0-9])|([A-PR-UWYZ][0-9][0-9])|([A-PR-UWYZ][A-HK-Y][0-9])|([A-PR-UWYZ][A-HK-Y][0-9][0-9])|([A-PR-UWYZ][0-9][A-HJKSTUW])|([A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRVWXY]))\s?([0-9][ABD-HJLNP-UW-Z]{2})|(GIR)\s?(0AA))$/i.test( value ); +}, "Please specify a valid UK postcode" ); + +/* + * Lets you say "at least X inputs that match selector Y must be filled." + * + * The end result is that neither of these inputs: + * + * + * + * + * ...will validate unless at least one of them is filled. + * + * partnumber: {require_from_group: [1,".productinfo"]}, + * description: {require_from_group: [1,".productinfo"]} + * + * options[0]: number of fields that must be filled in the group + * options[1]: CSS selector that defines the group of conditionally required fields + */ +$.validator.addMethod( "require_from_group", function( value, element, options ) { + var $fields = $( options[ 1 ], element.form ), + $fieldsFirst = $fields.eq( 0 ), + validator = $fieldsFirst.data( "valid_req_grp" ) ? $fieldsFirst.data( "valid_req_grp" ) : $.extend( {}, this ), + isValid = $fields.filter( function() { + return validator.elementValue( this ); + } ).length >= options[ 0 ]; + + // Store the cloned validator for future validation + $fieldsFirst.data( "valid_req_grp", validator ); + + // If element isn't being validated, run each require_from_group field's validation rules + if ( !$( element ).data( "being_validated" ) ) { + $fields.data( "being_validated", true ); + $fields.each( function() { + validator.element( this ); + } ); + $fields.data( "being_validated", false ); + } + return isValid; +}, $.validator.format( "Please fill at least {0} of these fields." ) ); + +/* + * Lets you say "either at least X inputs that match selector Y must be filled, + * OR they must all be skipped (left blank)." + * + * The end result, is that none of these inputs: + * + * + * + * + * + * ...will validate unless either at least two of them are filled, + * OR none of them are. + * + * partnumber: {skip_or_fill_minimum: [2,".productinfo"]}, + * description: {skip_or_fill_minimum: [2,".productinfo"]}, + * color: {skip_or_fill_minimum: [2,".productinfo"]} + * + * options[0]: number of fields that must be filled in the group + * options[1]: CSS selector that defines the group of conditionally required fields + * + */ +$.validator.addMethod( "skip_or_fill_minimum", function( value, element, options ) { + var $fields = $( options[ 1 ], element.form ), + $fieldsFirst = $fields.eq( 0 ), + validator = $fieldsFirst.data( "valid_skip" ) ? $fieldsFirst.data( "valid_skip" ) : $.extend( {}, this ), + numberFilled = $fields.filter( function() { + return validator.elementValue( this ); + } ).length, + isValid = numberFilled === 0 || numberFilled >= options[ 0 ]; + + // Store the cloned validator for future validation + $fieldsFirst.data( "valid_skip", validator ); + + // If element isn't being validated, run each skip_or_fill_minimum field's validation rules + if ( !$( element ).data( "being_validated" ) ) { + $fields.data( "being_validated", true ); + $fields.each( function() { + validator.element( this ); + } ); + $fields.data( "being_validated", false ); + } + return isValid; +}, $.validator.format( "Please either skip these fields or fill at least {0} of them." ) ); + +/* Validates US States and/or Territories by @jdforsythe + * Can be case insensitive or require capitalization - default is case insensitive + * Can include US Territories or not - default does not + * Can include US Military postal abbreviations (AA, AE, AP) - default does not + * + * Note: "States" always includes DC (District of Colombia) + * + * Usage examples: + * + * This is the default - case insensitive, no territories, no military zones + * stateInput: { + * caseSensitive: false, + * includeTerritories: false, + * includeMilitary: false + * } + * + * Only allow capital letters, no territories, no military zones + * stateInput: { + * caseSensitive: false + * } + * + * Case insensitive, include territories but not military zones + * stateInput: { + * includeTerritories: true + * } + * + * Only allow capital letters, include territories and military zones + * stateInput: { + * caseSensitive: true, + * includeTerritories: true, + * includeMilitary: true + * } + * + */ +$.validator.addMethod( "stateUS", function( value, element, options ) { + var isDefault = typeof options === "undefined", + caseSensitive = ( isDefault || typeof options.caseSensitive === "undefined" ) ? false : options.caseSensitive, + includeTerritories = ( isDefault || typeof options.includeTerritories === "undefined" ) ? false : options.includeTerritories, + includeMilitary = ( isDefault || typeof options.includeMilitary === "undefined" ) ? false : options.includeMilitary, + regex; + + if ( !includeTerritories && !includeMilitary ) { + regex = "^(A[KLRZ]|C[AOT]|D[CE]|FL|GA|HI|I[ADLN]|K[SY]|LA|M[ADEINOST]|N[CDEHJMVY]|O[HKR]|PA|RI|S[CD]|T[NX]|UT|V[AT]|W[AIVY])$"; + } else if ( includeTerritories && includeMilitary ) { + regex = "^(A[AEKLPRSZ]|C[AOT]|D[CE]|FL|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEINOPST]|N[CDEHJMVY]|O[HKR]|P[AR]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$"; + } else if ( includeTerritories ) { + regex = "^(A[KLRSZ]|C[AOT]|D[CE]|FL|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEINOPST]|N[CDEHJMVY]|O[HKR]|P[AR]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$"; + } else { + regex = "^(A[AEKLPRZ]|C[AOT]|D[CE]|FL|GA|HI|I[ADLN]|K[SY]|LA|M[ADEINOST]|N[CDEHJMVY]|O[HKR]|PA|RI|S[CD]|T[NX]|UT|V[AT]|W[AIVY])$"; + } + + regex = caseSensitive ? new RegExp( regex ) : new RegExp( regex, "i" ); + return this.optional( element ) || regex.test( value ); +}, "Please specify a valid state" ); + +// TODO check if value starts with <, otherwise don't try stripping anything +$.validator.addMethod( "strippedminlength", function( value, element, param ) { + return $( value ).text().length >= param; +}, $.validator.format( "Please enter at least {0} characters" ) ); + +$.validator.addMethod( "time", function( value, element ) { + return this.optional( element ) || /^([01]\d|2[0-3]|[0-9])(:[0-5]\d){1,2}$/.test( value ); +}, "Please enter a valid time, between 00:00 and 23:59" ); + +$.validator.addMethod( "time12h", function( value, element ) { + return this.optional( element ) || /^((0?[1-9]|1[012])(:[0-5]\d){1,2}(\ ?[AP]M))$/i.test( value ); +}, "Please enter a valid time in 12-hour am/pm format" ); + +// Same as url, but TLD is optional +$.validator.addMethod( "url2", function( value, element ) { + return this.optional( element ) || /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)*(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test( value ); +}, $.validator.messages.url ); + +/** + * Return true, if the value is a valid vehicle identification number (VIN). + * + * Works with all kind of text inputs. + * + * @example + * @desc Declares a required input element whose value must be a valid vehicle identification number. + * + * @name $.validator.methods.vinUS + * @type Boolean + * @cat Plugins/Validate/Methods + */ +$.validator.addMethod( "vinUS", function( v ) { + if ( v.length !== 17 ) { + return false; + } + + var LL = [ "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" ], + VL = [ 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 7, 9, 2, 3, 4, 5, 6, 7, 8, 9 ], + FL = [ 8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2 ], + rs = 0, + i, n, d, f, cd, cdv; + + for ( i = 0; i < 17; i++ ) { + f = FL[ i ]; + d = v.slice( i, i + 1 ); + if ( i === 8 ) { + cdv = d; + } + if ( !isNaN( d ) ) { + d *= f; + } else { + for ( n = 0; n < LL.length; n++ ) { + if ( d.toUpperCase() === LL[ n ] ) { + d = VL[ n ]; + d *= f; + if ( isNaN( cdv ) && n === 8 ) { + cdv = LL[ n ]; + } + break; + } + } + } + rs += d; + } + cd = rs % 11; + if ( cd === 10 ) { + cd = "X"; + } + if ( cd === cdv ) { + return true; + } + return false; +}, "The specified vehicle identification number (VIN) is invalid." ); + +$.validator.addMethod( "zipcodeUS", function( value, element ) { + return this.optional( element ) || /^\d{5}(-\d{4})?$/.test( value ); +}, "The specified US ZIP Code is invalid" ); + +$.validator.addMethod( "ziprange", function( value, element ) { + return this.optional( element ) || /^90[2-5]\d\{2\}-\d{4}$/.test( value ); +}, "Your ZIP-code must be in the range 902xx-xxxx to 905xx-xxxx" ); +return $; +})); \ No newline at end of file diff --git a/dockers/web/html/colorlib-regform-16/vendor/jquery-validation/dist/additional-methods.min.js b/dockers/web/html/colorlib-regform-16/vendor/jquery-validation/dist/additional-methods.min.js new file mode 100755 index 0000000..6767f24 --- /dev/null +++ b/dockers/web/html/colorlib-regform-16/vendor/jquery-validation/dist/additional-methods.min.js @@ -0,0 +1,4 @@ +/*! jQuery Validation Plugin - v1.17.0 - 7/29/2017 + * https://jqueryvalidation.org/ + * Copyright (c) 2017 Jörn Zaefferer; Licensed MIT */ +!function(a){"function"==typeof define&&define.amd?define(["jquery","./jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return function(){function b(a){return a.replace(/<.[^<>]*?>/g," ").replace(/ | /gi," ").replace(/[.(),;:!?%#$'\"_+=\/\-“”’]*/g,"")}a.validator.addMethod("maxWords",function(a,c,d){return this.optional(c)||b(a).match(/\b\w+\b/g).length<=d},a.validator.format("Please enter {0} words or less.")),a.validator.addMethod("minWords",function(a,c,d){return this.optional(c)||b(a).match(/\b\w+\b/g).length>=d},a.validator.format("Please enter at least {0} words.")),a.validator.addMethod("rangeWords",function(a,c,d){var e=b(a),f=/\b\w+\b/g;return this.optional(c)||e.match(f).length>=d[0]&&e.match(f).length<=d[1]},a.validator.format("Please enter between {0} and {1} words."))}(),a.validator.addMethod("accept",function(b,c,d){var e,f,g,h="string"==typeof d?d.replace(/\s/g,""):"image/*",i=this.optional(c);if(i)return i;if("file"===a(c).attr("type")&&(h=h.replace(/[\-\[\]\/\{\}\(\)\+\?\.\\\^\$\|]/g,"\\$&").replace(/,/g,"|").replace(/\/\*/g,"/.*"),c.files&&c.files.length))for(g=new RegExp(".?("+h+")$","i"),e=0;e9?"0":f,g="JABCDEFGHI".substr(f,1).toString(),i.match(/[ABEH]/)?k===f:i.match(/[KPQS]/)?k===g:k===f||k===g},"Please specify a valid CIF number."),a.validator.addMethod("cpfBR",function(a){if(a=a.replace(/([~!@#$%^&*()_+=`{}\[\]\-|\\:;'<>,.\/? ])+/g,""),11!==a.length)return!1;var b,c,d,e,f=0;if(b=parseInt(a.substring(9,10),10),c=parseInt(a.substring(10,11),10),d=function(a,b){var c=10*a%11;return 10!==c&&11!==c||(c=0),c===b},""===a||"00000000000"===a||"11111111111"===a||"22222222222"===a||"33333333333"===a||"44444444444"===a||"55555555555"===a||"66666666666"===a||"77777777777"===a||"88888888888"===a||"99999999999"===a)return!1;for(e=1;e<=9;e++)f+=parseInt(a.substring(e-1,e),10)*(11-e);if(d(f,b)){for(f=0,e=1;e<=10;e++)f+=parseInt(a.substring(e-1,e),10)*(12-e);return d(f,c)}return!1},"Please specify a valid CPF number"),a.validator.addMethod("creditcard",function(a,b){if(this.optional(b))return"dependency-mismatch";if(/[^0-9 \-]+/.test(a))return!1;var c,d,e=0,f=0,g=!1;if(a=a.replace(/\D/g,""),a.length<13||a.length>19)return!1;for(c=a.length-1;c>=0;c--)d=a.charAt(c),f=parseInt(d,10),g&&(f*=2)>9&&(f-=9),e+=f,g=!g;return e%10===0},"Please enter a valid credit card number."),a.validator.addMethod("creditcardtypes",function(a,b,c){if(/[^0-9\-]+/.test(a))return!1;a=a.replace(/\D/g,"");var d=0;return c.mastercard&&(d|=1),c.visa&&(d|=2),c.amex&&(d|=4),c.dinersclub&&(d|=8),c.enroute&&(d|=16),c.discover&&(d|=32),c.jcb&&(d|=64),c.unknown&&(d|=128),c.all&&(d=255),1&d&&/^(5[12345])/.test(a)?16===a.length:2&d&&/^(4)/.test(a)?16===a.length:4&d&&/^(3[47])/.test(a)?15===a.length:8&d&&/^(3(0[012345]|[68]))/.test(a)?14===a.length:16&d&&/^(2(014|149))/.test(a)?15===a.length:32&d&&/^(6011)/.test(a)?16===a.length:64&d&&/^(3)/.test(a)?16===a.length:64&d&&/^(2131|1800)/.test(a)?15===a.length:!!(128&d)},"Please enter a valid credit card number."),a.validator.addMethod("currency",function(a,b,c){var d,e="string"==typeof c,f=e?c:c[0],g=!!e||c[1];return f=f.replace(/,/g,""),f=g?f+"]":f+"]?",d="^["+f+"([1-9]{1}[0-9]{0,2}(\\,[0-9]{3})*(\\.[0-9]{0,2})?|[1-9]{1}[0-9]{0,}(\\.[0-9]{0,2})?|0(\\.[0-9]{0,2})?|(\\.[0-9]{1,2})?)$",d=new RegExp(d),this.optional(b)||d.test(a)},"Please specify a valid currency"),a.validator.addMethod("dateFA",function(a,b){return this.optional(b)||/^[1-4]\d{3}\/((0?[1-6]\/((3[0-1])|([1-2][0-9])|(0?[1-9])))|((1[0-2]|(0?[7-9]))\/(30|([1-2][0-9])|(0?[1-9]))))$/.test(a)},a.validator.messages.date),a.validator.addMethod("dateITA",function(a,b){var c,d,e,f,g,h=!1,i=/^\d{1,2}\/\d{1,2}\/\d{4}$/;return i.test(a)?(c=a.split("/"),d=parseInt(c[0],10),e=parseInt(c[1],10),f=parseInt(c[2],10),g=new Date(Date.UTC(f,e-1,d,12,0,0,0)),h=g.getUTCFullYear()===f&&g.getUTCMonth()===e-1&&g.getUTCDate()===d):h=!1,this.optional(b)||h},a.validator.messages.date),a.validator.addMethod("dateNL",function(a,b){return this.optional(b)||/^(0?[1-9]|[12]\d|3[01])[\.\/\-](0?[1-9]|1[012])[\.\/\-]([12]\d)?(\d\d)$/.test(a)},a.validator.messages.date),a.validator.addMethod("extension",function(a,b,c){return c="string"==typeof c?c.replace(/,/g,"|"):"png|jpe?g|gif",this.optional(b)||a.match(new RegExp("\\.("+c+")$","i"))},a.validator.format("Please enter a value with a valid extension.")),a.validator.addMethod("giroaccountNL",function(a,b){return this.optional(b)||/^[0-9]{1,7}$/.test(a)},"Please specify a valid giro account number"),a.validator.addMethod("iban",function(a,b){if(this.optional(b))return!0;var c,d,e,f,g,h,i,j,k,l=a.replace(/ /g,"").toUpperCase(),m="",n=!0,o="",p="",q=5;if(l.length9&&a.match(/^(?:(?:(?:00\s?|\+)44\s?|0)7(?:[1345789]\d{2}|624)\s?\d{3}\s?\d{3})$/)},"Please specify a valid mobile number"),a.validator.addMethod("netmask",function(a,b){return this.optional(b)||/^(254|252|248|240|224|192|128)\.0\.0\.0|255\.(254|252|248|240|224|192|128|0)\.0\.0|255\.255\.(254|252|248|240|224|192|128|0)\.0|255\.255\.255\.(254|252|248|240|224|192|128|0)/i.test(a)},"Please enter a valid netmask."),a.validator.addMethod("nieES",function(a,b){"use strict";if(this.optional(b))return!0;var c,d=new RegExp(/^[MXYZ]{1}[0-9]{7,8}[TRWAGMYFPDXBNJZSQVHLCKET]{1}$/gi),e="TRWAGMYFPDXBNJZSQVHLCKET",f=a.substr(a.length-1).toUpperCase();return a=a.toString().toUpperCase(),!(a.length>10||a.length<9||!d.test(a))&&(a=a.replace(/^[X]/,"0").replace(/^[Y]/,"1").replace(/^[Z]/,"2"),c=9===a.length?a.substr(0,8):a.substr(0,9),e.charAt(parseInt(c,10)%23)===f)},"Please specify a valid NIE number."),a.validator.addMethod("nifES",function(a,b){"use strict";return!!this.optional(b)||(a=a.toUpperCase(),!!a.match("((^[A-Z]{1}[0-9]{7}[A-Z0-9]{1}$|^[T]{1}[A-Z0-9]{8}$)|^[0-9]{8}[A-Z]{1}$)")&&(/^[0-9]{8}[A-Z]{1}$/.test(a)?"TRWAGMYFPDXBNJZSQVHLCKE".charAt(a.substring(8,0)%23)===a.charAt(8):!!/^[KLM]{1}/.test(a)&&a[8]==="TRWAGMYFPDXBNJZSQVHLCKE".charAt(a.substring(8,1)%23)))},"Please specify a valid NIF number."),a.validator.addMethod("nipPL",function(a){"use strict";if(a=a.replace(/[^0-9]/g,""),10!==a.length)return!1;for(var b=[6,5,7,2,3,4,5,6,7],c=0,d=0;d<9;d++)c+=b[d]*a[d];var e=c%11,f=10===e?0:e;return f===parseInt(a[9],10)},"Please specify a valid NIP number."),a.validator.addMethod("notEqualTo",function(b,c,d){return this.optional(c)||!a.validator.methods.equalTo.call(this,b,c,d)},"Please enter a different value, values must not be the same."),a.validator.addMethod("nowhitespace",function(a,b){return this.optional(b)||/^\S+$/i.test(a)},"No white space please"),a.validator.addMethod("pattern",function(a,b,c){return!!this.optional(b)||("string"==typeof c&&(c=new RegExp("^(?:"+c+")$")),c.test(a))},"Invalid format."),a.validator.addMethod("phoneNL",function(a,b){return this.optional(b)||/^((\+|00(\s|\s?\-\s?)?)31(\s|\s?\-\s?)?(\(0\)[\-\s]?)?|0)[1-9]((\s|\s?\-\s?)?[0-9]){8}$/.test(a)},"Please specify a valid phone number."),a.validator.addMethod("phonesUK",function(a,b){return a=a.replace(/\(|\)|\s+|-/g,""),this.optional(b)||a.length>9&&a.match(/^(?:(?:(?:00\s?|\+)44\s?|0)(?:1\d{8,9}|[23]\d{9}|7(?:[1345789]\d{8}|624\d{6})))$/)},"Please specify a valid uk phone number"),a.validator.addMethod("phoneUK",function(a,b){return a=a.replace(/\(|\)|\s+|-/g,""),this.optional(b)||a.length>9&&a.match(/^(?:(?:(?:00\s?|\+)44\s?)|(?:\(?0))(?:\d{2}\)?\s?\d{4}\s?\d{4}|\d{3}\)?\s?\d{3}\s?\d{3,4}|\d{4}\)?\s?(?:\d{5}|\d{3}\s?\d{3})|\d{5}\)?\s?\d{4,5})$/)},"Please specify a valid phone number"),a.validator.addMethod("phoneUS",function(a,b){return a=a.replace(/\s+/g,""),this.optional(b)||a.length>9&&a.match(/^(\+?1-?)?(\([2-9]([02-9]\d|1[02-9])\)|[2-9]([02-9]\d|1[02-9]))-?[2-9]([02-9]\d|1[02-9])-?\d{4}$/)},"Please specify a valid phone number"),a.validator.addMethod("postalcodeBR",function(a,b){return this.optional(b)||/^\d{2}.\d{3}-\d{3}?$|^\d{5}-?\d{3}?$/.test(a)},"Informe um CEP válido."),a.validator.addMethod("postalCodeCA",function(a,b){return this.optional(b)||/^[ABCEGHJKLMNPRSTVXY]\d[ABCEGHJKLMNPRSTVWXYZ] *\d[ABCEGHJKLMNPRSTVWXYZ]\d$/i.test(a)},"Please specify a valid postal code"),a.validator.addMethod("postalcodeIT",function(a,b){return this.optional(b)||/^\d{5}$/.test(a)},"Please specify a valid postal code"),a.validator.addMethod("postalcodeNL",function(a,b){return this.optional(b)||/^[1-9][0-9]{3}\s?[a-zA-Z]{2}$/.test(a)},"Please specify a valid postal code"),a.validator.addMethod("postcodeUK",function(a,b){return this.optional(b)||/^((([A-PR-UWYZ][0-9])|([A-PR-UWYZ][0-9][0-9])|([A-PR-UWYZ][A-HK-Y][0-9])|([A-PR-UWYZ][A-HK-Y][0-9][0-9])|([A-PR-UWYZ][0-9][A-HJKSTUW])|([A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRVWXY]))\s?([0-9][ABD-HJLNP-UW-Z]{2})|(GIR)\s?(0AA))$/i.test(a)},"Please specify a valid UK postcode"),a.validator.addMethod("require_from_group",function(b,c,d){var e=a(d[1],c.form),f=e.eq(0),g=f.data("valid_req_grp")?f.data("valid_req_grp"):a.extend({},this),h=e.filter(function(){return g.elementValue(this)}).length>=d[0];return f.data("valid_req_grp",g),a(c).data("being_validated")||(e.data("being_validated",!0),e.each(function(){g.element(this)}),e.data("being_validated",!1)),h},a.validator.format("Please fill at least {0} of these fields.")),a.validator.addMethod("skip_or_fill_minimum",function(b,c,d){var e=a(d[1],c.form),f=e.eq(0),g=f.data("valid_skip")?f.data("valid_skip"):a.extend({},this),h=e.filter(function(){return g.elementValue(this)}).length,i=0===h||h>=d[0];return f.data("valid_skip",g),a(c).data("being_validated")||(e.data("being_validated",!0),e.each(function(){g.element(this)}),e.data("being_validated",!1)),i},a.validator.format("Please either skip these fields or fill at least {0} of them.")),a.validator.addMethod("stateUS",function(a,b,c){var d,e="undefined"==typeof c,f=!e&&"undefined"!=typeof c.caseSensitive&&c.caseSensitive,g=!e&&"undefined"!=typeof c.includeTerritories&&c.includeTerritories,h=!e&&"undefined"!=typeof c.includeMilitary&&c.includeMilitary;return d=g||h?g&&h?"^(A[AEKLPRSZ]|C[AOT]|D[CE]|FL|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEINOPST]|N[CDEHJMVY]|O[HKR]|P[AR]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$":g?"^(A[KLRSZ]|C[AOT]|D[CE]|FL|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEINOPST]|N[CDEHJMVY]|O[HKR]|P[AR]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$":"^(A[AEKLPRZ]|C[AOT]|D[CE]|FL|GA|HI|I[ADLN]|K[SY]|LA|M[ADEINOST]|N[CDEHJMVY]|O[HKR]|PA|RI|S[CD]|T[NX]|UT|V[AT]|W[AIVY])$":"^(A[KLRZ]|C[AOT]|D[CE]|FL|GA|HI|I[ADLN]|K[SY]|LA|M[ADEINOST]|N[CDEHJMVY]|O[HKR]|PA|RI|S[CD]|T[NX]|UT|V[AT]|W[AIVY])$",d=f?new RegExp(d):new RegExp(d,"i"),this.optional(b)||d.test(a)},"Please specify a valid state"),a.validator.addMethod("strippedminlength",function(b,c,d){return a(b).text().length>=d},a.validator.format("Please enter at least {0} characters")),a.validator.addMethod("time",function(a,b){return this.optional(b)||/^([01]\d|2[0-3]|[0-9])(:[0-5]\d){1,2}$/.test(a)},"Please enter a valid time, between 00:00 and 23:59"),a.validator.addMethod("time12h",function(a,b){return this.optional(b)||/^((0?[1-9]|1[012])(:[0-5]\d){1,2}(\ ?[AP]M))$/i.test(a)},"Please enter a valid time in 12-hour am/pm format"),a.validator.addMethod("url2",function(a,b){return this.optional(b)||/^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)*(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(a)},a.validator.messages.url),a.validator.addMethod("vinUS",function(a){if(17!==a.length)return!1;var b,c,d,e,f,g,h=["A","B","C","D","E","F","G","H","J","K","L","M","N","P","R","S","T","U","V","W","X","Y","Z"],i=[1,2,3,4,5,6,7,8,1,2,3,4,5,7,9,2,3,4,5,6,7,8,9],j=[8,7,6,5,4,3,2,10,0,9,8,7,6,5,4,3,2],k=0;for(b=0;b<17;b++){if(e=j[b],d=a.slice(b,b+1),8===b&&(g=d),isNaN(d)){for(c=0;c
     
    ","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};ge.optgroup=ge.option,ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td;function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&N(e,t)?w.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n-1)i&&i.push(o);else if(l=w.contains(o.ownerDocument,o),a=ye(f.appendChild(o),"script"),l&&ve(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}!function(){var e=r.createDocumentFragment().appendChild(r.createElement("div")),t=r.createElement("input");t.setAttribute("type","radio"),t.setAttribute("checked","checked"),t.setAttribute("name","t"),e.appendChild(t),h.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="",h.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue}();var be=r.documentElement,we=/^key/,Te=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ce=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function ke(){return!1}function Se(){try{return r.activeElement}catch(e){}}function De(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)De(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=ke;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return w().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=w.guid++)),e.each(function(){w.event.add(this,t,i,r,n)})}w.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.get(e);if(y){n.handler&&(n=(o=n).handler,i=o.selector),i&&w.find.matchesSelector(be,i),n.guid||(n.guid=w.guid++),(u=y.events)||(u=y.events={}),(a=y.handle)||(a=y.handle=function(t){return"undefined"!=typeof w&&w.event.triggered!==t.type?w.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(M)||[""]).length;while(l--)d=g=(s=Ce.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=w.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=w.event.special[d]||{},c=w.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&w.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),w.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.hasData(e)&&J.get(e);if(y&&(u=y.events)){l=(t=(t||"").match(M)||[""]).length;while(l--)if(s=Ce.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){f=w.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,y.handle)||w.removeEvent(e,d,y.handle),delete u[d])}else for(d in u)w.event.remove(e,d+t[l],n,r,!0);w.isEmptyObject(u)&&J.remove(e,"handle events")}},dispatch:function(e){var t=w.event.fix(e),n,r,i,o,a,s,u=new Array(arguments.length),l=(J.get(this,"events")||{})[t.type]||[],c=w.event.special[t.type]||{};for(u[0]=t,n=1;n=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n-1:w.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u\x20\t\r\n\f]*)[^>]*)\/>/gi,Ae=/\s*$/g;function Le(e,t){return N(e,"table")&&N(11!==t.nodeType?t:t.firstChild,"tr")?w(e).children("tbody")[0]||e:e}function He(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Oe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Pe(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(J.hasData(e)&&(o=J.access(e),a=J.set(t,o),l=o.events)){delete a.handle,a.events={};for(i in l)for(n=0,r=l[i].length;n1&&"string"==typeof y&&!h.checkClone&&je.test(y))return e.each(function(i){var o=e.eq(i);v&&(t[0]=y.call(this,i,o.html())),Re(o,t,n,r)});if(p&&(i=xe(t,e[0].ownerDocument,!1,e,r),o=i.firstChild,1===i.childNodes.length&&(i=o),o||r)){for(u=(s=w.map(ye(i,"script"),He)).length;f")},clone:function(e,t,n){var r,i,o,a,s=e.cloneNode(!0),u=w.contains(e.ownerDocument,e);if(!(h.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||w.isXMLDoc(e)))for(a=ye(s),r=0,i=(o=ye(e)).length;r0&&ve(a,!u&&ye(e,"script")),s},cleanData:function(e){for(var t,n,r,i=w.event.special,o=0;void 0!==(n=e[o]);o++)if(Y(n)){if(t=n[J.expando]){if(t.events)for(r in t.events)i[r]?w.event.remove(n,r):w.removeEvent(n,r,t.handle);n[J.expando]=void 0}n[K.expando]&&(n[K.expando]=void 0)}}}),w.fn.extend({detach:function(e){return Ie(this,e,!0)},remove:function(e){return Ie(this,e)},text:function(e){return z(this,function(e){return void 0===e?w.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Re(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Le(this,e).appendChild(e)})},prepend:function(){return Re(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Le(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(w.cleanData(ye(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return w.clone(this,e,t)})},html:function(e){return z(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ae.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=w.htmlPrefilter(e);try{for(;n=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))),u}function et(e,t,n){var r=$e(e),i=Fe(e,t,r),o="border-box"===w.css(e,"boxSizing",!1,r),a=o;if(We.test(i)){if(!n)return i;i="auto"}return a=a&&(h.boxSizingReliable()||i===e.style[t]),("auto"===i||!parseFloat(i)&&"inline"===w.css(e,"display",!1,r))&&(i=e["offset"+t[0].toUpperCase()+t.slice(1)],a=!0),(i=parseFloat(i)||0)+Ze(e,t,n||(o?"border":"content"),a,r,i)+"px"}w.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Fe(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=G(t),u=Xe.test(t),l=e.style;if(u||(t=Je(s)),a=w.cssHooks[t]||w.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"==(o=typeof n)&&(i=ie.exec(n))&&i[1]&&(n=ue(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(w.cssNumber[s]?"":"px")),h.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=G(t);return Xe.test(t)||(t=Je(s)),(a=w.cssHooks[t]||w.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Fe(e,t,r)),"normal"===i&&t in Ve&&(i=Ve[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),w.each(["height","width"],function(e,t){w.cssHooks[t]={get:function(e,n,r){if(n)return!ze.test(w.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?et(e,t,r):se(e,Ue,function(){return et(e,t,r)})},set:function(e,n,r){var i,o=$e(e),a="border-box"===w.css(e,"boxSizing",!1,o),s=r&&Ze(e,t,r,a,o);return a&&h.scrollboxSize()===o.position&&(s-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-Ze(e,t,"border",!1,o)-.5)),s&&(i=ie.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=w.css(e,t)),Ke(e,n,s)}}}),w.cssHooks.marginLeft=_e(h.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Fe(e,"marginLeft"))||e.getBoundingClientRect().left-se(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),w.each({margin:"",padding:"",border:"Width"},function(e,t){w.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+oe[r]+t]=o[r]||o[r-2]||o[0];return i}},"margin"!==e&&(w.cssHooks[e+t].set=Ke)}),w.fn.extend({css:function(e,t){return z(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=$e(e),i=t.length;a1)}});function tt(e,t,n,r,i){return new tt.prototype.init(e,t,n,r,i)}w.Tween=tt,tt.prototype={constructor:tt,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||w.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(w.cssNumber[n]?"":"px")},cur:function(){var e=tt.propHooks[this.prop];return e&&e.get?e.get(this):tt.propHooks._default.get(this)},run:function(e){var t,n=tt.propHooks[this.prop];return this.options.duration?this.pos=t=w.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):tt.propHooks._default.set(this),this}},tt.prototype.init.prototype=tt.prototype,tt.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=w.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){w.fx.step[e.prop]?w.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[w.cssProps[e.prop]]&&!w.cssHooks[e.prop]?e.elem[e.prop]=e.now:w.style(e.elem,e.prop,e.now+e.unit)}}},tt.propHooks.scrollTop=tt.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},w.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},w.fx=tt.prototype.init,w.fx.step={};var nt,rt,it=/^(?:toggle|show|hide)$/,ot=/queueHooks$/;function at(){rt&&(!1===r.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(at):e.setTimeout(at,w.fx.interval),w.fx.tick())}function st(){return e.setTimeout(function(){nt=void 0}),nt=Date.now()}function ut(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=oe[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function lt(e,t,n){for(var r,i=(pt.tweeners[t]||[]).concat(pt.tweeners["*"]),o=0,a=i.length;o1)},removeAttr:function(e){return this.each(function(){w.removeAttr(this,e)})}}),w.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?w.prop(e,t,n):(1===o&&w.isXMLDoc(e)||(i=w.attrHooks[t.toLowerCase()]||(w.expr.match.bool.test(t)?dt:void 0)),void 0!==n?null===n?void w.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=w.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!h.radioValue&&"radio"===t&&N(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(M);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),dt={set:function(e,t,n){return!1===t?w.removeAttr(e,n):e.setAttribute(n,n),n}},w.each(w.expr.match.bool.source.match(/\w+/g),function(e,t){var n=ht[t]||w.find.attr;ht[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=ht[a],ht[a]=i,i=null!=n(e,t,r)?a:null,ht[a]=o),i}});var gt=/^(?:input|select|textarea|button)$/i,yt=/^(?:a|area)$/i;w.fn.extend({prop:function(e,t){return z(this,w.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[w.propFix[e]||e]})}}),w.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&w.isXMLDoc(e)||(t=w.propFix[t]||t,i=w.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=w.find.attr(e,"tabindex");return t?parseInt(t,10):gt.test(e.nodeName)||yt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),h.optSelected||(w.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),w.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){w.propFix[this.toLowerCase()]=this});function vt(e){return(e.match(M)||[]).join(" ")}function mt(e){return e.getAttribute&&e.getAttribute("class")||""}function xt(e){return Array.isArray(e)?e:"string"==typeof e?e.match(M)||[]:[]}w.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).addClass(e.call(this,t,mt(this)))});if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).removeClass(e.call(this,t,mt(this)))});if(!arguments.length)return this.attr("class","");if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])while(r.indexOf(" "+o+" ")>-1)r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):g(e)?this.each(function(n){w(this).toggleClass(e.call(this,n,mt(this),t),t)}):this.each(function(){var t,i,o,a;if(r){i=0,o=w(this),a=xt(e);while(t=a[i++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else void 0!==e&&"boolean"!==n||((t=mt(this))&&J.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":J.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&(" "+vt(mt(n))+" ").indexOf(t)>-1)return!0;return!1}});var bt=/\r/g;w.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=g(e),this.each(function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,w(this).val()):e)?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=w.map(i,function(e){return null==e?"":e+""})),(t=w.valHooks[this.type]||w.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return(t=w.valHooks[i.type]||w.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(bt,""):null==n?"":n}}}),w.extend({valHooks:{option:{get:function(e){var t=w.find.attr(e,"value");return null!=t?t:vt(w.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),w.each(["radio","checkbox"],function(){w.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=w.inArray(w(e).val(),t)>-1}},h.checkOn||(w.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),h.focusin="onfocusin"in e;var wt=/^(?:focusinfocus|focusoutblur)$/,Tt=function(e){e.stopPropagation()};w.extend(w.event,{trigger:function(t,n,i,o){var a,s,u,l,c,p,d,h,v=[i||r],m=f.call(t,"type")?t.type:t,x=f.call(t,"namespace")?t.namespace.split("."):[];if(s=h=u=i=i||r,3!==i.nodeType&&8!==i.nodeType&&!wt.test(m+w.event.triggered)&&(m.indexOf(".")>-1&&(m=(x=m.split(".")).shift(),x.sort()),c=m.indexOf(":")<0&&"on"+m,t=t[w.expando]?t:new w.Event(m,"object"==typeof t&&t),t.isTrigger=o?2:3,t.namespace=x.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+x.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=i),n=null==n?[t]:w.makeArray(n,[t]),d=w.event.special[m]||{},o||!d.trigger||!1!==d.trigger.apply(i,n))){if(!o&&!d.noBubble&&!y(i)){for(l=d.delegateType||m,wt.test(l+m)||(s=s.parentNode);s;s=s.parentNode)v.push(s),u=s;u===(i.ownerDocument||r)&&v.push(u.defaultView||u.parentWindow||e)}a=0;while((s=v[a++])&&!t.isPropagationStopped())h=s,t.type=a>1?l:d.bindType||m,(p=(J.get(s,"events")||{})[t.type]&&J.get(s,"handle"))&&p.apply(s,n),(p=c&&s[c])&&p.apply&&Y(s)&&(t.result=p.apply(s,n),!1===t.result&&t.preventDefault());return t.type=m,o||t.isDefaultPrevented()||d._default&&!1!==d._default.apply(v.pop(),n)||!Y(i)||c&&g(i[m])&&!y(i)&&((u=i[c])&&(i[c]=null),w.event.triggered=m,t.isPropagationStopped()&&h.addEventListener(m,Tt),i[m](),t.isPropagationStopped()&&h.removeEventListener(m,Tt),w.event.triggered=void 0,u&&(i[c]=u)),t.result}},simulate:function(e,t,n){var r=w.extend(new w.Event,n,{type:e,isSimulated:!0});w.event.trigger(r,null,t)}}),w.fn.extend({trigger:function(e,t){return this.each(function(){w.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return w.event.trigger(e,t,n,!0)}}),h.focusin||w.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){w.event.simulate(t,e.target,w.event.fix(e))};w.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=J.access(r,t);i||r.addEventListener(e,n,!0),J.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=J.access(r,t)-1;i?J.access(r,t,i):(r.removeEventListener(e,n,!0),J.remove(r,t))}}});var Ct=e.location,Et=Date.now(),kt=/\?/;w.parseXML=function(t){var n;if(!t||"string"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,"text/xml")}catch(e){n=void 0}return n&&!n.getElementsByTagName("parsererror").length||w.error("Invalid XML: "+t),n};var St=/\[\]$/,Dt=/\r?\n/g,Nt=/^(?:submit|button|image|reset|file)$/i,At=/^(?:input|select|textarea|keygen)/i;function jt(e,t,n,r){var i;if(Array.isArray(t))w.each(t,function(t,i){n||St.test(e)?r(e,i):jt(e+"["+("object"==typeof i&&null!=i?t:"")+"]",i,n,r)});else if(n||"object"!==x(t))r(e,t);else for(i in t)jt(e+"["+i+"]",t[i],n,r)}w.param=function(e,t){var n,r=[],i=function(e,t){var n=g(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!w.isPlainObject(e))w.each(e,function(){i(this.name,this.value)});else for(n in e)jt(n,e[n],t,i);return r.join("&")},w.fn.extend({serialize:function(){return w.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=w.prop(this,"elements");return e?w.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!w(this).is(":disabled")&&At.test(this.nodeName)&&!Nt.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=w(this).val();return null==n?null:Array.isArray(n)?w.map(n,function(e){return{name:t.name,value:e.replace(Dt,"\r\n")}}):{name:t.name,value:n.replace(Dt,"\r\n")}}).get()}});var qt=/%20/g,Lt=/#.*$/,Ht=/([?&])_=[^&]*/,Ot=/^(.*?):[ \t]*([^\r\n]*)$/gm,Pt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Mt=/^(?:GET|HEAD)$/,Rt=/^\/\//,It={},Wt={},$t="*/".concat("*"),Bt=r.createElement("a");Bt.href=Ct.href;function Ft(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(M)||[];if(g(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function _t(e,t,n,r){var i={},o=e===Wt;function a(s){var u;return i[s]=!0,w.each(e[s]||[],function(e,s){var l=s(t,n,r);return"string"!=typeof l||o||i[l]?o?!(u=l):void 0:(t.dataTypes.unshift(l),a(l),!1)}),u}return a(t.dataTypes[0])||!i["*"]&&a("*")}function zt(e,t){var n,r,i=w.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&w.extend(!0,e,r),e}function Xt(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}function Ut(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}w.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ct.href,type:"GET",isLocal:Pt.test(Ct.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":$t,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":w.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?zt(zt(e,w.ajaxSettings),t):zt(w.ajaxSettings,e)},ajaxPrefilter:Ft(It),ajaxTransport:Ft(Wt),ajax:function(t,n){"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,p,d,h=w.ajaxSetup({},n),g=h.context||h,y=h.context&&(g.nodeType||g.jquery)?w(g):w.event,v=w.Deferred(),m=w.Callbacks("once memory"),x=h.statusCode||{},b={},T={},C="canceled",E={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s){s={};while(t=Ot.exec(a))s[t[1].toLowerCase()]=t[2]}t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return c?a:null},setRequestHeader:function(e,t){return null==c&&(e=T[e.toLowerCase()]=T[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)E.always(e[E.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||C;return i&&i.abort(t),k(0,t),this}};if(v.promise(E),h.url=((t||h.url||Ct.href)+"").replace(Rt,Ct.protocol+"//"),h.type=n.method||n.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(M)||[""],null==h.crossDomain){l=r.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=Bt.protocol+"//"+Bt.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=w.param(h.data,h.traditional)),_t(It,h,n,E),c)return E;(f=w.event&&h.global)&&0==w.active++&&w.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!Mt.test(h.type),o=h.url.replace(Lt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(qt,"+")):(d=h.url.slice(o.length),h.data&&(h.processData||"string"==typeof h.data)&&(o+=(kt.test(o)?"&":"?")+h.data,delete h.data),!1===h.cache&&(o=o.replace(Ht,"$1"),d=(kt.test(o)?"&":"?")+"_="+Et+++d),h.url=o+d),h.ifModified&&(w.lastModified[o]&&E.setRequestHeader("If-Modified-Since",w.lastModified[o]),w.etag[o]&&E.setRequestHeader("If-None-Match",w.etag[o])),(h.data&&h.hasContent&&!1!==h.contentType||n.contentType)&&E.setRequestHeader("Content-Type",h.contentType),E.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+$t+"; q=0.01":""):h.accepts["*"]);for(p in h.headers)E.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(!1===h.beforeSend.call(g,E,h)||c))return E.abort();if(C="abort",m.add(h.complete),E.done(h.success),E.fail(h.error),i=_t(Wt,h,n,E)){if(E.readyState=1,f&&y.trigger("ajaxSend",[E,h]),c)return E;h.async&&h.timeout>0&&(u=e.setTimeout(function(){E.abort("timeout")},h.timeout));try{c=!1,i.send(b,k)}catch(e){if(c)throw e;k(-1,e)}}else k(-1,"No Transport");function k(t,n,r,s){var l,p,d,b,T,C=n;c||(c=!0,u&&e.clearTimeout(u),i=void 0,a=s||"",E.readyState=t>0?4:0,l=t>=200&&t<300||304===t,r&&(b=Xt(h,E,r)),b=Ut(h,b,E,l),l?(h.ifModified&&((T=E.getResponseHeader("Last-Modified"))&&(w.lastModified[o]=T),(T=E.getResponseHeader("etag"))&&(w.etag[o]=T)),204===t||"HEAD"===h.type?C="nocontent":304===t?C="notmodified":(C=b.state,p=b.data,l=!(d=b.error))):(d=C,!t&&C||(C="error",t<0&&(t=0))),E.status=t,E.statusText=(n||C)+"",l?v.resolveWith(g,[p,C,E]):v.rejectWith(g,[E,C,d]),E.statusCode(x),x=void 0,f&&y.trigger(l?"ajaxSuccess":"ajaxError",[E,h,l?p:d]),m.fireWith(g,[E,C]),f&&(y.trigger("ajaxComplete",[E,h]),--w.active||w.event.trigger("ajaxStop")))}return E},getJSON:function(e,t,n){return w.get(e,t,n,"json")},getScript:function(e,t){return w.get(e,void 0,t,"script")}}),w.each(["get","post"],function(e,t){w[t]=function(e,n,r,i){return g(n)&&(i=i||r,r=n,n=void 0),w.ajax(w.extend({url:e,type:t,dataType:i,data:n,success:r},w.isPlainObject(e)&&e))}}),w._evalUrl=function(e){return w.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},w.fn.extend({wrapAll:function(e){var t;return this[0]&&(g(e)&&(e=e.call(this[0])),t=w(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return g(e)?this.each(function(t){w(this).wrapInner(e.call(this,t))}):this.each(function(){var t=w(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=g(e);return this.each(function(n){w(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){w(this).replaceWith(this.childNodes)}),this}}),w.expr.pseudos.hidden=function(e){return!w.expr.pseudos.visible(e)},w.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},w.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var Vt={0:200,1223:204},Gt=w.ajaxSettings.xhr();h.cors=!!Gt&&"withCredentials"in Gt,h.ajax=Gt=!!Gt,w.ajaxTransport(function(t){var n,r;if(h.cors||Gt&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");for(a in i)s.setRequestHeader(a,i[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o(Vt[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=s.ontimeout=n("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout(function(){n&&r()})},n=n("abort");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}}),w.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),w.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return w.globalEval(e),e}}}),w.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),w.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(i,o){t=w(" + + + + + + + + diff --git a/dockers/web/html/deja_kaznaute.html b/dockers/web/html/deja_kaznaute.html new file mode 100644 index 0000000..6b5d1c0 --- /dev/null +++ b/dockers/web/html/deja_kaznaute.html @@ -0,0 +1,129 @@ + + + + + Cochez la KAZ du libre! + + + + + + + + +
    + + +
    + + + + + +
    +
    + +

    Vous êtes déjà kaznaute

    + + +
    + +

    Voici les services auxquels vous pouvez accéder en vous connectant grâce à votre email @kaz :

    + + + + +
    + + +

    Si vous avez souscrit à une offre de type ORGANISATION alors vous avez accès à des services supplémentaires. + Dans les adresses suivantes, il faut remplacer ORGA par le nom de votre orga à vous:

    + + + + + + + +
    + + + + +
    + +
    +
    + + +
    + +
    + + + + + + + + + + + diff --git a/dockers/web/html/elements.html b/dockers/web/html/elements.html new file mode 100644 index 0000000..0a1db61 --- /dev/null +++ b/dockers/web/html/elements.html @@ -0,0 +1,354 @@ + + + + + Cochez la KAZ du libre! + + + + + + + + +
    + + +
    + + + + + +
    +
    +

    Elements

    + + +
    +

    Text

    +

    This is bold and this is strong. This is italic and this is emphasized. + This is superscript text and this is subscript text. + This is underlined and this is code: for (;;) { ... }. Finally, this is a link.

    +
    +

    Nunc lacinia ante nunc ac lobortis. Interdum adipiscing gravida odio porttitor sem non mi integer non faucibus ornare mi ut ante amet placerat aliquet. Volutpat eu sed ante lacinia sapien lorem accumsan varius montes viverra nibh in adipiscing blandit tempus accumsan.

    +
    +

    Heading Level 2

    +

    Heading Level 3

    +

    Heading Level 4

    +
    +

    Blockquote

    +
    Fringilla nisl. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan faucibus. Vestibulum ante ipsum primis in faucibus lorem ipsum dolor sit amet nullam adipiscing eu felis.
    +

    Preformatted

    +
    i = 0;
    +
    +while (!deck.isInOrder()) {
    +    print 'Iteration ' + i;
    +    deck.shuffle();
    +    i++;
    +}
    +
    +print 'It took ' + i + ' iterations to sort the deck.';
    +
    + + +
    +

    Lists

    +
    +
    +

    Unordered

    +
      +
    • Dolor pulvinar etiam.
    • +
    • Sagittis adipiscing.
    • +
    • Felis enim feugiat.
    • +
    +

    Alternate

    +
      +
    • Dolor pulvinar etiam.
    • +
    • Sagittis adipiscing.
    • +
    • Felis enim feugiat.
    • +
    +
    +
    +

    Ordered

    +
      +
    1. Dolor pulvinar etiam.
    2. +
    3. Etiam vel felis viverra.
    4. +
    5. Felis enim feugiat.
    6. +
    7. Dolor pulvinar etiam.
    8. +
    9. Etiam vel felis lorem.
    10. +
    11. Felis enim et feugiat.
    12. +
    +

    Icons

    + + +
    +
    +

    Actions

    +
    +
    + + + + +
    +
    + + +
    +
    +
    + + +
    +

    Table

    +

    Default

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameDescriptionPrice
    Item OneAnte turpis integer aliquet porttitor.29.99
    Item TwoVis ac commodo adipiscing arcu aliquet.19.99
    Item Three Morbi faucibus arcu accumsan lorem.29.99
    Item FourVitae integer tempus condimentum.19.99
    Item FiveAnte turpis integer aliquet porttitor.29.99
    100.00
    +
    + +

    Alternate

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameDescriptionPrice
    Item OneAnte turpis integer aliquet porttitor.29.99
    Item TwoVis ac commodo adipiscing arcu aliquet.19.99
    Item Three Morbi faucibus arcu accumsan lorem.29.99
    Item FourVitae integer tempus condimentum.19.99
    Item FiveAnte turpis integer aliquet porttitor.29.99
    100.00
    +
    +
    + + +
    +

    Buttons

    + + + + + +
      +
    • Disabled
    • +
    • Disabled
    • +
    +
    + + +
    +

    Form

    +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + +
    +
    +
      +
    • +
    • +
    +
    +
    +
    +
    + + +
    +

    Image

    +

    Fit

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Left & Right

    +

    Fringilla nisl. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent.

    +

    Fringilla nisl. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent.

    +
    + +
    +
    + + +
    + +
    + + + + + + + + + + + diff --git a/dockers/web/html/favicon.ico b/dockers/web/html/favicon.ico new file mode 100644 index 0000000..47de6bf Binary files /dev/null and b/dockers/web/html/favicon.ico differ diff --git a/dockers/web/html/images/1f609.png b/dockers/web/html/images/1f609.png new file mode 100644 index 0000000..9323028 Binary files /dev/null and b/dockers/web/html/images/1f609.png differ diff --git a/dockers/web/html/images/2705.png b/dockers/web/html/images/2705.png new file mode 100644 index 0000000..1854f44 Binary files /dev/null and b/dockers/web/html/images/2705.png differ diff --git a/dockers/web/html/images/actualites.png b/dockers/web/html/images/actualites.png new file mode 100644 index 0000000..10cb03e Binary files /dev/null and b/dockers/web/html/images/actualites.png differ diff --git a/dockers/web/html/images/calc.jpg b/dockers/web/html/images/calc.jpg new file mode 100644 index 0000000..fa81b67 Binary files /dev/null and b/dockers/web/html/images/calc.jpg differ diff --git a/dockers/web/html/images/chatons.png b/dockers/web/html/images/chatons.png new file mode 100644 index 0000000..dd9d1a5 Binary files /dev/null and b/dockers/web/html/images/chatons.png differ diff --git a/dockers/web/html/images/confiance_agglo_vannes.png b/dockers/web/html/images/confiance_agglo_vannes.png new file mode 100644 index 0000000..74ae54a Binary files /dev/null and b/dockers/web/html/images/confiance_agglo_vannes.png differ diff --git a/dockers/web/html/images/confiance_ess.png b/dockers/web/html/images/confiance_ess.png new file mode 100644 index 0000000..9d86af2 Binary files /dev/null and b/dockers/web/html/images/confiance_ess.png differ diff --git a/dockers/web/html/images/confiance_iut.png b/dockers/web/html/images/confiance_iut.png new file mode 100644 index 0000000..dac86d8 Binary files /dev/null and b/dockers/web/html/images/confiance_iut.png differ diff --git a/dockers/web/html/images/date.jpg b/dockers/web/html/images/date.jpg new file mode 100644 index 0000000..336c5e4 Binary files /dev/null and b/dockers/web/html/images/date.jpg differ diff --git a/dockers/web/html/images/deja_kaznaute.jpg b/dockers/web/html/images/deja_kaznaute.jpg new file mode 100644 index 0000000..6d75616 Binary files /dev/null and b/dockers/web/html/images/deja_kaznaute.jpg differ diff --git a/dockers/web/html/images/file.jpg b/dockers/web/html/images/file.jpg new file mode 100644 index 0000000..f31ef08 Binary files /dev/null and b/dockers/web/html/images/file.jpg differ diff --git a/dockers/web/html/images/forms.jpg b/dockers/web/html/images/forms.jpg new file mode 100644 index 0000000..4d822eb Binary files /dev/null and b/dockers/web/html/images/forms.jpg differ diff --git a/dockers/web/html/images/generic/pic01.jpg b/dockers/web/html/images/generic/pic01.jpg new file mode 100644 index 0000000..642a6b2 Binary files /dev/null and b/dockers/web/html/images/generic/pic01.jpg differ diff --git a/dockers/web/html/images/generic/pic02.jpg b/dockers/web/html/images/generic/pic02.jpg new file mode 100644 index 0000000..5bddb6d Binary files /dev/null and b/dockers/web/html/images/generic/pic02.jpg differ diff --git a/dockers/web/html/images/generic/pic03.jpg b/dockers/web/html/images/generic/pic03.jpg new file mode 100644 index 0000000..1754490 Binary files /dev/null and b/dockers/web/html/images/generic/pic03.jpg differ diff --git a/dockers/web/html/images/generic/pic04.jpg b/dockers/web/html/images/generic/pic04.jpg new file mode 100644 index 0000000..7deb571 Binary files /dev/null and b/dockers/web/html/images/generic/pic04.jpg differ diff --git a/dockers/web/html/images/generic/pic05.jpg b/dockers/web/html/images/generic/pic05.jpg new file mode 100644 index 0000000..8f5e523 Binary files /dev/null and b/dockers/web/html/images/generic/pic05.jpg differ diff --git a/dockers/web/html/images/generic/pic06.jpg b/dockers/web/html/images/generic/pic06.jpg new file mode 100644 index 0000000..64722c0 Binary files /dev/null and b/dockers/web/html/images/generic/pic06.jpg differ diff --git a/dockers/web/html/images/generic/pic07.jpg b/dockers/web/html/images/generic/pic07.jpg new file mode 100644 index 0000000..d64aba8 Binary files /dev/null and b/dockers/web/html/images/generic/pic07.jpg differ diff --git a/dockers/web/html/images/generic/pic08.jpg b/dockers/web/html/images/generic/pic08.jpg new file mode 100644 index 0000000..11bed52 Binary files /dev/null and b/dockers/web/html/images/generic/pic08.jpg differ diff --git a/dockers/web/html/images/generic/pic09.jpg b/dockers/web/html/images/generic/pic09.jpg new file mode 100644 index 0000000..7f77fe0 Binary files /dev/null and b/dockers/web/html/images/generic/pic09.jpg differ diff --git a/dockers/web/html/images/generic/pic10.jpg b/dockers/web/html/images/generic/pic10.jpg new file mode 100644 index 0000000..e4d7328 Binary files /dev/null and b/dockers/web/html/images/generic/pic10.jpg differ diff --git a/dockers/web/html/images/generic/pic11.jpg b/dockers/web/html/images/generic/pic11.jpg new file mode 100644 index 0000000..a975380 Binary files /dev/null and b/dockers/web/html/images/generic/pic11.jpg differ diff --git a/dockers/web/html/images/generic/pic12.jpg b/dockers/web/html/images/generic/pic12.jpg new file mode 100644 index 0000000..cc68345 Binary files /dev/null and b/dockers/web/html/images/generic/pic12.jpg differ diff --git a/dockers/web/html/images/generic/pic13.jpg b/dockers/web/html/images/generic/pic13.jpg new file mode 100644 index 0000000..dc892a2 Binary files /dev/null and b/dockers/web/html/images/generic/pic13.jpg differ diff --git a/dockers/web/html/images/generic/pic14.jpg b/dockers/web/html/images/generic/pic14.jpg new file mode 100644 index 0000000..ab6cd9a Binary files /dev/null and b/dockers/web/html/images/generic/pic14.jpg differ diff --git a/dockers/web/html/images/generic/pic15.jpg b/dockers/web/html/images/generic/pic15.jpg new file mode 100644 index 0000000..0e94d0a Binary files /dev/null and b/dockers/web/html/images/generic/pic15.jpg differ diff --git a/dockers/web/html/images/instant_messaging.jpg b/dockers/web/html/images/instant_messaging.jpg new file mode 100644 index 0000000..7f03b09 Binary files /dev/null and b/dockers/web/html/images/instant_messaging.jpg differ diff --git a/dockers/web/html/images/link_rbg.png b/dockers/web/html/images/link_rbg.png new file mode 100644 index 0000000..f0b64eb Binary files /dev/null and b/dockers/web/html/images/link_rbg.png differ diff --git a/dockers/web/html/images/link_resolu.png b/dockers/web/html/images/link_resolu.png new file mode 100644 index 0000000..6e92141 Binary files /dev/null and b/dockers/web/html/images/link_resolu.png differ diff --git a/dockers/web/html/images/link_tycloud.png b/dockers/web/html/images/link_tycloud.png new file mode 100644 index 0000000..6273219 Binary files /dev/null and b/dockers/web/html/images/link_tycloud.png differ diff --git a/dockers/web/html/images/logo.svg b/dockers/web/html/images/logo.svg new file mode 100644 index 0000000..3c5d897 --- /dev/null +++ b/dockers/web/html/images/logo.svg @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + diff --git a/dockers/web/html/images/nextcloud.png b/dockers/web/html/images/nextcloud.png new file mode 100644 index 0000000..717d55a Binary files /dev/null and b/dockers/web/html/images/nextcloud.png differ diff --git a/dockers/web/html/images/offre_associations.jpg b/dockers/web/html/images/offre_associations.jpg new file mode 100644 index 0000000..9754c88 Binary files /dev/null and b/dockers/web/html/images/offre_associations.jpg differ diff --git a/dockers/web/html/images/offre_associations_bandeau.jpg b/dockers/web/html/images/offre_associations_bandeau.jpg new file mode 100644 index 0000000..7261638 Binary files /dev/null and b/dockers/web/html/images/offre_associations_bandeau.jpg differ diff --git a/dockers/web/html/images/offre_decouverte.jpg b/dockers/web/html/images/offre_decouverte.jpg new file mode 100644 index 0000000..2b95adc Binary files /dev/null and b/dockers/web/html/images/offre_decouverte.jpg differ diff --git a/dockers/web/html/images/offre_decouverte_bandeau.jpg b/dockers/web/html/images/offre_decouverte_bandeau.jpg new file mode 100644 index 0000000..16637ce Binary files /dev/null and b/dockers/web/html/images/offre_decouverte_bandeau.jpg differ diff --git a/dockers/web/html/images/offre_dediee.jpg b/dockers/web/html/images/offre_dediee.jpg new file mode 100644 index 0000000..9754c88 Binary files /dev/null and b/dockers/web/html/images/offre_dediee.jpg differ diff --git a/dockers/web/html/images/offre_dediee_bandeau.png b/dockers/web/html/images/offre_dediee_bandeau.png new file mode 100644 index 0000000..c2a6597 Binary files /dev/null and b/dockers/web/html/images/offre_dediee_bandeau.png differ diff --git a/dockers/web/html/images/offre_entreprises.jpg b/dockers/web/html/images/offre_entreprises.jpg new file mode 100644 index 0000000..9754c88 Binary files /dev/null and b/dockers/web/html/images/offre_entreprises.jpg differ diff --git a/dockers/web/html/images/offre_entreprises_bandeau.png b/dockers/web/html/images/offre_entreprises_bandeau.png new file mode 100644 index 0000000..c2a6597 Binary files /dev/null and b/dockers/web/html/images/offre_entreprises_bandeau.png differ diff --git a/dockers/web/html/images/offre_familles.jpg b/dockers/web/html/images/offre_familles.jpg new file mode 100644 index 0000000..9754c88 Binary files /dev/null and b/dockers/web/html/images/offre_familles.jpg differ diff --git a/dockers/web/html/images/offre_familles_bandeau.jpg b/dockers/web/html/images/offre_familles_bandeau.jpg new file mode 100644 index 0000000..5b6cb1a Binary files /dev/null and b/dockers/web/html/images/offre_familles_bandeau.jpg differ diff --git a/dockers/web/html/images/offre_organisations.jpg b/dockers/web/html/images/offre_organisations.jpg new file mode 100644 index 0000000..9754c88 Binary files /dev/null and b/dockers/web/html/images/offre_organisations.jpg differ diff --git a/dockers/web/html/images/offre_organisations_bandeau.jpg b/dockers/web/html/images/offre_organisations_bandeau.jpg new file mode 100644 index 0000000..7261638 Binary files /dev/null and b/dockers/web/html/images/offre_organisations_bandeau.jpg differ diff --git a/dockers/web/html/images/offre_particuliers.jpg b/dockers/web/html/images/offre_particuliers.jpg new file mode 100644 index 0000000..6eb4138 Binary files /dev/null and b/dockers/web/html/images/offre_particuliers.jpg differ diff --git a/dockers/web/html/images/offre_particuliers_bandeau.jpg b/dockers/web/html/images/offre_particuliers_bandeau.jpg new file mode 100644 index 0000000..1a57738 Binary files /dev/null and b/dockers/web/html/images/offre_particuliers_bandeau.jpg differ diff --git a/dockers/web/html/images/pad.jpg b/dockers/web/html/images/pad.jpg new file mode 100644 index 0000000..323cb53 Binary files /dev/null and b/dockers/web/html/images/pad.jpg differ diff --git a/dockers/web/html/images/pourquoi_kaz.png b/dockers/web/html/images/pourquoi_kaz.png new file mode 100644 index 0000000..c2a6597 Binary files /dev/null and b/dockers/web/html/images/pourquoi_kaz.png differ diff --git a/dockers/web/html/images/qui_sommes_nous.png b/dockers/web/html/images/qui_sommes_nous.png new file mode 100644 index 0000000..871513e Binary files /dev/null and b/dockers/web/html/images/qui_sommes_nous.png differ diff --git a/dockers/web/html/images/tableur.jpg b/dockers/web/html/images/tableur.jpg new file mode 100644 index 0000000..949710f Binary files /dev/null and b/dockers/web/html/images/tableur.jpg differ diff --git a/dockers/web/html/images/visio.jpg b/dockers/web/html/images/visio.jpg new file mode 100644 index 0000000..b013ac5 Binary files /dev/null and b/dockers/web/html/images/visio.jpg differ diff --git a/dockers/web/html/images/wiki.jpg b/dockers/web/html/images/wiki.jpg new file mode 100644 index 0000000..ecb7208 Binary files /dev/null and b/dockers/web/html/images/wiki.jpg differ diff --git a/dockers/web/html/index.html b/dockers/web/html/index.html new file mode 100644 index 0000000..93a9761 --- /dev/null +++ b/dockers/web/html/index.html @@ -0,0 +1,265 @@ + + + + Cochez la KAZ du libre ! + + + + + + + + + + + +
    + + +
    + + + + + +
    +
    +
    +

    Bienvenue chez KAZ, ici, on prend soin de vos données et on ne les vend pas !

    + + +

    +Nous sommes un joyeux collectif de citoyens provenant d’horizons divers, dotés de compétences diverses, qui partageons une vision commune des enjeux humains, écologiques, techniques, économiques, pédagogiques, artistiques, juridiques et politiques pour l'avenir de l'humanité et de notre planète.

    + +

    Pour changer le monde (rien que ça !), on commence par des services numériques cousus mains, respectueux de la vie privée, hébergés en Bretagne, pour tous les particuliers, associations et entreprises.

    + +

    Comme association à but non lucratif, nous fonctionnons avec des bénévoles et des dons. C'est bien comme cela que nous pouvons vivre sans vendre vos données. Un soutien de votre part sera toujours le bienvenu.

    + +

    Laissez-vous happer par un petit vent de libertésmile

    + +
    + + +
    + +
    +
    + + + +
    +
    + +
    + + +
    + + + + + + + +
    + +

    +

    Des services gratuits et sécurisés que vous soyez adhérents ou pas !

    +
    + + + + + + + + + + + + + + + +
    + +

    +

    Cochez la Kaz du libre !
    smile

    +
    +
    + + +
    + +
    + + + + + + + + + + + diff --git a/dockers/web/html/m/Readme.txt b/dockers/web/html/m/Readme.txt new file mode 100644 index 0000000..d468823 --- /dev/null +++ b/dockers/web/html/m/Readme.txt @@ -0,0 +1,2 @@ +Il faut concerver ce répertoire utilisé par la signature de KAZ dans les messages. + diff --git a/dockers/web/html/m/coche.png b/dockers/web/html/m/coche.png new file mode 100644 index 0000000..7cf8728 Binary files /dev/null and b/dockers/web/html/m/coche.png differ diff --git a/dockers/web/html/m/email.css b/dockers/web/html/m/email.css new file mode 100644 index 0000000..1167550 --- /dev/null +++ b/dockers/web/html/m/email.css @@ -0,0 +1,69 @@ +div.kaz:hover { + font-size: initial !important; + color: initial !important; +} +div.kaz:hover a.kaz { + background-size: initial !important; + padding: 4px 0 4px 230px; +} +div.kaz a.kaz { + background-size: 110px 12px; + padding: 4px 0 4px 120px; +} +div.kaz { + font-size: 10px; + color: #969696; + padding: 1pc 0 0 0; + margin: 0 0 0 80px; + min-height: 200px; + clear: left; +} + +div.kaz>p.gray { + font-size: 10px; + color: #969696; +} + +div.kaz::before { + content: url("/m/logo.png"); + position: absolute; + padding: 0; + margin: 0 0 0 -70px; + width: 50px; + height: 100px; +} +div.kaz>ul>li { + list-style-type: none; /* Remove bullets */ +} +div.kaz>ul>li::before { + content: "\2713"; + color: green; + margin-left: -20px; + margin-right: 10px; +} +a.kaz { + background-image: url("/m/coche.png"); + background-repeat: no-repeat; + padding: 4px 0 4px 230px; + margin: 0 0 0 0; + min-height: 25px; +} +div.kaz:hover::after { + font-size: initial !important; + color: initial !important; + border-width: medium; + border-color: red; +} +div.kaz::after { + font-size: 10px; + color: #969696; + content: "Attention : Kaz a dépollué ce message. Les pièces jointes ont été retirées et placées dans un dépôt provisoire. Elles seront automatiquement supprimées dans 1 mois. Si elles sont importantes et que vous souhaitez les conserver, vous devez utiliser les liens ci-dessus. Pour mieux comprendre la politique de nos services visitez kaz.bzh"; + width: 40pc; + padding: 1pc; + margin: 0 0 0 -70px; + display: block; + border-radius: 30px; + border-style: solid; + border-width: thin; + border-color: red; +} diff --git a/dockers/web/html/m/kaz-50.png b/dockers/web/html/m/kaz-50.png new file mode 120000 index 0000000..bd36e71 --- /dev/null +++ b/dockers/web/html/m/kaz-50.png @@ -0,0 +1 @@ +logo.png \ No newline at end of file diff --git a/dockers/web/html/m/kaz-du-libre-23.png b/dockers/web/html/m/kaz-du-libre-23.png new file mode 120000 index 0000000..b28225b --- /dev/null +++ b/dockers/web/html/m/kaz-du-libre-23.png @@ -0,0 +1 @@ +coche.png \ No newline at end of file diff --git a/dockers/web/html/m/logo.png b/dockers/web/html/m/logo.png new file mode 100644 index 0000000..f508ed7 Binary files /dev/null and b/dockers/web/html/m/logo.png differ diff --git a/dockers/web/html/mail-cleanup b/dockers/web/html/mail-cleanup new file mode 120000 index 0000000..08b9811 --- /dev/null +++ b/dockers/web/html/mail-cleanup @@ -0,0 +1 @@ +m \ No newline at end of file diff --git a/dockers/web/html/mail/config-v1.1.xml b/dockers/web/html/mail/config-v1.1.xml new file mode 100644 index 0000000..9abafba --- /dev/null +++ b/dockers/web/html/mail/config-v1.1.xml @@ -0,0 +1,28 @@ + + + + + kaz.bzh + + kaz.bzh + Kaz + + mail.kaz.bzh + 993 + SSL + password-cleartext + %EMAILADDRESS% + + + smtp.kaz.bzh + 587 + STARTTLS + password-cleartext + %EMAILADDRESS% + + + + + diff --git a/dockers/web/html/mentions_legales.html b/dockers/web/html/mentions_legales.html new file mode 100644 index 0000000..3584b9c --- /dev/null +++ b/dockers/web/html/mentions_legales.html @@ -0,0 +1,97 @@ + + + + + Cochez la KAZ du libre! + + + + + + + + +
    + + +
    + + + + + +
    +
    +

    Conformité au RGPD

    + + +
    + +

    +Les informations recueillies sur ce site ou sur les outils proposés par KAZ sont enregistrées dans un fichier informatisé par la personne morale KAZ afin de fournir les services proposés par KAZ. La base légale du traitement est l'intérêt légitime. +

    + +

    +Les données collectées ne seront communiquées à aucun tiers. +

    + +

    +Les données sont conservées pendant le temps nécessaire à l'exécution du bon fonctionnement des services KAZ. +

    + +

    +Les données gérées par KAZ sont hébergées chez grifon.fr, association à but non lucratif située à Rennes. +

    + +

    +Les sauvegardes ont lieu quotidiennement dans un espace sécurisé et permettent d'assurer la continuité de services attendues par les utilisateurs +

    + +

    +Vous pouvez accéder aux données vous concernant, les rectifier, demander leur effacement ou exercer votre droit à la limitation du traitement de vos données. (en fonction de la base légale du traitement, mentionner également : Vous pouvez retirer à tout moment votre consentement au traitement de vos données ; Vous pouvez également vous opposer au traitement de vos données ; Vous pouvez également exercer votre droit à la portabilité de vos données) +

    + +

    +Pour toute demande, merci de contacter Fab, membre de la collegiale, à l' adresse contact@kaz.bzh +

    + +

    +Si vous estimez, après nous avoir contactés, que vos droits « Informatique et Libertés » ne sont pas respectés, vous pouvez adresser une réclamation sur le site cnil.fr pour plus d’informations . +

    + +

    +Pour exercer ces droits ou pour toute question sur le traitement de vos données dans ce dispositif, vous pouvez contacter (le cas échéant, notre délégué à la protection des données ou le service chargé de l’exercice de ces droits) : +Fab, membre de la collégiale, sur contact@kaz.bzh +

    + +

    +Adresse postale du siège de l'association KAZ: Maison des associations, Rue Guillaume le Bartz, Vannes (56000) +

    + + +
    + +

    Cochez la Kaz du libre !
    smile

    + +
    +
    + + +
    + +
    + + + + + + + + + + + diff --git a/dockers/web/html/offre_associations.html b/dockers/web/html/offre_associations.html new file mode 100644 index 0000000..0642d71 --- /dev/null +++ b/dockers/web/html/offre_associations.html @@ -0,0 +1,77 @@ + + + + + Cochez la KAZ du libre! + + + + + + + + +
    + + +
    + + + + + +
    +
    +

    Offre pour les Associations

    + + + + +
    + + KAZ vous propose jusqu'à 5 adresses électroniques en @kaz.bzh ainsi qu'un espace sur notre cloud qui vous donne accès à une boîte à outils de logiciels libres orientés partage et collaboration. + Pour 30€/an, vous avez un espace de 10Go, pour 40€/an, vous avez un espace de 20Go, pour 50€/an, vous avez un espace de 30Go, etc... + +
    + + Votre activité (données et logiciels) est totalement cloisonnée au sein de KAZ, grâce à un environnement dédié, indépendant des autres utilisateurs. + +
    + + Vous aurez acces à un espace de discussion pour vos membres actifs, un espace de publication de site Web, une solution de comptabilité en ligne et la possibilité de créer des listes de diffusion pour vos adhérents et vos sympathisants. + Vous manquez d'espace ? vous pouvez demander des compléments de 10Go + +
    + + Vos données sont stockées sur des serveurs en Bretagne. + KAZ garantit leur protection. + +
    + + Disponibilité et sauvegardes : Nous faisons tout pour assurer un service de qualité, disponible et pérenne mais nous vous conseillons quand même de prendre vos précautions ! Faites aussi des sauvegardes de vos propres données ! + +
    +

    Cochez la Kaz du libre !
    smile

    + +
    + +
    +
    + + +
    + +
    + + + + + + + + + + + diff --git a/dockers/web/html/offre_decouverte.html b/dockers/web/html/offre_decouverte.html new file mode 100644 index 0000000..89f2d71 --- /dev/null +++ b/dockers/web/html/offre_decouverte.html @@ -0,0 +1,91 @@ + + + + + Cochez la KAZ du libre! + + + + + + + + +
    + + +
    + + + + + +
    +
    +

    Offre Découverte

    + + + +
    + +

    Vous êtes un particulier.

    + +Vous utilisez des services des GAFAM: Google, Apple, Facebook, Amazon et Microsoft. +Ces mégacompagnies vous offrent des solutions performantes pour communiquer et organiser vos données. +Ces services vous sont présentés comme gratuits pour la plupart mais... + +
    + +Mais vous savez bien qu'en contre-partie des services apportés, vous leur livrez gratuitement vos données personnelles, vos habitudes de vie, de consommation, de relations, ... +Ces pratiques annoncent un monde inquiétant déjà en oeuvre : surveillance généralisée, comportements de consommation, sociaux, politiques, ... +Et le tout orienté par des algorythmes toujours plus puissants. + +
    + +

    Comment résister ?

    + + +Les Etats ont une responsabilté majeure de régulation de ces mégacompagnies. + +Mais nous pouvons aussi agir ici et maintenant. + +
    + +KAZ vous propose solidairement une adresse électronique gratuite du type @kaz.bzh ainsi qu'un espace sur notre cloud de 1 Go qui vous donne accès à une boîte à outils de logiciels libres orientés partage et collaboration. +Vos données sont stockées sur des serveurs en Bretagne. +KAZ garantit leur protection. +Rejoignez-nous dans cette offre découverte ! + +
    + +Disponibilité et sauvegardes : Nous faisons tout pour assurer un service de qualité, disponible et pérenne mais nous vous conseillons quand même de prendre vos précautions ! Faites aussi des sauvegardes de vos propres données ! + +
    + +

    Ensuite, si vous êtes convaincus, devenez membre en souscrivant à l'offre particuliers à partir de 10 € par an.

    + +

    Cochez la Kaz du libre !
    smile

    + +
    + + +
    +
    + + +
    + +
    + + + + + + + + + + + diff --git a/dockers/web/html/offre_dedie.html b/dockers/web/html/offre_dedie.html new file mode 100644 index 0000000..c184bd1 --- /dev/null +++ b/dockers/web/html/offre_dedie.html @@ -0,0 +1,55 @@ + + + + + Cochez la KAZ du libre! + + + + + + + + +
    + + +
    + + + + + +
    +
    +

    Offre pour les Organisations

    + + + +
    +Afin de mieux préparer l'arrivée de Kaz dans le monde de l'hébergement pour les entreprises et associations, nous continuons à travailler sur notre future offre. Prenez contact avec nous pour en discuter! +
    + +

    Cochez la Kaz du libre !
    smile

    + + +
    +
    + + +
    + +
    + + + + + + + + + + + diff --git a/dockers/web/html/offre_dediee.html b/dockers/web/html/offre_dediee.html new file mode 100644 index 0000000..9324de6 --- /dev/null +++ b/dockers/web/html/offre_dediee.html @@ -0,0 +1,69 @@ + + + + + Cochez la KAZ du libre! + + + + + + + + +
    + + +
    + + + + + +
    +
    +

    Offre dédiée

    + + + +
    + +
    + Si vous avez de gros besoins en performances / outils / place disque alors nous pouvons vous mettre à disposition un serveur dédié regroupant l'ensemble des services KAZ-Entreprise. Cela signifie qu'un serveur unique est dédié à votre entreprise. Prenez contact avec nous pour en discuter! + +
    + Vos données sont stockées sur des serveurs en Bretagne. + KAZ garantit leur protection. + +
    + Sur les serveurs dédiés, disponibilité et sauvegardes sont les 2 axes privilégiés par KAZ. Vos données sont répliquées chaque nuit sur nos serveurs de backup et peuvent être restaurées à la moindre demande. + +
    + La veille sécurité et le contrôle de performances sont assurés par KAZ H24 par l'utilisation de sondes implantées sur vos machines. + +
    +

    Cochez la Kaz du libre !
    smile

    + +
    + + +
    +
    + + +
    + +
    + + + + + + + + + + + diff --git a/dockers/web/html/offre_entreprises.html b/dockers/web/html/offre_entreprises.html new file mode 100644 index 0000000..e570c3e --- /dev/null +++ b/dockers/web/html/offre_entreprises.html @@ -0,0 +1,68 @@ + + + + + Cochez la KAZ du libre! + + + + + + + + +
    + + +
    + + + + + +
    +
    +

    Offre pour les Entreprises

    + + +
    + +
    + Parce que vos besoins en messagerie, espace disque, outils de gestion sont plus importants, KAZ vous proposera les outils de base des associations adossés à un espace disque de 100Go et une solution de comptabilité professionnelle (ERP). + + Nous continuons à travailler sur notre offre aux Entreprises. Prenez contact avec nous pour en discuter! + +
    + + Vos données sont stockées sur des serveurs en Bretagne. + KAZ garantit leur protection. + +
    + + Disponibilité et sauvegardes : Nous faisons tout pour assurer un service de qualité, disponible et pérenne mais nous vous conseillons quand même de prendre vos précautions ! Faites aussi des sauvegardes de vos propres données ! + +
    +

    Cochez la Kaz du libre !
    smile

    + +
    + +
    +
    + + +
    + +
    + + + + + + + + + + + diff --git a/dockers/web/html/offre_familles.html b/dockers/web/html/offre_familles.html new file mode 100644 index 0000000..3a897f4 --- /dev/null +++ b/dockers/web/html/offre_familles.html @@ -0,0 +1,72 @@ + + + + + Cochez la KAZ du libre! + + + + + + + + +
    + + +
    + + + + + +
    +
    +

    Offre pour les Familles

    + + + + +
    + +

    Vous êtes convaincus par l'approche de Kaz, vous allez devenir membre en souscrivant à l'offre familles à partir de 20€ par an !

    + +
    + +KAZ vous propose jusqu'à 5 adresses électroniques en @kaz.bzh ainsi qu'un espace sur notre cloud qui vous donne accès à une boîte à outils de logiciels libres orientés partage et collaboration. Pour 20€/an, vous avez un espace de 10Go, pour 40€/an, vous avez un espace de 20Go, etc... + +
    + +Vos données sont stockées sur des serveurs en Bretagne. +KAZ garantit leur protection. + +
    + +Disponibilité et sauvegardes : Nous faisons tout pour assurer un service de qualité, disponible et pérenne mais nous vous conseillons quand même de prendre vos précautions ! Faites aussi des sauvegardes de vos propres données ! + +
    +

    Cochez la Kaz du libre !
    smile

    + +
    + + +
    +
    + + +
    + +
    + + + + + + + + + + + diff --git a/dockers/web/html/offre_organisations.html b/dockers/web/html/offre_organisations.html new file mode 100644 index 0000000..f8077b4 --- /dev/null +++ b/dockers/web/html/offre_organisations.html @@ -0,0 +1,89 @@ + + + + + Cochez la KAZ du libre! + + + + + + + + +
    + + +
    + + + + + +
    +
    +

    Offre pour les Organisations

    +

    Familles / Assos / Entreprises

    + + + +
    + + Pour tout type d'organisation, qu'elle soit de type familliale, associative ou encore d'entreprise, KAZ propose un package complet d'applications totalement étanches, où vos données ne seront pas connues des autres kaznautes. + + KAZ propose ainsi jusqu'à 5 adresses électroniques en @kaz.bzh ainsi qu'un espace sur votre propre cloud qui vous donne accès à une boîte à outils de logiciels libres orientés partage et collaboration. + Pour 30€/an, vous avez un espace de 10Go, pour 40€/an, vous avez un espace de 20Go, pour 50€/an, vous avez un espace de 30Go, etc... + +
    + + Votre activité (données et logiciels) est totalement cloisonnée au sein de KAZ, grâce à un environnement dédié, indépendant des autres utilisateurs. + +
    + + Vous aurez acces à un espace de discussion pour vos membres actifs, un espace de publication de site Web, une solution de comptabilité en ligne et la possibilité de créer des listes de diffusion pour vos adhérents et vos sympathisants. + Vous manquez d'espace ? vous pouvez demander des compléments de 10Go + +
    + Voici les principaux outils que nous utilisons: +
      +
    • Messagerie: Postifx / Dovecot / Roundcube
    • +
    • Partage de fichiers : Nextcloud
    • +
    • Messagerie instantanée : Mattermost
    • +
    • Site Web : Wordpress
    • +
    • Liste de diffusion : Sympa
    • +
    + +
    + + Vos données sont stockées sur des serveurs en Bretagne. + KAZ garantit leur protection. + +
    + + Disponibilité et sauvegardes : Nous faisons tout pour assurer un service de qualité, disponible et pérenne mais nous vous conseillons quand même de prendre vos précautions ! Faites aussi des sauvegardes de vos propres données ! + +
    +

    Cochez la Kaz du libre !
    smile

    + +
    + +
    +
    + + +
    + +
    + + + + + + + + + + + diff --git a/dockers/web/html/offre_particuliers.html b/dockers/web/html/offre_particuliers.html new file mode 100644 index 0000000..ef7fa3b --- /dev/null +++ b/dockers/web/html/offre_particuliers.html @@ -0,0 +1,71 @@ + + + + + Cochez la KAZ du libre! + + + + + + + + +
    + + +
    + + + + + +
    +
    +

    Offre pour les Particuliers

    + + + +
    + +

    Vous êtes convaincus par l'approche de Kaz, vous allez devenir membre en souscrivant à l'offre particuliers à partir de 10 € par an !

    + +
    + +KAZ vous propose une adresse électronique en @kaz.bzh ainsi qu'un espace sur notre cloud qui vous donne accès à une boîte à outils de logiciels libres orientés partage et collaboration. Pour 10€/an, vous avez un espace de 10Go, pour 20€/an, vous avez un espace de 20Go, pour 30€/an, vous avez un espace de 30Go, etc... + +
    + +Vos données sont stockées sur des serveurs en Bretagne. +KAZ garantit leur protection. + +
    + +Disponibilité et sauvegardes : Nous faisons tout pour assurer un service de qualité, disponible et pérenne mais nous vous conseillons quand même de prendre vos précautions ! Faites aussi des sauvegardes de vos propres données ! + +
    +

    Cochez la Kaz du libre !
    smile

    + +
    + + +
    +
    + + +
    + +
    + + + + + + + + + + + diff --git a/dockers/web/html/pourquoi_choisir_kaz.html b/dockers/web/html/pourquoi_choisir_kaz.html new file mode 100644 index 0000000..8a5d39d --- /dev/null +++ b/dockers/web/html/pourquoi_choisir_kaz.html @@ -0,0 +1,101 @@ + + + + + Cochez la KAZ du libre! + + + + + + + + +
    + + +
    + + + + + +
    +
    +

    Pourquoi choisir KAZ

    + +

    +Vous êtes un particulier, une association, un syndicat, une petite entreprise du Morbihan.
    +Vous voulez contrôler votre budget informatique.
    +Vous voulez maîtriser vos informations et vos données.
    +Vous avez le désir de développer une gouvernance collaborative.
    +Vous souhaitez contribuer à la réduction de la pression du numérique sur le dérèglement climatique.
    +Vous voulez participer à la création de richesse locale.
    + +

    +
    + +
    +
    +
    + +
    +
    + +
    +
    + +

    + +
    +

    Pourquoi Kaz a t-il choisi la solution Nextcloud ?

    + + +Nextcloud est un logiciel libre de site d'hébergement de fichiers et une plateforme de collaboration. accessible par n'importe quel navigateur web ou des clients spécialisés, son architecture ouverte a permis de voir ses fonctionnalités s'étendre depuis ses origines. En 2020, il propose de nombreux services : + +
      +
    • Synchronisation de fichiers entre différents ordinateurs, tablettes et smartphones.
    • +
    • Stockage sécurisé (chiffrement des fichiers sur le serveur, chiffrement de la connexion de point à point).
    • +
    • Partage de fichiers entre utilisateurs nextcloud ou utilisateurs externes.
    • +
    • Serveur de fichiers WebDAV.
    • +
    • Calendrier (permettant la synchronisation CalDAV).
    • +
    • Gestion des tâches (synchronisé avec CalDAV).
    • +
    • Gestionnaire de contacts (CardDAV).
    • +
    • Conférences privées audio/vidéo avec partage d'écran.
    • +
    • Éditeur de texte en ligne (proposant la coloration syntaxique).
    • +
    • Visionneuse de documents en ligne (PDF, OpenDocument).
    • +
    • Édition en ligne de documents avec intégration de OnlyOffice et/ou LibreOffice (en option)
    • +
    • Galerie d'images multiformats (jpg, cr2, img…).
    • +
    • Gestionnaire de marque-pages.
    • +
    • Prise de notes prenant en charge le Markdown.
    • +
    • Messagerie web.
    • +
    • Antivirus ClamAV (en option).
    • +
    • Et de nombreuses applications.
    • +
    • Support d'ActivityPub.
    • +
    + +Mes premiers pas sur le cloud de KAZ... + +
    + + +
    +
    + + +
    + +
    + + + + + + + + + + + diff --git a/dockers/web/html/qui_sommes_nous.html b/dockers/web/html/qui_sommes_nous.html new file mode 100644 index 0000000..c57bc2d --- /dev/null +++ b/dockers/web/html/qui_sommes_nous.html @@ -0,0 +1,74 @@ + + + + + Cochez la KAZ du libre! + + + + + + + + +
    + + +
    + + + + + +
    +
    +

    Qui sommes nous

    + +

    Nous sommes un collectif de citoyennes et citoyens provenant d’horizons divers, dotés de compétences diverses, qui partageons une vision commune des enjeux humains, écologiques, techniques, économiques, pédagogiques, artistiques, juridiques et politiques pour l'avenir de l'humanité et de notre planète Terre.

    +

    Le Numérique a pris une place considérable dans le fonctionnement de nos sociétés, que ce soit au niveau des individus, des organisations et des institutions.

    +

    La crise sanitaire a été un accélérateurextraordinaire des transformations que le Numérique induit dans toutes les relations sociales, familiales, amicales, professionnelles, citoyennes,...

    +

    Le numérique est-il un progrès dans l'évolution de la société ? Les critères "Quantité de données échangées" et "Rapidité de la circulation de ces données" sont-ils pertinents dans l'évaluation de la contribution du Numérique au seul progrès qui vaille, le progrès humain ?

    +

    La privatisation et la concentration des services du web par quelques acteurs mondialisés, les GAFAM (Google, Amazon, Facebook, Apple et Microsoft), ont modifié profondément la promesse initiale de l'Internet, la liberté, la connaissance, la libération du travail contraint et pénible.

    +

    L'internet n'est-il pas devenu aujourd'hui cet espace de soumission volontaire où nous, consommateurs, acceptons d'abandonner, contre des services apparemment gratuits, notre liberté de citoyens au risque de nous faire imposer des comportements ayant pour objectif in fine de concentrer les richesses et le pouvoir entre les mains de quelques-uns. Ce que d'aucuns appellent désormais "Capitalisme de surveillance".

    +

    Soumission volontaire, capitalisme de surveillance, croissance des inégalités mais aussi important impact sur le réchauffement climatique. Le Numérique, tout au long de son cycle de vie, contribue pour 4% à la production de gaz à effet de serre avec une croissance annuelle de 8%. Il est aussi un important consommateur de ressources rares dans la production matérielle de ses infrastructures et de ses objets.

    +

    "Résister et proposer".
    + Il ne suffit plus de critiquer et de résister. Le rouleau compresseur est trop puissant.
    + Les États, à l’échelle mondiale, doivent bien sûr intervenir pour réguler le Numérique, préserer la liberté des citoyens et lutter contre les inégalités.
    + Pour autant, sans attendre, des alternatives aux GAFAM sont possibles. Des collectifs de militants proposent localement des solutions et des services libres, éthiques, solidaires, décentralisés et sobres. Ce sont les CHATONS (Collectif d'Hébergeurs Alternatifs,Transparents,Ouverts, Neutres et Solidaires).

    +

    L’association Kaz est membre des CHATONS et à ce titre, propose de nombreux logiciels libres et gratuits.

    +

    La sobriété, un engagement concret. Lorsque vous utilisez votre adresse @kaz.bzh pour envoyer un courriel avec une pièce jointe à une ou plusieurs personnes, Kaz filtre la pièce jointe et la dépose sur son serveur. Lorsque votre ou vos destinataires ouvriront leur courriel, Kaz leur proposera de télécharger la pièce jointe. Vu du côté des utilisateurs, aucun changement. Mais le serveur de Kaz ne conserve la pièce attachée que pendant un mois. Cette innovation de Kaz qu'on ne trouve encore nulle part ailleurs, allège considérablement les réseaux et les serveurs encombrés par la multitude de fichiers, parfois très lourds, qui ne sont souvent jamais supprimés.

    +

    Rejoignez-nous à Kaz, une association d'éducation populaire.

    +

    N'hésitez pas à nous solliciter.

    +

    Bien sincèrement.

    + +

    La collégiale des membres fondateurs de Kaz :

    +
      +
    • Pascal BAUDONT, enseignant en économie et gestion
    • +
    • Didier DENOUAL, informaticien , salarié
    • +
    • Fabrice RÉGNIER, sniper du logiciel libre
    • +
    • Gersan MOGUEROU, ingénieur de recherche en informatique
    • +
    • François MERCIOL, citoyen engagé, enseignant-chercheur en informatique
    • +
    • Alain RIVAT, retraité, ancien responsable informatique du Lycée Lesage, Vannes
    • +
    • Jacques-Henri VANDAELE, retraité, ancien dirigeant d'entreprise associative agricole
    • +
    + +
    +
    + + +
    + +
    + + + + + + + + + + + diff --git a/dockers/web/html/rgpd.html b/dockers/web/html/rgpd.html new file mode 100644 index 0000000..44c9e2a --- /dev/null +++ b/dockers/web/html/rgpd.html @@ -0,0 +1,101 @@ + + + + + Cochez la KAZ du libre! + + + + + + + + +
    + + +
    + + + + + +
    +
    + +

    Mentions légales et conformité au RGPD

    + + +

    + + +
    + +

    +Les informations recueillies sur ce site ou sur les outils proposés par KAZ sont enregistrées dans un fichier informatisé par la personne morale KAZ afin de fournir les services proposés par KAZ. La base légale du traitement est l'intérêt légitime. +

    + +

    +Les données collectées ne seront communiquées à aucun tiers. +

    + +

    +Les données sont conservées pendant le temps nécessaire à l'exécution du bon fonctionnement des services KAZ. +

    + +

    +Pour toute demande, merci de contacter contact@kaz.bzh +

    + +

    +L'hébergement des données KAZ est effectué chez grifon.fr, situé à Rennes +

    + +

    +Les sauvegardes ont lieu quotidiennement dans un espace sécurisé et permettent d'assurer la continuité de services attendues par les utilisateurs +

    + + +

    +Vous pouvez accéder aux données vous concernant, les rectifier, demander leur effacement ou exercer votre droit à la limitation du traitement de vos données. (en fonction de la base légale du traitement, mentionner également : Vous pouvez retirer à tout moment votre consentement au traitement de vos données ; Vous pouvez également vous opposer au traitement de vos données ; Vous pouvez également exercer votre droit à la portabilité de vos données) +

    + +

    +Consultez le site cnil.fr pour plus d’informations sur vos droits. +

    + +

    +Pour exercer ces droits ou pour toute question sur le traitement de vos données dans ce dispositif, vous pouvez contacter (le cas échéant, notre délégué à la protection des données ou le service chargé de l’exercice de ces droits) : [adresse électronique, postale, coordonnées téléphoniques, etc.] +

    + +

    +Si vous estimez, après nous avoir contactés, que vos droits « Informatique et Libertés » ne sont pas respectés, vous pouvez adresser une réclamation à la CNIL. +

    + + +
    + +

    Cochez la Kaz du libre !
    smile

    + +
    +
    + + +
    + +
    + + + + + + + + + + + diff --git a/dockers/web/html/robot.txt b/dockers/web/html/robot.txt new file mode 100644 index 0000000..f4625e6 --- /dev/null +++ b/dockers/web/html/robot.txt @@ -0,0 +1,2 @@ +User-agent: GPTBot +Disallow: / diff --git a/dockers/web/html/sites_amis.html b/dockers/web/html/sites_amis.html new file mode 100644 index 0000000..9134182 --- /dev/null +++ b/dockers/web/html/sites_amis.html @@ -0,0 +1,79 @@ + + + + + Cochez la KAZ du libre! + + + + + + + + +
    + + +
    + + + + + +
    +
    +

    Sites Amis

    + + +
    + +

    Resoluhttps://soyezresolu.org/
    + Réseaux éthiques et solutions ouvertes pour libérer vos usages. Agir pour l'Économie Sociale et Solidaire demande beaucoup d'organisation, de communication et de collaboration. Promouvoir un modèle de production alternatif fondé sur la solidarité et le partage est un travail collectif qui ne peut se passer d'outils numériques pour être efficace. Bien souvent, ces outils sont choisis par défaut, et ne correspondent ni aux besoins ni aux valeurs des organisations qui les utilisent.

    + +
    + +
    + +

    Resoluhttp://radio-bro-gwened.com/
    + Radio Bro Gwened se définit comme une radio de Pays généraliste. Elle réunit journalistes, animateurs professionnels et animateurs bénévoles toutes générations confondues dans le but de travailler à l’animation d’un outil de communication au plus proche de la réalité locale et n’excluant personne.
    + De fonctionnement associatif, Radio Bro Gwened regroupe des personnes morales et individuelles investies dans le fonctionnement général de la station et dans son animation. Plus d’une centaine de personnes bénévoles participent à la vie de la radio soit en animant une émission ou des des chroniques régulières pour les magazines ou encore en proposant l’organisation d’animations spécifiques. +

    + +
    + +
    + +

    Resoluhttp://www.tycloud.bzh/
    +Entreprise implantée à Vannes, Tycloud est opérateur (agréée ARCEP) & hébergeur de solutions numériques de proximité. Innovation, sécurité des données et relations de confiance sont les moteurs de leurs accompagnements et réalisations. Ils possèdent et exploitent leur propre coffre-fort numérique sur Vannes, un datacentre écoconçu et efficient en matière de consommation d'énergie.
    + +Riche d'expériences variées de plus de 10 ans dans les mondes de l'agroalimentaire, l'industrie, la fonction publique et des services... L’équipe de Tycloud à cœur de comprendre les besoins et enjeux de ses clients afin de mettre son expertise à leur service.
    + +Et on ne vous a pas dit: Une partie de l'infra KAZ est hébergée chez Tycloud à des tarifs très très, mais alors très sympa ;) +

    + +
    + +

    Cochez la Kaz du libre !
    smile

    + +
    +
    + + +
    + +
    + + + + + + + + + + + diff --git a/dockers/web/web-gen.sh b/dockers/web/web-gen.sh new file mode 100755 index 0000000..6ae1214 --- /dev/null +++ b/dockers/web/web-gen.sh @@ -0,0 +1,120 @@ +#!/bin/bash + +######################################## +# setup + +KAZ_ROOT=$(cd "$(dirname $0)/../.."; pwd) +. "${KAZ_ROOT}/bin/.commonFunctions.sh" +setKazVars + +#TMPL=html/index.tmpl.html +#CONF=html/index.html +TEST_TMPL="${DOCK_VOL}/web_html/_data/status/allServices.tmpl.html" +TEST_CONF="${DOCK_VOL}/web_html/_data/status/allServices.html" + +declare -A siteNames=( [prod1]="Rennes" [prod2]="Nantes A" [prod3]="Nantes B" ) +declare -A siteSSH=( [prod1]="" [prod2]="ssh -p 2201 root@prod2.kaz.bzh" [prod3]="ssh -p 2201 root@kazoulet.kaz.bzh" ) +siteKeys=$(echo "${!siteNames[@]}"|tr ' ' '\n'|sort|tr '\n' ' ') + +declare -a availableServices +. "${DOCKERS_ENV}" +# XXX A reprendre de ce qui est attendu par container.sh +availableServices=( + www + ${calcHost} + # ${castopodHost} : need orga + ${cloudHost} + ${dateHost} + ${dokuwikiHost} + ${fileHost} + ${gitHost} + # ${gravHost} + ${imapsyncHost} + # ${ldapHost} + ${ldapUIHost} + ${matterHost} + ${mobilizonHost} + ${padHost} + # paheko : need orga + ${sympaHost} + ${vigiloHost} + ${vaultwardenHost} + ${webmailHost} + # wordpress : need orga +) + +echo -e "create status/allServices.html" + +mkdir -p "$(dirname ${TEST_TMPL})" + +######################################## +# commun + +echo -e "${BLUE}${BOLD} * Base${NC}" +cat > "${TEST_TMPL}" < + + + + KAZ : test des services + + + + + +

    Commun

    +EOF + +for service in ${availableServices[@]} ; do + echo " " >> "${TEST_TMPL}" +done + +for service in prod1-office prod2-office kazoulet-office; do + echo " " >> "${TEST_TMPL}" +done + +echo "
    " >> "${TEST_TMPL}" + +######################################## +# All sites + +for siteKey in ${siteKeys}; do + echo -e "${BLUE}${BOLD} * Orgas ${siteNames[$siteKey]}${NC}" + declare -a availableOrga + availableOrga=( $(${siteSSH[$siteKey]} cat "${KAZ_CONF_DIR}/container-orga.list" | sed -e "s/\(.*\)[ \t]*#.*$/\1/" -e "s/^[ \t]*\(.*\)$/\1/" -e "/^$/d") ) + + echo "

    Le(s) ${#availableOrga[@]} orga(s) de ${siteNames[$siteKey]} :

    " >> "${TEST_TMPL}" + for orga in ${availableOrga[@]} ; do + orgaName=${orga%-orga} + + echo " ${orgaName}" + services=$(${siteSSH[$siteKey]} "${KAZ_COMP_DIR}/${orga}/orga-gen.sh" -l | sed -e "s/collabora/office/") + for service in ${services}; do + echo " " + done + echo "
    " + done >> "${TEST_TMPL}" +done + +######################################## +# foreign domains + +echo -e "${BLUE}${BOLD} * Extern${NC}" +echo "

    Les redirections

    " >> "${TEST_TMPL}" +for siteKey in ${siteKeys}; do + echo -e "${BLUE}${BOLD} * Orgas ${siteNames[$siteKey]}${NC}" + + echo "

    ${siteNames[$siteKey]}

    " >> "${TEST_TMPL}" + for extrenal in $( ${siteSSH[$siteKey]} "${KAZ_BIN_DIR}/foreign-domain.sh" -l | sort -u) ; do + echo " " + done >> "${TEST_TMPL}" +done +echo "


    " >> "${TEST_TMPL}" + +######################################## +#"${APPLY_TMPL}" "${TMPL}" "${CONF}" +"${APPLY_TMPL}" "${TEST_TMPL}" "${TEST_CONF}" diff --git a/secret.tmpl/Readme.txt b/secret.tmpl/Readme.txt new file mode 100644 index 0000000..4ab7f8c --- /dev/null +++ b/secret.tmpl/Readme.txt @@ -0,0 +1,11 @@ +Mise à jour des mots de passe + +L'idée c'est d'extraire la gestion des mots de passe de l'installation. + +Tous les mots de passe sont dans un fichier "SetAllPass.sh" que des scripts vont chercher. + +updateDockerPassword.sh met à jours les fichiers d'environnement de mots de passe utilisé par docker-compose. + +(Il y a un problème pour mettre à jour le mot de passe d'une BD si son conteneur n'est pas en route) + +Les modifications sont prises en compte que lors de la création de nouveaux conteneurs (les données permanentes (mot de passe) dans les volumes ne sont pas changées) diff --git a/secret.tmpl/SetAllPass.sh b/secret.tmpl/SetAllPass.sh new file mode 100755 index 0000000..909eb35 --- /dev/null +++ b/secret.tmpl/SetAllPass.sh @@ -0,0 +1,305 @@ +#!/bin/bash + +# Attention à cause des scripts pas de ["'/] dans les mot de passe + +#################### +# ethercalc +ethercalc_REDIS_PORT_6379_TCP_ADDR="redis" +ethercalc_REDIS_PORT_6379_TCP_PORT="6379" + +#################### +# etherpad +etherpad_MYSQL_ROOT_PASSWORD="--clean_val--" +etherpad_MYSQL_DATABASE="--clean_val--" +etherpad_MYSQL_USER="--clean_val--" +etherpad_MYSQL_PASSWORD="--clean_val--" + +# Share with etherpadDB +etherpad_DB_NAME="${etherpad_MYSQL_DATABASE}" +etherpad_DB_USER="${etherpad_MYSQL_USER}" +etherpad_DB_PASS="${etherpad_MYSQL_PASSWORD}" + +etherpad_DB_TYPE="mysql" +etherpad_DB_HOST="padDB" +etherpad_DB_PORT="3306" +#etherpad_DB_CHARSET="utf8" +#user: admin +etherpad_ADMIN_PASSWORD="--clean_val--" +etherpad_PAD_OPTIONS_LANG="fr" +etherpad_TITLE="KazPad" +etherpad_TRUST_PROXY="true" + +#################### +# framadate +framadate_MYSQL_ROOT_PASSWORD="--clean_val--" +framadate_MYSQL_DATABASE="--clean_val--" +framadate_MYSQL_USER="--clean_val--" +framadate_MYSQL_PASSWORD="--clean_val--" + +framadate_HTTPD_USER="--clean_val--" +framadate_HTTPD_PASSWORD="--clean_val--" + +################## +# Gandi +# à supprimer et à replacer par dns_gandi_api_key +gandi_GANDI_KEY="xxx" +gandi_GANDI_API="https://api.gandi.net/v5/livedns/domains/${domain}" +gandi_dns_gandi_api_key="${gandi_GANDI_KEY}" + +#################### +# mattermost +mattermost_MYSQL_ROOT_PASSWORD="--clean_val--" +mattermost_MYSQL_DATABASE="--clean_val--" +mattermost_MYSQL_USER="--clean_val--" +mattermost_MYSQL_PASSWORD="--clean_val--" + +# Share with mattermostDB +mattermost_MM_DBNAME="${mattermost_MYSQL_DATABASE}" +mattermost_MM_USERNAME="${mattermost_MYSQL_USER}" +mattermost_MM_PASSWORD="${mattermost_MYSQL_PASSWORD}" + +mattermost_DB_PORT_NUMBER="3306" +mattermost_DB_HOST="db" +mattermost_MM_SQLSETTINGS_DRIVERNAME="mysql" +mattermost_MM_ADMIN_EMAIL="admin@kaz.bzh" + +# mattermost_MM_SQLSETTINGS_DATASOURCE = "MM_USERNAME:MM_PASSWORD@tcp(DB_HOST:DB_PORT_NUMBER)/MM_DBNAME?charset=utf8mb4,utf8&readTimeout=30s&writeTimeout=30s" +# Don't forget to replace all entries (beginning by MM_ and DB_) in MM_SQLSETTINGS_DATASOURCE with the real variables values. +mattermost_MM_SQLSETTINGS_DATASOURCE="${mattermost_MYSQL_USER}:${mattermost_MYSQL_PASSWORD}@tcp(${mattermost_DB_HOST}:${mattermost_DB_PORT_NUMBER})/${mattermost_MM_DBNAME}?charset=utf8mb4,utf8&readTimeout=30s&writeTimeout=30s" +# sinon avec postgres +# mattermost_MM_SQLSETTINGS_DATASOURCE = "postgres://${MM_USERNAME}:${MM_PASSWORD}@db:5432/${MM_DBNAME}?sslmode=disable&connect_timeout=10" + +# pour envoyer des messages sur l'agora avec mmctl +mattermost_user="admin-mattermost" +mattermost_pass="--clean_val--" +mattermost_token="xxx-private" + +################## +# Openldap +ldap_LDAP_ADMIN_USERNAME="--clean_val--" +ldap_LDAP_ADMIN_PASSWORD="--clean_val--" +ldap_LDAP_CONFIG_ADMIN_USERNAME="--clean_val--" +ldap_LDAP_CONFIG_ADMIN_PASSWORD="--clean_val--" +ldap_LDAP_POSTFIX_PASSWORD="--clean_val--" +ldap_LDAP_LDAPUI_PASSWORD="--clean_val--" +ldap_LDAP_MATTERMOST_PASSWORD="--clean_val--" +ldap_LDAP_CLOUD_PASSWORD="--clean_val--" +ldap_LDAP_MOBILIZON_PASSWORD="--clean_val--" + +ldap_LDAPUI_URI=ldap://ldap +ldap_LDAPUI_BASE_DN=${ldap_root} +ldap_LDAPUI_REQUIRE_STARTTLS=FALSE +ldap_LDAPUI_ADMINS_GROUP=admins +ldap_LDAPUI_ADMIN_BIND_DN=cn=ldapui,ou=applications,${ldap_root} +ldap_LDAPUI_ADMIN_BIND_PWD=${ldap_LDAP_LDAPUI_PASSWORD} +ldap_LDAPUI_IGNORE_CERT_ERRORS=TRUE +ldap_LDAPUI_PASSWORD="--clean_val--" +ldap_LDAPUI_MM_ADMIN_TOKEN=${mattermost_token} + +################### +# gitea +gitea_MYSQL_ROOT_PASSWORD="--clean_val--" +gitea_MYSQL_DATABASE="--clean_val--" +gitea_MYSQL_USER="--clean_val--" +gitea_MYSQL_PASSWORD="--clean_val--" + +# on ne peut pas utiliser le login "admin" +gitea_user_admin="admin_gitea" +gitea_pass_admin="--clean_val--" +gitea_admin_email="admin@kaz.bzh" + +#################### +# jirafeau +jirafeau_HTTPD_PASSWORD="--clean_val--" +jirafeau_DATA_DIR="--clean_val--" + + +#################### +# nexcloud +nextcloud_MYSQL_ROOT_PASSWORD="${mattermost_MYSQL_ROOT_PASSWORD}" +nextcloud_MYSQL_DATABASE="--clean_val--" +nextcloud_MYSQL_USER="--clean_val--" +nextcloud_MYSQL_PASSWORD="--clean_val--" + +nextcloud_NEXTCLOUD_ADMIN_USER="admin" +nextcloud_NEXTCLOUD_ADMIN_PASSWORD="--clean_val--" +nextcloud_MYSQL_HOST="db" + +#user: admin +nextcloud_RAIN_LOOP="--clean_val--" + +#################### +# collabora +office_username="admin" +office_password="--clean_val--" + +#################### +# roundcube +roundcube_MYSQL_ROOT_PASSWORD="--clean_val--" +roundcube_MYSQL_DATABASE="--clean_val--" +roundcube_MYSQL_USER="--clean_val--" +roundcube_MYSQL_PASSWORD="--clean_val--" + +# Share with roundcubeDB +roundcube_ROUNDCUBEMAIL_DB_TYPE="mysql" +roundcube_ROUNDCUBEMAIL_DB_NAME="${roundcube_MYSQL_DATABASE}" +roundcube_ROUNDCUBEMAIL_DB_USER="${roundcube_MYSQL_USER}" +roundcube_ROUNDCUBEMAIL_DB_PASSWORD="${roundcube_MYSQL_PASSWORD}" +roundcube_ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE="1G" + +#################### +# postfix LDAP +mail_LDAP_BIND_DN=cn=postfix,ou=applications,${ldap_root} +mail_LDAP_BIND_PW=${ldap_LDAP_POSTFIX_PASSWORD} + +#################### +# sympa +sympa_MYSQL_ROOT_PASSWORD="--clean_val--" +sympa_MYSQL_DATABASE="sympa" +sympa_MYSQL_USER="sympa" +sympa_MYSQL_PASSWORD="--clean_val--" + +sympa_KEY="/etc/letsencrypt/live/${domain}/privkey.pem" +sympa_CERT="/etc/letsencrypt/live/${domain}/fullchain.pem" +sympa_LISTMASTERS="listmaster@${domain_sympa}" +sympa_ADMINEMAIL="listmaster@${domain_sympa}" +sympa_SOAP_USER="sympa" +sympa_SOAP_PASSWORD="--clean_val--" + +# pour inscrire des users sur des listes sympa avec soap +#il faut que le user soit admin de sympa +sympa_user="a@${domain}" +sympa_pass="--clean_val--" + +################## +# vigilo +vigilo_MYSQL_ROOT_PASSWORD="--clean_val--" +vigilo_MYSQL_USER="--clean_val--" +vigilo_MYSQL_PASSWORD="--clean_val--" +vigilo_MYSQL_DATABASE="--clean_val--" +vigilo_MYSQL_HOST="db" +#vigilo_BIND= + +#################### +# wordpress +wp_MYSQL_ROOT_PASSWORD="${mattermost_MYSQL_ROOT_PASSWORD}" +wp_MYSQL_DATABASE="--clean_val--" +wp_MYSQL_USER="--clean_val--" +wp_MYSQL_PASSWORD="--clean_val--" + +# Share with wpDB +wp_WORDPRESS_DB_HOST="db:3306" +wp_WORDPRESS_DB_NAME="${wp_MYSQL_DATABASE}" +wp_WORDPRESS_DB_USER="${wp_MYSQL_USER}" +wp_WORDPRESS_DB_PASSWORD="${wp_MYSQL_PASSWORD}" + +wp_WORDPRESS_ADMIN_USER="admin" +wp_WORDPRESS_ADMIN_PASSWORD="--clean_val--" + +################## +#qui envoi le mail d'inscription ? +EMAIL_CONTACT="toto@kaz.bzh" + + +################## +# Paheko +paheko_API_USER="admin-api" +paheko_API_PASSWORD="--clean_val--" + +################## +# La nas de Kaz chez Grifon +nas_admin1="admin" +nas_password1="--clean_val--" +nas_admin2="kaz" +nas_password1="--clean_val--" +# compte mail pour les notifications du nas +nas_email_account="admin-nas@${domain}" +nas_email_password="--clean_val--" + +################## +#Compte sur outlook.com +outlook_user="kaz-user@outlook.fr" +outlook_pass="--clean_val--" + +################## +#Borg +BORG_REPO="/mnt/backup-nas1/BorgRepo" +BORG_PASSPHRASE="--clean_val--" +VOLUME_SAUVEGARDES="/mnt/backup-nas1" +MAIL_RAPPORT="a@${domain};b@${domain};c@${domain}" +BORGMOUNT="/mnt/disk-nas1/tmp/repo_mount" + + +################### +# mobilizon +mobilizon_POSTGRES_USER="--clean_val--" +mobilizon_POSTGRES_PASSWORD="--clean_val--" +mobilizon_POSTGRES_DB=mobilizon +mobilizon_MOBILIZON_DATABASE_USERNAME="${mobilizon_POSTGRES_USER}" +mobilizon_MOBILIZON_DATABASE_PASSWORD="${mobilizon_POSTGRES_PASSWORD}" +mobilizon_MOBILIZON_DATABASE_DBNAME=mobilizon + +mobilizon_MOBILIZON_INSTANCE_REGISTRATIONS_OPEN=false +mobilizon_MOBILIZON_INSTANCE_NAME="Mobilizon" +mobilizon_MOBILIZON_INSTANCE_HOST="${mobilizonHost}.${domain}" + +mobilizon_MOBILIZON_INSTANCE_SECRET_KEY_BASE=changeme +mobilizon_MOBILIZON_INSTANCE_SECRET_KEY=changeme + +mobilizon_MOBILIZON_INSTANCE_EMAIL=noreply@${domain} +mobilizon_MOBILIZON_REPLY_EMAIL=contact@${domain_sympa} +mobilizon_MOBILIZON_ADMIN_EMAIL=admin@${domain_sympa} + +mobilizon_MOBILIZON_SMTP_SERVER="${smtpHost}.${domain}" +mobilizon_MOBILIZON_SMTP_PORT=25 +mobilizon_MOBILIZON_SMTP_HOSTNAME="${smtpHost}.${domain}" +mobilizon_MOBILIZON_SMTP_USERNAME=noreply@${domain} +mobilizon_MOBILIZON_SMTP_PASSWORD= +mobilizon_MOBILIZON_SMTP_SSL=false + +mobilizon_MOBILIZON_LDAP_BINDUID=cn=mobilizon,ou=applications,${ldap_root} +mobilizon_MOBILIZON_LDAP_BINDPASSWORD=${ldap_LDAP_MOBILIZON_PASSWORD} + + +##################### +# Vaultwarden + +vaultwarden_MYSQL_ROOT_PASSWORD="--clean_val--" +vaultwarden_MYSQL_DATABASE="vaultwarden" +vaultwarden_MYSQL_USER="vaultwarden" +vaultwarden_MYSQL_PASSWORD="--clean_val--" + +vaultwarden_DATABASE_URL="mysql://${vaultwarden_MYSQL_USER}:${vaultwarden_MYSQL_PASSWORD}@db/${vaultwarden_MYSQL_DATABASE}" +vaultwarden_ADMIN_TOKEN="--clean_val--" + +##################### +#Traefik + +traefik_DASHBOARD_USER="admin" +traefik_DASHBOARD_PASSWORD="--clean_val--" + + +##################### +# dokuwiki + +dokuwiki_WIKI_ROOT=Kaz +dokuwiki_WIKI_EMAIL=wiki@kaz.local +dokuwiki_WIKI_PASSWORD="--clean_val--" + +##################### +# Castopod +castopod_MYSQL_ROOT_PASSWORD="--clean_val--" +castopod_MYSQL_DATABASE="--clean_val--" +castopod_MYSQL_USER="--clean_val--" +castopod_MYSQL_PASSWORD="--clean_val--" +castopod_CP_REDIS_PASSWORD="${castopodRedisPassword}" +castopod_ADMIN_USER=adminKaz +castopod_ADMIN_MAIL=admin@${domain} +castopod_ADMIN_PASSWORD="--clean_val--" +castopod_CP_EMAIL_SMTP_HOST="${smtpHost}.${domain}" +castopod_CP_EMAIL_SMTP_PORT=25 +castopod_CP_EMAIL_SMTP_USERNAME=noreply@${domain} +castopod_CP_EMAIL_SMTP_PASSWORD= +castopod_CP_EMAIL_FROM=noreply@${domain} +castopod_CP_EMAIL_SMTP_CRYPTO=tls + diff --git a/secret.tmpl/allow_admin_ip b/secret.tmpl/allow_admin_ip new file mode 100644 index 0000000..bf1168a --- /dev/null +++ b/secret.tmpl/allow_admin_ip @@ -0,0 +1,10 @@ +# ip for admin access only + +# local test (ne doivent pas apparaître dans /etc/hosts.allow) +allow 10.0.0.0/8; +allow 127.0.0.0/8; +allow 172.16.0.0/11; +allow 192.168.0.0/16; +allow 100.64.0.0/10; + +deny all; diff --git a/secret.tmpl/env-apikazServ b/secret.tmpl/env-apikazServ new file mode 100644 index 0000000..efeddb0 --- /dev/null +++ b/secret.tmpl/env-apikazServ @@ -0,0 +1,32 @@ +paheko_API_USER= +paheko_API_PASSWORD= +paheko_url= + +mattermost_user= +mattermost_pass= +mattermost_url= + +ldap_LDAP_ADMIN_USERNAME= +ldap_LDAP_ADMIN_PASSWORD= +ldap_root= + +nextcloud_NEXTCLOUD_ADMIN_USER= +nextcloud_NEXTCLOUD_ADMIN_PASSWORD= +cloud_url= + +sympa_SOAP_USER= +sympa_SOAP_PASSWORD= +sympa_url= + +gandi_GANDI_KEY= +gandi_GANDI_API= + +# Api KAZ +apikaz_MAIL_SERVER= +apikaz_MAIL_USERNAME= +apikaz_MAIL_PASSWORD= + +webmail_url= +mdp_url= +site_url= +nc_url= diff --git a/secret.tmpl/env-castopodDB b/secret.tmpl/env-castopodDB new file mode 100644 index 0000000..6e2de9c --- /dev/null +++ b/secret.tmpl/env-castopodDB @@ -0,0 +1,4 @@ +MYSQL_ROOT_PASSWORD= +MYSQL_DATABASE= +MYSQL_USER= +MYSQL_PASSWORD= diff --git a/secret.tmpl/env-castopodServ b/secret.tmpl/env-castopodServ new file mode 100644 index 0000000..52aafc4 --- /dev/null +++ b/secret.tmpl/env-castopodServ @@ -0,0 +1,7 @@ +CP_EMAIL_SMTP_HOST= +CP_EMAIL_FROM= +CP_EMAIL_SMTP_USERNAME= +CP_EMAIL_SMTP_PASSWORD= +CP_EMAIL_SMTP_PORT= +CP_EMAIL_SMTP_CRYPTO= +CP_REDIS_PASSWORD= diff --git a/secret.tmpl/env-dokuwikiServ b/secret.tmpl/env-dokuwikiServ new file mode 100644 index 0000000..35e8c91 --- /dev/null +++ b/secret.tmpl/env-dokuwikiServ @@ -0,0 +1,4 @@ + +WIKI_ROOT= +WIKI_EMAIL= +WIKI_PASSWORD= \ No newline at end of file diff --git a/secret.tmpl/env-ethercalcServ b/secret.tmpl/env-ethercalcServ new file mode 100644 index 0000000..1999977 --- /dev/null +++ b/secret.tmpl/env-ethercalcServ @@ -0,0 +1,3 @@ + +REDIS_PORT_6379_TCP_ADDR=redis +REDIS_PORT_6379_TCP_PORT=6379 diff --git a/secret.tmpl/env-etherpadDB b/secret.tmpl/env-etherpadDB new file mode 100644 index 0000000..60f2779 --- /dev/null +++ b/secret.tmpl/env-etherpadDB @@ -0,0 +1,5 @@ + +MYSQL_ROOT_PASSWORD= +MYSQL_DATABASE= +MYSQL_USER= +MYSQL_PASSWORD= diff --git a/secret.tmpl/env-etherpadServ b/secret.tmpl/env-etherpadServ new file mode 100644 index 0000000..e56486b --- /dev/null +++ b/secret.tmpl/env-etherpadServ @@ -0,0 +1,16 @@ + +# share with padDB +DB_NAME= +DB_USER= +DB_PASS= + +DB_TYPE= +DB_HOST= +DB_PORT= +#DB_CHARSET= +ADMIN_PASSWORD= + +TITLE= +PAD_OPTIONS_LANG= +TRUST_PROXY= +#DEFAULT_PAD_TEXT="––––– Ce texte est à effacer (après lecture si c’est votre première visite) ou à conserver en bas de votre pad –––––\n\nBienvenue sur notre PAD !\n\n➡ Comment commencer ?\n• Renseignez votre nom ou pseudo, en cliquant sur l’icône « utilisateur » en haut à droite.\n• Choisissez votre couleur d'écriture au même endroit.\n• Lancez-vous : écrivez sur votre pad !\n• Les contributions de chacun se synchronisent « en temps réel » sous leur propre couleur.\n\n➡ Comment partager / collaborer ?\n• Sélectionnez et copiez l'URL (l'adresse web dans la grande barre en haut à gauche du navigateur)\n• Partagez-là à vos collaborateurs et collaboratrices (email, messagerie, etc.)\n• Attention : toute personne ayant cette adresse d'accès peut modifier le pad à sa convenance.\n• Utilisez l'onglet chat (en bas à droite) pour séparer les discussions du texte sur lequel vous travaillez.\n\n➡ Comment sauvegarder ?\n• Il n'y a rien à faire : le texte est automatiquement sauvegardé, à chaque caractère tapé.\n• Marquez une version (un état du pad) en cliquant sur l’icône « étoile ».\n• Retrouvez toute l'évolution du pad et vos versions marquées d'une étoile dans l’historique (icône « horloge »).\n• Importez et exportez votre texte avec l'icône « double flèche » (formats HTML, texte brut, PDF, ODF…) ou avec un copier/coller.\n\nImportant ! N’oubliez pas de conserver quelque part l’adresse web (URL) de votre pad.\n\nBon travail collaboratif :)\n\n––––– Ce texte est à effacer (après lecture si c’est votre première visite) –––––\n\n**ATTENTION**\nCETTE INSTANCE PROPOSE DES PADS À EFFACEMENT AUTOMATIQUE !\n\nVOS PADS SERONT AUTOMATIQUEMENT SUPPRIMÉS AU BOUT DE 62 JOURS (2 MOIS) SANS ÉDITION !\n\nSi le contenu de votre pad bimestriel a été effacé, c'est qu'il n'avait pas été modifié depuis plus de 62 jours consécutifs.\n" diff --git a/secret.tmpl/env-framadateDB b/secret.tmpl/env-framadateDB new file mode 100644 index 0000000..60f2779 --- /dev/null +++ b/secret.tmpl/env-framadateDB @@ -0,0 +1,5 @@ + +MYSQL_ROOT_PASSWORD= +MYSQL_DATABASE= +MYSQL_USER= +MYSQL_PASSWORD= diff --git a/secret.tmpl/env-framadateServ b/secret.tmpl/env-framadateServ new file mode 100644 index 0000000..ba956eb --- /dev/null +++ b/secret.tmpl/env-framadateServ @@ -0,0 +1,3 @@ + +HTTPD_USER= +HTTPD_PASSWORD= diff --git a/secret.tmpl/env-gandi b/secret.tmpl/env-gandi new file mode 100644 index 0000000..571ad1a --- /dev/null +++ b/secret.tmpl/env-gandi @@ -0,0 +1,3 @@ + +GANDI_KEY= +GANDI_API= diff --git a/secret.tmpl/env-gitDB b/secret.tmpl/env-gitDB new file mode 100644 index 0000000..60f2779 --- /dev/null +++ b/secret.tmpl/env-gitDB @@ -0,0 +1,5 @@ + +MYSQL_ROOT_PASSWORD= +MYSQL_DATABASE= +MYSQL_USER= +MYSQL_PASSWORD= diff --git a/secret.tmpl/env-gitServ b/secret.tmpl/env-gitServ new file mode 100644 index 0000000..90c5c1d --- /dev/null +++ b/secret.tmpl/env-gitServ @@ -0,0 +1,3 @@ +user_admin= +pass_admin= +admin_email= \ No newline at end of file diff --git a/secret.tmpl/env-jirafeauServ b/secret.tmpl/env-jirafeauServ new file mode 100644 index 0000000..27ee028 --- /dev/null +++ b/secret.tmpl/env-jirafeauServ @@ -0,0 +1,2 @@ + +HTTPD_PASSWORD= diff --git a/secret.tmpl/env-ldapServ b/secret.tmpl/env-ldapServ new file mode 100644 index 0000000..e3a50f4 --- /dev/null +++ b/secret.tmpl/env-ldapServ @@ -0,0 +1,9 @@ +LDAP_ADMIN_USERNAME= +LDAP_ADMIN_PASSWORD= +LDAP_CONFIG_ADMIN_USERNAME= +LDAP_CONFIG_ADMIN_PASSWORD= +LDAP_POSTFIX_PASSWORD= +LDAP_LDAPUI_PASSWORD= +LDAP_MATTERMOST_PASSWORD= +LDAP_CLOUD_PASSWORD= +LDAP_MOBILIZON_PASSWORD= diff --git a/secret.tmpl/env-ldapUI b/secret.tmpl/env-ldapUI new file mode 100644 index 0000000..af9c9e0 --- /dev/null +++ b/secret.tmpl/env-ldapUI @@ -0,0 +1,9 @@ +LDAPUI_URI= +LDAPUI_BASE_DN= +LDAPUI_REQUIRE_STARTTLS= +LDAPUI_ADMINS_GROUP= +LDAPUI_ADMIN_BIND_DN= +LDAPUI_ADMIN_BIND_PWD= +LDAPUI_IGNORE_CERT_ERRORS= +LDAPUI_PASSWORD= +LDAPUI_MM_ADMIN_TOKEN= diff --git a/secret.tmpl/env-mailServ b/secret.tmpl/env-mailServ new file mode 100644 index 0000000..255bb9a --- /dev/null +++ b/secret.tmpl/env-mailServ @@ -0,0 +1,3 @@ +LDAP_BIND_DN= +LDAP_BIND_PW= +POSTMASTER_ADDRESS= diff --git a/secret.tmpl/env-mattermostDB b/secret.tmpl/env-mattermostDB new file mode 100644 index 0000000..80c9b00 --- /dev/null +++ b/secret.tmpl/env-mattermostDB @@ -0,0 +1,8 @@ + +MYSQL_ROOT_PASSWORD= +MYSQL_DATABASE= +MYSQL_USER= +MYSQL_PASSWORD= + +MM_MYSQL_USER= +MM_MYSQL_PASSWORD= diff --git a/secret.tmpl/env-mattermostServ b/secret.tmpl/env-mattermostServ new file mode 100644 index 0000000..773986a --- /dev/null +++ b/secret.tmpl/env-mattermostServ @@ -0,0 +1,15 @@ + +# share with matterDB +MM_DBNAME= +MM_USERNAME= +MM_PASSWORD= + +MM_ADMIN_EMAIL= +MM_ADMIN_USER= +MM_ADMIN_PASSWORD= + +DB_HOST= +DB_PORT_NUMBER= +MM_SQLSETTINGS_DRIVERNAME= +MM_SQLSETTINGS_DATASOURCE= + diff --git a/secret.tmpl/env-mobilizonDB b/secret.tmpl/env-mobilizonDB new file mode 100644 index 0000000..59259d0 --- /dev/null +++ b/secret.tmpl/env-mobilizonDB @@ -0,0 +1,4 @@ +# Database settings +POSTGRES_USER= +POSTGRES_PASSWORD= +POSTGRES_DB= diff --git a/secret.tmpl/env-mobilizonServ b/secret.tmpl/env-mobilizonServ new file mode 100644 index 0000000..040e994 --- /dev/null +++ b/secret.tmpl/env-mobilizonServ @@ -0,0 +1,27 @@ +# Instance configuration +MOBILIZON_INSTANCE_REGISTRATIONS_OPEN= +MOBILIZON_INSTANCE_NAME= +MOBILIZON_INSTANCE_HOST= + +MOBILIZON_INSTANCE_SECRET_KEY_BASE= +MOBILIZON_INSTANCE_SECRET_KEY= + +MOBILIZON_INSTANCE_EMAIL= +MOBILIZON_REPLY_EMAIL= +MOBILIZON_ADMIN_EMAIL= + +# Email settings +MOBILIZON_SMTP_SERVER= +MOBILIZON_SMTP_PORT= +MOBILIZON_SMTP_HOSTNAME= +MOBILIZON_SMTP_USERNAME= +MOBILIZON_SMTP_PASSWORD= +MOBILIZON_SMTP_SSL= + +MOBILIZON_DATABASE_USERNAME= +MOBILIZON_DATABASE_PASSWORD= +MOBILIZON_DATABASE_DBNAME= + +# LDAP +MOBILIZON_LDAP_BINDUID= +MOBILIZON_LDAP_BINDPASSWORD= diff --git a/secret.tmpl/env-nextcloudDB b/secret.tmpl/env-nextcloudDB new file mode 100644 index 0000000..4d40dff --- /dev/null +++ b/secret.tmpl/env-nextcloudDB @@ -0,0 +1,8 @@ + +MYSQL_ROOT_PASSWORD= +MYSQL_DATABASE= +MYSQL_USER= +MYSQL_PASSWORD= + +NC_MYSQL_USER= +NC_MYSQL_PASSWORD= diff --git a/secret.tmpl/env-nextcloudServ b/secret.tmpl/env-nextcloudServ new file mode 100644 index 0000000..22de296 --- /dev/null +++ b/secret.tmpl/env-nextcloudServ @@ -0,0 +1,5 @@ + +NEXTCLOUD_ADMIN_USER= +NEXTCLOUD_ADMIN_PASSWORD= +MYSQL_HOST= +RAIN_LOOP= diff --git a/secret.tmpl/env-officeServ b/secret.tmpl/env-officeServ new file mode 100644 index 0000000..0324caa --- /dev/null +++ b/secret.tmpl/env-officeServ @@ -0,0 +1,3 @@ + +username= +password= diff --git a/secret.tmpl/env-roundcubeDB b/secret.tmpl/env-roundcubeDB new file mode 100644 index 0000000..6e2de9c --- /dev/null +++ b/secret.tmpl/env-roundcubeDB @@ -0,0 +1,4 @@ +MYSQL_ROOT_PASSWORD= +MYSQL_DATABASE= +MYSQL_USER= +MYSQL_PASSWORD= diff --git a/secret.tmpl/env-roundcubeServ b/secret.tmpl/env-roundcubeServ new file mode 100644 index 0000000..3f3cf66 --- /dev/null +++ b/secret.tmpl/env-roundcubeServ @@ -0,0 +1,6 @@ + +ROUNDCUBEMAIL_DB_TYPE= +ROUNDCUBEMAIL_DB_NAME= +ROUNDCUBEMAIL_DB_USER= +ROUNDCUBEMAIL_DB_PASSWORD= +ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE= diff --git a/secret.tmpl/env-sympaDB b/secret.tmpl/env-sympaDB new file mode 100644 index 0000000..6e2de9c --- /dev/null +++ b/secret.tmpl/env-sympaDB @@ -0,0 +1,4 @@ +MYSQL_ROOT_PASSWORD= +MYSQL_DATABASE= +MYSQL_USER= +MYSQL_PASSWORD= diff --git a/secret.tmpl/env-sympaServ b/secret.tmpl/env-sympaServ new file mode 100644 index 0000000..87d5c8b --- /dev/null +++ b/secret.tmpl/env-sympaServ @@ -0,0 +1,10 @@ +KEY= +CERT= +LISTMASTERS= +ADMINEMAIL= +SOAP_USER= +SOAP_PASSWORD= + +MYSQL_DATABASE= +MYSQL_USER= +MYSQL_PASSWORD= diff --git a/secret.tmpl/env-vaultwardenDB b/secret.tmpl/env-vaultwardenDB new file mode 100644 index 0000000..6e2de9c --- /dev/null +++ b/secret.tmpl/env-vaultwardenDB @@ -0,0 +1,4 @@ +MYSQL_ROOT_PASSWORD= +MYSQL_DATABASE= +MYSQL_USER= +MYSQL_PASSWORD= diff --git a/secret.tmpl/env-vaultwardenServ b/secret.tmpl/env-vaultwardenServ new file mode 100644 index 0000000..074e267 --- /dev/null +++ b/secret.tmpl/env-vaultwardenServ @@ -0,0 +1,2 @@ +DATABASE_URL= +ADMIN_TOKEN= diff --git a/secret.tmpl/env-vigiloDB b/secret.tmpl/env-vigiloDB new file mode 100644 index 0000000..45f98a0 --- /dev/null +++ b/secret.tmpl/env-vigiloDB @@ -0,0 +1,4 @@ +MYSQL_ROOT_PASSWORD= +MYSQL_USER= +MYSQL_PASSWORD= +MYSQL_DATABASE= diff --git a/secret.tmpl/env-vigiloServ b/secret.tmpl/env-vigiloServ new file mode 100644 index 0000000..d39005b --- /dev/null +++ b/secret.tmpl/env-vigiloServ @@ -0,0 +1,7 @@ +BIND= + +MYSQL_ROOT_PASSWORD= +MYSQL_USER= +MYSQL_PASSWORD= +MYSQL_DATABASE= +MYSQL_HOST= diff --git a/secret.tmpl/env-wpDB b/secret.tmpl/env-wpDB new file mode 100644 index 0000000..ede646e --- /dev/null +++ b/secret.tmpl/env-wpDB @@ -0,0 +1,8 @@ + +MYSQL_ROOT_PASSWORD= +MYSQL_DATABASE= +MYSQL_USER= +MYSQL_PASSWORD= + +WP_MYSQL_USER= +WP_MYSQL_PASSWORD= diff --git a/secret.tmpl/env-wpServ b/secret.tmpl/env-wpServ new file mode 100644 index 0000000..a6770be --- /dev/null +++ b/secret.tmpl/env-wpServ @@ -0,0 +1,6 @@ +# share with wpDB + +WORDPRESS_DB_HOST= +WORDPRESS_DB_USER= +WORDPRESS_DB_PASSWORD= +WORDPRESS_DB_NAME=