diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5a6a0fb --- /dev/null +++ b/.gitignore @@ -0,0 +1,63 @@ +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties + +# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) +!/.mvn/wrapper/maven-wrapper.jar + +### IntelliJ IDEA ### +.idea +*.iws +*.ipr + +.DS_Store +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### NetBeans ### +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ +/META-INF/ +src/main/resources/templates/ +/out/ +/bin/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..30abcfe --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2018 zhazhapan + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 0ad2e11..3d95cd0 100644 --- a/README.md +++ b/README.md @@ -1,92 +1,41 @@ -# webskt-query-agent +### 关于项目 +- 项目名称 `efo` 是 `Easy File Online` 的缩写,字面意思就是让您轻松实现线上文件管理 +- 本系统具有文件分享的功能,权限控制和自定义配置都很强大(可能还不完善) -## Getting started +- 系统后端框架有Spring Boot,Spring, SpringMVC,MyBatis; 前段框架有Bootstrap,Jquery, Layer, Vue。项目完全纯注解,零XML配置。 -To make it easy for you to get started with GitLab, here's a list of recommended next steps. +> 第一次运行系统,请先运行 [SQL代码](/mysql/efo.sql) , 并登陆系统修改用户 `system` (默认密码 `123456`)的密码 -Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! +### 环境要求 -## Add your files +- MySQL 5.7+ -- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files -- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: +- JDK 1.8+ -``` -cd existing_repo -git remote add origin https://git.mesalab.cn/web-sketch/webskt-query-agent.git -git branch -M main -git push -uf origin main -``` +### 系统部分截图(背景图片可通过配置设置) -## Integrate with your tools +- 登录页面(包含登录、注册、密码重置),路径 `/signin` -- [ ] [Set up project integrations](https://git.mesalab.cn/web-sketch/webskt-query-agent/-/settings/integrations) + ![登录页面](http://towerpan.qiniu.segocat.com/git/efo/signin.png) + +- 资源首页,路径 `/index` -## Collaborate with your team + ![登录页面](http://towerpan.qiniu.segocat.com/git/efo/index.png) + +- 上传页面,路径 `/upload` -- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) -- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) -- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) -- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) -- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) + ![登录页面](http://towerpan.qiniu.segocat.com/git/efo/upload.png) + +- 管理员管理页面,路径 `/admin` -## Test and Deploy + ![登录页面](http://towerpan.qiniu.segocat.com/git/efo/admin.png) + +- 远程文件管理(管理服务器端所有文件,只有系统用户才能进入此页面),路径 `/filemanager` -Use the built-in continuous integration in GitLab. - -- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) -- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) -- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) -- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) -- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) - -*** - -# Editing this README - -When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template. - -## Suggestions for a good README -Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. - -## Name -Choose a self-explaining name for your project. - -## Description -Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. - -## Badges -On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. - -## Visuals -Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. - -## Installation -Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. - -## Usage -Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. - -## Support -Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. - -## Roadmap -If you have ideas for releases in the future, it is a good idea to list them in the README. - -## Contributing -State if you are open to contributions and what your requirements are for accepting them. - -For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. - -You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. - -## Authors and acknowledgment -Show your appreciation to those who have contributed to the project. - -## License -For open source projects, say how it is licensed. - -## Project status -If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. + ![登录页面](http://towerpan.qiniu.segocat.com/git/efo/filemanager.png) + + > 此功能基于 [angular-filamanager](https://github.com/joni2back/angular-filemanager) 实现 + +**项目有不足的地方欢迎提出来哦,大家一起交流学习,觉得不错的话,Star来一个呗** diff --git a/efo.iml b/efo.iml new file mode 100644 index 0000000..d8b88fa --- /dev/null +++ b/efo.iml @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mvnw b/mvnw new file mode 100644 index 0000000..5bf251c --- /dev/null +++ b/mvnw @@ -0,0 +1,225 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Migwn, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +echo $MAVEN_PROJECTBASEDIR +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..019bd74 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,143 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" + +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/mysql/efo.mwb b/mysql/efo.mwb new file mode 100644 index 0000000..b81397a Binary files /dev/null and b/mysql/efo.mwb differ diff --git a/mysql/efo.mwb.bak b/mysql/efo.mwb.bak new file mode 100644 index 0000000..6eea521 Binary files /dev/null and b/mysql/efo.mwb.bak differ diff --git a/mysql/efo.sql b/mysql/efo.sql new file mode 100644 index 0000000..7bed513 --- /dev/null +++ b/mysql/efo.sql @@ -0,0 +1,188 @@ +-- MySQL Script generated by MySQL Workbench +-- Wed Jan 24 16:19:01 2018 +-- Model: New Model Version: 1.0 +-- MySQL Workbench Forward Engineering + +SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0; +SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0; +SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES'; + +-- ----------------------------------------------------- +-- Schema efo +-- ----------------------------------------------------- +-- 线上文件管理系统 +DROP SCHEMA IF EXISTS `efo` ; + +-- ----------------------------------------------------- +-- Schema efo +-- +-- 线上文件管理系统 +-- ----------------------------------------------------- +CREATE SCHEMA IF NOT EXISTS `efo` DEFAULT CHARACTER SET utf8 ; +USE `efo` ; + +-- ----------------------------------------------------- +-- Table `efo`.`user` +-- ----------------------------------------------------- +DROP TABLE IF EXISTS `efo`.`user` ; + +CREATE TABLE IF NOT EXISTS `efo`.`user` ( + `id` INT NOT NULL AUTO_INCREMENT COMMENT '用户编号', + `username` VARCHAR(16) NOT NULL DEFAULT '' COMMENT '用户名', + `real_name` VARCHAR(45) NULL DEFAULT '' COMMENT '真实姓名', + `email` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '邮箱地址', + `password` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '登录密码', + `permission` INT NOT NULL DEFAULT 1 COMMENT '0(禁止登录),1(正常,普通用户),2(正常,管理员),3(正常,超级管理员)', + `create_time` DATETIME NOT NULL DEFAULT current_timestamp COMMENT '注册时间', + `last_login_time` DATETIME NOT NULL DEFAULT current_timestamp COMMENT '最后一次登录时间', + `is_downloadable` INT NOT NULL DEFAULT 1 COMMENT '(全局权限)用户是否可以下载,0不可以,1可以', + `is_uploadable` INT NOT NULL DEFAULT 1 COMMENT '(全局权限)用户是否可以上传,0不可以,1可以', + `is_visible` INT NOT NULL DEFAULT 1 COMMENT '(全局权限)用户是否可以查看文件,0不可以,1可以', + `is_deletable` INT NOT NULL DEFAULT 0 COMMENT '(全局权限)用户可以删除文件,0不可以,1可以', + `is_updatable` INT NOT NULL DEFAULT 0 COMMENT '(全局权限)用户是否可以更新文件,0不可以,1可以', + `avatar` VARCHAR(255) NULL DEFAULT '' COMMENT '头像', + PRIMARY KEY (`id`), + UNIQUE INDEX `id_UNIQUE` (`id` ASC), + UNIQUE INDEX `username_UNIQUE` (`username` ASC), + UNIQUE INDEX `create_time_UNIQUE` (`create_time` ASC), + UNIQUE INDEX `email_UNIQUE` (`email` ASC)) +ENGINE = InnoDB +COMMENT = '用户表'; + + +-- ----------------------------------------------------- +-- Table `efo`.`category` +-- ----------------------------------------------------- +DROP TABLE IF EXISTS `efo`.`category` ; + +CREATE TABLE IF NOT EXISTS `efo`.`category` ( + `id` INT NOT NULL AUTO_INCREMENT, + `name` VARCHAR(45) NOT NULL DEFAULT '', + `create_time` DATETIME NOT NULL DEFAULT current_timestamp, + PRIMARY KEY (`id`), + UNIQUE INDEX `name_UNIQUE` (`name` ASC), + UNIQUE INDEX `cat_id_UNIQUE` (`id` ASC)) +ENGINE = InnoDB +COMMENT = '文件分类'; + + +-- ----------------------------------------------------- +-- Table `efo`.`file` +-- ----------------------------------------------------- +DROP TABLE IF EXISTS `efo`.`file` ; + +CREATE TABLE IF NOT EXISTS `efo`.`file` ( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '编号', + `name` VARCHAR(256) NULL DEFAULT '' COMMENT '文件名', + `suffix` VARCHAR(16) NOT NULL DEFAULT '' COMMENT '文件后缀', + `local_url` VARCHAR(1024) NOT NULL DEFAULT '' COMMENT '本地路径', + `visit_url` VARCHAR(1024) NOT NULL DEFAULT '' COMMENT '客户端访问路径', + `size` BIGINT NOT NULL DEFAULT 0 COMMENT '文件大小,单位bit', + `create_time` DATETIME NOT NULL DEFAULT current_timestamp COMMENT '创建时间', + `description` VARCHAR(1024) NULL DEFAULT '' COMMENT '文件描述', + `check_times` INT NOT NULL DEFAULT 0 COMMENT '查看次数', + `download_times` INT NOT NULL DEFAULT 0 COMMENT '下载次数', + `tag` VARCHAR(45) NULL DEFAULT '' COMMENT '文件标签', + `user_id` INT NOT NULL, + `category_id` INT NOT NULL, + `is_downloadable` INT NOT NULL DEFAULT 1 COMMENT '(全局权限)文件是否可以下载,0不可以,1可以', + `is_uploadable` INT NOT NULL DEFAULT 1 COMMENT '(全局权限)文件夹是否允许上传(需要判断文件是否是文件夹),0不可以,1可以', + `is_visible` INT NOT NULL DEFAULT 1 COMMENT '(全局权限)文件是否可见,0不可以,1可以', + `is_deletable` INT NOT NULL DEFAULT 1 COMMENT '(全局权限)文件是否可以删除,0不可以,1可以', + `is_updatable` INT NOT NULL DEFAULT 1 COMMENT '(全局权限)文件是否可以更新,0不可以,1可以', + `last_modify_time` DATETIME NOT NULL DEFAULT current_timestamp COMMENT '最近一次修改时间', + PRIMARY KEY (`id`), + UNIQUE INDEX `file_id_UNIQUE` (`id` ASC), + INDEX `fk_file_user_idx` (`user_id` ASC), + INDEX `fk_file_category1_idx` (`category_id` ASC), + UNIQUE INDEX `local_url_UNIQUE` (`local_url` ASC), + UNIQUE INDEX `visit_url_UNIQUE` (`visit_url` ASC), + CONSTRAINT `fk_file_user` + FOREIGN KEY (`user_id`) + REFERENCES `efo`.`user` (`id`) + ON DELETE NO ACTION + ON UPDATE NO ACTION, + CONSTRAINT `fk_file_category1` + FOREIGN KEY (`category_id`) + REFERENCES `efo`.`category` (`id`) + ON DELETE NO ACTION + ON UPDATE NO ACTION) +ENGINE = InnoDB +COMMENT = '文件列表'; + + +-- ----------------------------------------------------- +-- Table `efo`.`download` +-- ----------------------------------------------------- +DROP TABLE IF EXISTS `efo`.`download` ; + +CREATE TABLE IF NOT EXISTS `efo`.`download` ( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '编号', + `create_time` DATETIME NOT NULL DEFAULT current_timestamp COMMENT '下载时间', + `user_id` INT NOT NULL, + `file_id` BIGINT NOT NULL, + PRIMARY KEY (`id`), + UNIQUE INDEX `id_UNIQUE` (`id` ASC), + INDEX `fk_download_user1_idx` (`user_id` ASC), + INDEX `fk_download_file1_idx` (`file_id` ASC), + CONSTRAINT `fk_download_user1` + FOREIGN KEY (`user_id`) + REFERENCES `efo`.`user` (`id`) + ON DELETE NO ACTION + ON UPDATE NO ACTION, + CONSTRAINT `fk_download_file1` + FOREIGN KEY (`file_id`) + REFERENCES `efo`.`file` (`id`) + ON DELETE NO ACTION + ON UPDATE NO ACTION) +ENGINE = InnoDB +COMMENT = '下载历史表'; + + +-- ----------------------------------------------------- +-- Table `efo`.`auth` +-- ----------------------------------------------------- +DROP TABLE IF EXISTS `efo`.`auth` ; + +CREATE TABLE IF NOT EXISTS `efo`.`auth` ( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `is_uploadable` INT NOT NULL DEFAULT 1 COMMENT '是否可以上传(需要判断对应的文件是否是文件夹),0不可以,1可以', + `is_deletable` INT NOT NULL DEFAULT 1 COMMENT '是否可以删除,0不可以,1可以', + `is_updatable` INT NOT NULL DEFAULT 1 COMMENT '是否可以更新,0不可以,1可以', + `user_id` INT NOT NULL, + `file_id` BIGINT NOT NULL, + `is_visible` INT NOT NULL DEFAULT 1 COMMENT '是否可以查看,0不可以,1可以', + `is_downloadable` INT NOT NULL DEFAULT 1 COMMENT '用户是否可以下载,0不可以,1可以', + `create_time` DATETIME NOT NULL DEFAULT current_timestamp COMMENT '创建时间', + PRIMARY KEY (`id`), + UNIQUE INDEX `id_UNIQUE` (`id` ASC), + INDEX `fk_auth_user1_idx` (`user_id` ASC), + INDEX `fk_auth_file1_idx` (`file_id` ASC), + CONSTRAINT `fk_auth_user1` + FOREIGN KEY (`user_id`) + REFERENCES `efo`.`user` (`id`) + ON DELETE NO ACTION + ON UPDATE NO ACTION, + CONSTRAINT `fk_auth_file1` + FOREIGN KEY (`file_id`) + REFERENCES `efo`.`file` (`id`) + ON DELETE NO ACTION + ON UPDATE NO ACTION) +ENGINE = InnoDB +COMMENT = '用户对应指定文件的权限表,覆盖用户表的权限'; + + +SET SQL_MODE=@OLD_SQL_MODE; +SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; +SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; + +INSERT INTO user(username,real_name,email,password,permission,is_deletable,is_updatable) VALUES("system","系统","system@local.host",sha2("123456",256),3,1,1); + +#请确保数据库中始终有“未分类”这个分类,否则系统运行时有可能出错 +INSERT INTO category(name) VALUES("未分类"); + +DROP USER IF EXISTS 'zhazhapan'@'localhost'; + +CREATE USER 'zhazhapan'@'localhost' IDENTIFIED BY 'zhazhapan'; + +GRANT INSERT, DELETE, UPDATE, SELECT ON efo.* TO 'zhazhapan'@'localhost'; \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..cc6f457 --- /dev/null +++ b/pom.xml @@ -0,0 +1,224 @@ + + + 4.0.0 + + com.mesasoft.cn + webskt-query-agent + 1.0 + jar + + webskt-query-agent + 简单的线上文件管理系统 + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.M7 + + + + + + UTF-8 + UTF-8 + 1.8 + 1.8 + 1.8 + + + + + org.apache.shiro + shiro-all + 1.4.0 + + + commons-fileupload + commons-fileupload + 1.3.3 + + + commons-io + commons-io + + + + + com.zhazhapan + util + 1.1.0 + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 1.3.1 + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-devtools + runtime + + + mysql + mysql-connector-java + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + asm + org.ow2.asm + + + + + com.spring4all + swagger-spring-boot-starter + 1.7.1.RELEASE + + + guava + com.google.guava + + + + + org.projectlombok + lombok + 1.18.4 + true + + + org.apache.httpcomponents + httpclient + 4.5.6 + + + com.alibaba + druid + 1.0.9 + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + com.mesasoft.cn.SketchApplication + + + + + repackage + + + + + + maven-assembly-plugin + 2.2-beta-5 + + + + true + com.mesasoft.cn.SketchApplication + + + + jar-with-dependencies + + + + + assemble-all + package + + single + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.12.4 + + true + + + + + + src/main/resources + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + \ No newline at end of file diff --git a/src/main/java/com/mesasoft/cn/SketchApplication.java b/src/main/java/com/mesasoft/cn/SketchApplication.java new file mode 100644 index 0000000..44a3041 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/SketchApplication.java @@ -0,0 +1,42 @@ +package com.mesasoft.cn; + +import com.spring4all.swagger.EnableSwagger2Doc; +import com.zhazhapan.config.JsonParser; +import com.mesasoft.cn.config.TokenConfig; +import com.mesasoft.cn.modules.constant.ConfigConsts; +import com.mesasoft.cn.modules.constant.DefaultValues; +import com.zhazhapan.util.FileExecutor; +import com.zhazhapan.util.MailSender; +import com.zhazhapan.util.ReflectUtils; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import java.io.IOException; +import java.util.Hashtable; +import java.util.List; + +/** + * @author pantao + */ +@SpringBootApplication +@EnableSwagger2Doc +@MapperScan("com.mesasoft.cn.dao") +@EnableTransactionManagement +public class SketchApplication { + + public static JsonParser settings = new JsonParser(); + + public static List> controllers; + + public static Hashtable tokens; + + public static void main(String[] args) throws IOException, ClassNotFoundException { + settings.setJsonObject(FileExecutor.read(SketchApplication.class.getResourceAsStream(DefaultValues.SETTING_PATH))); + MailSender.config(settings.getObjectUseEval(ConfigConsts.EMAIL_CONFIG_OF_SETTINGS)); + controllers = ReflectUtils.getClasses(DefaultValues.CONTROLLER_PACKAGE); + tokens = TokenConfig.loadToken(); + SpringApplication.run(SketchApplication.class, args); + } +} diff --git a/src/main/java/com/mesasoft/cn/annotation/AuthInterceptor.java b/src/main/java/com/mesasoft/cn/annotation/AuthInterceptor.java new file mode 100644 index 0000000..3b1a658 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/annotation/AuthInterceptor.java @@ -0,0 +1,22 @@ +package com.mesasoft.cn.annotation; + +import com.mesasoft.cn.enums.InterceptorLevel; + +import java.lang.annotation.*; + +/** + * @author pantao + * @since 2018/1/25 + */ +@Documented +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface AuthInterceptor { + + /** + * 定义拦截级别,默认为用户级别拦截 + * + * @return {@link InterceptorLevel} + */ + InterceptorLevel value() default InterceptorLevel.USER; +} diff --git a/src/main/java/com/mesasoft/cn/config/SettingConfig.java b/src/main/java/com/mesasoft/cn/config/SettingConfig.java new file mode 100644 index 0000000..a78197a --- /dev/null +++ b/src/main/java/com/mesasoft/cn/config/SettingConfig.java @@ -0,0 +1,100 @@ +package com.mesasoft.cn.config; + +import com.mesasoft.cn.SketchApplication; +import com.mesasoft.cn.modules.constant.ConfigConsts; +import com.mesasoft.cn.util.CommonUtils; +import com.zhazhapan.modules.constant.ValueConsts; +import com.zhazhapan.util.Checker; +import com.zhazhapan.util.FileExecutor; +import com.zhazhapan.util.Formatter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Date; + +/** + * @author pantao + * @since 2018/1/26 + */ +public class SettingConfig { + + private static final String WINDOWS = "windows"; + + private static final String MAC = "mac"; + + private static final String LINUX = "linux"; + + private static Logger logger = LoggerFactory.getLogger(SettingConfig.class); + + private static OsName currentOS; + + static { + if (Checker.isWindows()) { + currentOS = OsName.WINDOWS; + } else if (Checker.isMacOS()) { + currentOS = OsName.MAC; + } else { + currentOS = OsName.LINUX; + } + } + + public static int[] getAuth(String jsonPath) { + int[] auth = new int[5]; + for (int i = 0; i < ConfigConsts.AUTH_OF_SETTINGS.length; i++) { + String key = jsonPath + ValueConsts.DOT_SIGN + ConfigConsts.AUTH_OF_SETTINGS[i]; + auth[i] = SketchApplication.settings.getBooleanUseEval(key) ? 1 : 0; + } + return auth; + } + + public static String getUploadStoragePath() { + String parent = getStoragePath(ConfigConsts.UPLOAD_PATH_OF_SETTING); + String formatWay = SketchApplication.settings.getStringUseEval(ConfigConsts.UPLOAD_FORM_OF_SETTING); + String childPath = ValueConsts.SEPARATOR + Formatter.datetimeToCustomString(new Date(), formatWay); + String path = parent + childPath; + if (!FileExecutor.createFolder(path)) { + path = ConfigConsts.DEFAULT_UPLOAD_PATH + childPath; + FileExecutor.createFolder(path); + } + logger.info("upload path: " + path); + return path; + } + + public static String getAvatarStoragePath() { + String path = getStoragePath(ConfigConsts.UPLOAD_PATH_OF_SETTING) + ValueConsts.SEPARATOR + "avatar"; + FileExecutor.createFolder(path); + return path; + } + + public static String getStoragePath(String path) { + path += ValueConsts.DOT_SIGN; + if (currentOS == OsName.WINDOWS) { + path += WINDOWS; + } else if (currentOS == OsName.MAC) { + path += MAC; + } else { + path += LINUX; + } + return CommonUtils.checkPath(SketchApplication.settings.getStringUseEval(path)); + } + + /** + * 当前系统名称 + */ + public enum OsName { + /** + * windows系统 + */ + WINDOWS, + + /** + * MacOS系统 + */ + MAC, + + /** + * Linux系统 + */ + LINUX + } +} diff --git a/src/main/java/com/mesasoft/cn/config/TokenConfig.java b/src/main/java/com/mesasoft/cn/config/TokenConfig.java new file mode 100644 index 0000000..7a83572 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/config/TokenConfig.java @@ -0,0 +1,80 @@ +package com.mesasoft.cn.config; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.mesasoft.cn.SketchApplication; +import com.mesasoft.cn.modules.constant.ConfigConsts; +import com.zhazhapan.modules.constant.ValueConsts; +import com.zhazhapan.util.Checker; +import com.zhazhapan.util.FileExecutor; +import com.zhazhapan.util.Formatter; +import com.zhazhapan.util.RandomUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Hashtable; + +/** + * @author pantao + * @since 2018/1/26 + */ +public class TokenConfig { + + private static Logger logger = LoggerFactory.getLogger(TokenConfig.class); + + public static String generateToken(String token, int userId) { + if (Checker.isNotEmpty(token)) { + SketchApplication.tokens.remove(token); + } + return generateToken(userId); + } + + public static String generateToken(int userId) { + String token = RandomUtils.getRandomStringOnlyLetter(ValueConsts.THIRTY_TWO_INT); + SketchApplication.tokens.put(token, userId); + saveToken(); + return token; + } + + public static void saveToken() { + String tokens = Formatter.mapToJson(SketchApplication.tokens); + try { + FileExecutor.saveFile(SettingConfig.getStoragePath(ConfigConsts.TOKEN_OF_SETTINGS), tokens); + } catch (Exception e) { + logger.error("save token error: " + e.getMessage()); + } + } + + public static Hashtable loadToken() { + Hashtable tokens = new Hashtable<>(ValueConsts.SIXTEEN_INT); + try { + String token = FileExecutor.readFile(SettingConfig.getStoragePath(ConfigConsts.TOKEN_OF_SETTINGS)); + JSONArray array = JSON.parseArray(token); + array.forEach(object -> { + JSONObject jsonObject = (JSONObject) object; + tokens.put(jsonObject.getString(ValueConsts.KEY_STRING), jsonObject.getInteger(ValueConsts + .VALUE_STRING)); + }); + } catch (Exception e) { + logger.error("load token error: " + e.getMessage()); + } + return tokens; + } + + public static void removeTokenByValue(int userId) { + if (userId > 0) { + String removeKey = ""; + for (String key : SketchApplication.tokens.keySet()) { + if (SketchApplication.tokens.get(key) == userId) { + removeKey = key; + break; + } + } + if (Checker.isNotEmpty(removeKey)) { + SketchApplication.tokens.remove(removeKey); + TokenConfig.saveToken(); + } + } + } +} diff --git a/src/main/java/com/mesasoft/cn/config/WebMvcConfig.java b/src/main/java/com/mesasoft/cn/config/WebMvcConfig.java new file mode 100644 index 0000000..51380b4 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/config/WebMvcConfig.java @@ -0,0 +1,63 @@ +package com.mesasoft.cn.config; + +import com.alibaba.fastjson.JSONObject; +import com.mesasoft.cn.exception.GlobalExceptionHandler; +import com.mesasoft.cn.interceptor.WebInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.HandlerExceptionResolver; +import org.springframework.web.servlet.config.annotation.*; +import org.springframework.web.servlet.view.InternalResourceViewResolver; + +import java.util.List; + +/** + * @author pantao + * @since 2018/1/22 + */ +@Configuration +public class WebMvcConfig implements WebMvcConfigurer { + + @Override + public void configureViewResolvers(ViewResolverRegistry registry) { + InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); + viewResolver.setPrefix("/"); + viewResolver.setSuffix(".html"); + registry.viewResolver(viewResolver); + } + + @Override + public void configureHandlerExceptionResolvers(List resolvers) { + resolvers.add(globalExceptionHandler()); + } + + @Override + public void configurePathMatch(PathMatchConfigurer configurer) { + configurer.setUseSuffixPatternMatch(false); + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(webInterceptor()); + } + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/assets/**").addResourceLocations("classpath:/assets/"); + } + + @Bean + public WebInterceptor webInterceptor() { + return new WebInterceptor(); + } + + @Bean + public GlobalExceptionHandler globalExceptionHandler() { + return new GlobalExceptionHandler(); + } + + @Bean + public JSONObject jsonObject() { + return new JSONObject(); + } +} diff --git a/src/main/java/com/mesasoft/cn/dao/AuthDAO.java b/src/main/java/com/mesasoft/cn/dao/AuthDAO.java new file mode 100644 index 0000000..2d62e69 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/dao/AuthDAO.java @@ -0,0 +1,107 @@ +package com.mesasoft.cn.dao; + +import com.mesasoft.cn.dao.sqlprovider.AuthSqlProvider; +import com.mesasoft.cn.entity.Auth; +import com.mesasoft.cn.model.AuthRecord; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + * @author pantao + * @since 2018/1/19 + */ +@Repository +public interface AuthDAO { + + /** + * 检测某个权限是否存在 + * + * @param userId 用户编号 + * @param fileId 文件编号 + * + * @return {@link Auth} + */ + @Select("select * from auth where user_id=#{userId} and file_id=#{fileId}") + Auth exists(@Param("userId") int userId, @Param("fileId") long fileId); + + /** + * 批量删除权限记录 + * + * @param ids 权限编号集 + * + * @return 是否删除成功 + */ + @DeleteProvider(type = AuthSqlProvider.class, method = "batchDelete") + boolean batchDelete(@Param("ids") String ids); + + /** + * 添加一条权限记录 + * + * @param auth {@link Auth} + * + * @return 是否添加成功 + */ + @Insert("insert into auth(user_id,file_id,is_downloadable,is_uploadable,is_deletable,is_updatable,is_visible) " + + "values(#{userId},#{fileId},#{isDownloadable},#{isUploadable},#{isDeletable},#{isUpdatable},#{isVisible})") + boolean insertAuth(Auth auth); + + /** + * 删除一条权限记录 + * + * @param id 编号 + */ + @Delete("delete from auth where id=#{id}") + void removeAuthById(int id); + + /** + * 删除一条权限记录 + * + * @param userId 编号 + */ + @Delete("delete from auth where user_id=#{userId}") + void removeAuthByUserId(int userId); + + /** + * 删除一条权限记录 + * + * @param fileId 编号 + * + * @return 是否删除成功 + */ + @Delete("delete from auth where file_id=#{fileId}") + boolean removeAuthByFileId(long fileId); + + /** + * 更新权限记录 + * + * @param id 编号 + * @param isDownloadable 下载权限 + * @param isUploadable 上传权限 + * @param isVisible 可查权限 + * @param isDeletable 删除权限 + * @param isUpdatable 更新权限 + * + * @return 是否更新成功 + */ + @UpdateProvider(type = AuthSqlProvider.class, method = "updateAuthById") + boolean updateAuthById(@Param("id") long id, @Param("isDownloadable") int isDownloadable, @Param("isUploadable") + int isUploadable, @Param("isDeletable") int isDeletable, @Param("isUpdatable") int isUpdatable, @Param + ("isVisible") int isVisible); + + /** + * 获取权限记录 + * + * @param id 编号,值小于等于0时不作为条件 + * @param userId 用户编号,值小于等于0时不作为条件 + * @param fileId 文件编号,值小于等于0时不作为条件 + * @param fileName 模糊搜索文件名(当参数不为空时) + * @param offset 偏移 + * + * @return {@link List} + */ + @SelectProvider(type = AuthSqlProvider.class, method = "getAuthBy") + List listAuthBy(@Param("id") long id, @Param("userId") int userId, @Param("fileId") long fileId, + @Param("fileName") String fileName, @Param("offset") int offset); +} diff --git a/src/main/java/com/mesasoft/cn/dao/CategoryDAO.java b/src/main/java/com/mesasoft/cn/dao/CategoryDAO.java new file mode 100644 index 0000000..848d6b8 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/dao/CategoryDAO.java @@ -0,0 +1,103 @@ +package com.mesasoft.cn.dao; + +import com.mesasoft.cn.entity.Category; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + * @author pantao + * @since 2018/1/18 + */ +@Repository +public interface CategoryDAO { + + /** + * 通过分类名获取ID + * + * @param name 分类名 + * + * @return {@link Integer} + */ + @Select("select id from category where name=#{name}") + int getIdByName(String name); + + /** + * 添加一个分类 + * + * @param name 分类名 + * + * @return 是否添加成功 + */ + @Insert("insert into category(name) values(#{name})") + boolean insertCategory(String name); + + /** + * 通过编号删除一个分类 + * + * @param id 编号 + * + * @return 是否删除成功 + */ + @Delete("delete from category where id=#{id}") + boolean removeCategoryById(int id); + + /** + * 通过名称删除一个分类 + * + * @param name 分类名称 + * + * @return 是否删除成功 + */ + @Delete("delete from category where name=#{name}") + boolean removeCategoryByName(String name); + + /** + * 更新一个分类名 + * + * @param name 分类名 + * @param id 分类ID + * + * @return 是否更新成功 + */ + @Update("update category set name=#{name} where id=#{id}") + boolean updateNameById(@Param("id") int id, @Param("name") String name); + + /** + * 通过分类名更新分类名 + * + * @param newName 新的分类名 + * @param oldName 旧的分类名 + */ + @Update("update category set name=#{newName} where name=#{oldName}") + void updateNameByName(String newName, String oldName); + + /** + * 获取所有分类 + * + * @return {@link List} + */ + @Select("select * from category") + List listCategory(); + + /** + * 通过编号获取一个分类 + * + * @param id 编号 + * + * @return {@link Category} + */ + @Select("select * from category where id=#{id}") + Category getCategoryById(int id); + + /** + * 通过名称获取一个分类 + * + * @param name 名称 + * + * @return {@link Category} + */ + @Select("select * from category where name=#{name}") + Category getCategoryByName(String name); +} diff --git a/src/main/java/com/mesasoft/cn/dao/DownloadedDAO.java b/src/main/java/com/mesasoft/cn/dao/DownloadedDAO.java new file mode 100644 index 0000000..263945c --- /dev/null +++ b/src/main/java/com/mesasoft/cn/dao/DownloadedDAO.java @@ -0,0 +1,53 @@ +package com.mesasoft.cn.dao; + +import com.mesasoft.cn.dao.sqlprovider.DownloadedSqlProvider; +import com.mesasoft.cn.model.DownloadRecord; +import org.apache.ibatis.annotations.Delete; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.SelectProvider; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + * @author pantao + * @since 2018/1/18 + */ +@Repository +public interface DownloadedDAO { + + /** + * 新增一条下载记录 + * + * @param userId 用户编号 + * @param fileId 文件编号 + */ + @Insert("insert into download(user_id,file_id) values(#{userId},#{fileId})") + void insertDownload(@Param("userId") int userId, @Param("fileId") long fileId); + + /** + * 查询下载记录 + * + * @param userId 用户编号,不使用用户编号作为条件时设置值小于等于0即可 + * @param fileId 文件编号,不使用文件编号作为条件时设置值小于等于0即可 + * @param categoryId 分类编号,不用分类编号作为条件时设置值小于等于0即可 + * @param fileName 文件名,不使用文件名作为条件时设置值为空即可 + * @param offset 偏移 + * + * @return 下载记录 + */ + @SelectProvider(type = DownloadedSqlProvider.class, method = "getDownloadBy") + List listDownloadedBy(@Param("userId") int userId, @Param("fileId") long fileId, @Param + ("fileName") String fileName, @Param("categoryId") int categoryId, @Param("offset") int offset); + + /** + * 删除文件 + * + * @param fileId 文件编号 + * + * @return 是否删除成功 + */ + @Delete("delete from download where file_id=#{fileId}") + boolean removeByFileId(long fileId); +} diff --git a/src/main/java/com/mesasoft/cn/dao/FileDAO.java b/src/main/java/com/mesasoft/cn/dao/FileDAO.java new file mode 100644 index 0000000..fe33c54 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/dao/FileDAO.java @@ -0,0 +1,340 @@ +package com.mesasoft.cn.dao; + +import com.mesasoft.cn.dao.sqlprovider.FileSqlProvider; +import com.mesasoft.cn.entity.File; +import com.mesasoft.cn.model.BaseAuthRecord; +import com.mesasoft.cn.model.FileBasicRecord; +import com.mesasoft.cn.model.FileRecord; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + * @author pantao + * @since 2018/1/19 + */ +@Repository +public interface FileDAO { + + /** + * 获取文件权限 + * + * @param id 文件编号 + * + * @return {@link BaseAuthRecord} + */ + @Select("select is_downloadable,is_uploadable,is_deletable,is_updatable,is_visible from file where id=#{id}") + BaseAuthRecord getAuth(long id); + + /** + * 通过编号获取文件 + * + * @param id 编号 + * + * @return {@link File} + */ + @Select("select * from file where id=#{id}") + File getById(long id); + + /** + * 通过ID获取本地路径 + * + * @param fileId 文件编号 + * + * @return {@link String} + */ + @Select("select local_url from file where id=#{id}") + String getLocalUrlById(long fileId); + + /** + * 通过编号删除文件 + * + * @param id 编号 + * + * @return 是否删除成功 + */ + @Delete("delete from file where id=#{id}") + boolean removeById(long id); + + /** + * 通过本地路径获取文件编号 + * + * @param visitUrl 本地路径 + * + * @return 编号 + */ + @Select("select id from file where visit_url=#{visitUrl}") + long getIdByVisitUrl(String visitUrl); + + /** + * 通过本地路径获取文件编号 + * + * @param localUrl 本地路径 + * + * @return 编号 + */ + @Select("select id from file where local_url=#{localUrl}") + long getIdByLocalUrl(String localUrl); + + /** + * 通过访问路径获取本地文件路径 + * + * @param visitUrl 访问路径 + * + * @return {@link String} + */ + @Select("select local_url from file where visit_url=#{visitUrl}") + String getLocalUrlByVisitUrl(String visitUrl); + + /** + * 通过访问路径删除 + * + * @param visitUrl 访问路径 + * + * @return 是否删除成功 + */ + @Delete("delete from file where visit_url=#{visitUrl}") + boolean removeByVisitUrl(String visitUrl); + + /** + * 通过本地路径删除 + * + * @param localUrl 本地路径 + * + * @return 是否删除成功 + */ + @Delete("delete from file where local_url=#{localUrl}") + boolean removeByLocalUrl(String localUrl); + + /** + * 检查本地路径 + * + * @param localUrl 本地路径 + * + * @return {@link Integer} + */ + @Select("select count(*) from file where local_url=#{localUrl}") + int checkLocalUrl(String localUrl); + + /** + * 检查访问路径 + * + * @param visitUrl 访问路径 + * + * @return {@link Integer} + */ + @Select("select count(*) from file where visit_url=#{visitUrl}") + int checkVisitUrl(String visitUrl); + + /** + * 添加一个文件 + * + * @param file {@link File} + * + * @return 是否添加成功 + */ + @Insert("insert into file(name,suffix,local_url,visit_url,size,description,tag,user_id,category_id," + + "is_downloadable,is_uploadable,is_deletable,is_updatable,is_visible) values(#{name},#{suffix}," + + "#{localUrl},#{visitUrl},#{size},#{description},#{tag},#{userId},#{categoryId},#{isDownloadable}," + + "#{isUploadable},#{isDeletable},#{isUpdatable},#{isVisible})") + boolean insertFile(File file); + + /** + * 删除一个文件 + * + * @param id 文件编号 + */ + @Delete("delete from file where id=#{id}") + void deleteFileById(int id); + + /** + * 删除文件 + * + * @param userId 用户编号 + */ + @Delete("delete from file where user_id=#{userId}") + void deleteFileByUserId(int userId); + + /** + * 删除文件 + * + * @param categoryId 分类编号 + */ + @Delete("delete from file where category_d=#{categoryId}") + void deleteFileByCategoryId(int categoryId); + + /** + * 更新文件基本信息 + * + * @param file 文件 + * + * @return 是否更新成功 + */ + @Update("update file set name=#{name},suffix=#{suffix},local_url=#{localUrl},visit_url=#{visitUrl}," + + "description=#{description},tag=#{tag},category_id=#{categoryId},last_modify_time=current_timestamp " + + "where" + " id=#{id}") + boolean updateFileInfo(File file); + + /** + * 更新文件权限 + * + * @param id 编号 + * @param isDownloadable 下载权限 + * @param isUploadable 上传权限 + * @param isVisible 可查权限 + * @param isDeletable 删除权限 + * @param isUpdatable 上传权限 + * + * @return 是否更新成功 + */ + @UpdateProvider(type = FileSqlProvider.class, method = "updateAuthById") + boolean updateAuthById(@Param("id") long id, @Param("isDownloadable") int isDownloadable, @Param("isUploadable") + int isUploadable, @Param("isDeletable") int isDeletable, @Param("isUpdatable") int isUpdatable, @Param + ("isVisible") int isVisible); + + /** + * 更新文件名 + * + * @param id 编号 + * @param name 文件名 + * @param suffix 后缀名 + */ + @Update("update file set name=#{name},suffix=#{suffix},last_modify_time=current_timestamp where id=#{id}") + void updateFileNameById(@Param("id") int id, @Param("name") String name, @Param("suffix") String suffix); + + /** + * 更新文件修改时间 + * + * @param id 编号 + */ + @Update("update file set last_modify_time=current_timestamp where id=#{id}") + void updateLastModifyTimeById(int id); + + /** + * 更新文件本地路径 + * + * @param id 编号 + * @param localUrl 本地路径 + */ + @Update("update file set local_url=#{localUrl} where id=#{id}") + void updateLocalUrlById(@Param("id") int id, @Param("localUrl") String localUrl); + + /** + * 更新文件访问路径 + * + * @param id 编号 + * @param visitUrl 访问链接 + */ + @Update("update file set visit_url=#{visitUrl} where id=#{id}") + void updateVisitUrlById(@Param("id") int id, @Param("visitUrl") String visitUrl); + + /** + * 更新文件描述 + * + * @param id 文件编号 + * @param description 描述 + */ + @Update("update file set description=#{description} where id=#{id}") + void updateDescriptionById(@Param("id") int id, @Param("description") String description); + + /** + * 更新文件查看次数 + * + * @param id 编号 + */ + @Update("update file set check_times=check_times+1 where id=#{id}") + void updateCheckTimesById(int id); + + /** + * 更新文件下载次数 + * + * @param id 编号 + */ + @Update("update file set download_times=download_times+1 where id=#{id}") + void updateDownloadTimesById(long id); + + /** + * 更新文件标签 + * + * @param id 编号 + * @param tag 标签 + */ + @Update("update file set tag=#{tag} where id=#{id}") + void updateTagById(@Param("id") int id, @Param("tag") String tag); + + /** + * 更新文件分类 + * + * @param id 编号 + * @param categoryId 分类编号 + */ + @Update("update file set category_id=#{categoryId} where id=#{id}") + void updateCategoryById(@Param("id") int id, @Param("categoryId") int categoryId); + + /** + * 获取文件信息 + * + * @param visitUrl 访问链接 + * + * @return {@link File} + */ + @Select("select * from file where visit_url=#{visitUrl}") + File getFileByVisitUrl(String visitUrl); + + /** + * 获取所有文件 + * + * @param userId 用户编号 + * @param offset 偏移 + * @param categoryId 分类编号 + * @param orderBy 排序方式 + * @param search 搜索 + * + * @return {@link List} + */ + @SelectProvider(type = FileSqlProvider.class, method = "getAll") + List listAll(@Param("userId") int userId, @Param("offset") int offset, @Param("categoryId") int + categoryId, @Param("orderBy") String orderBy, @Param("search") String search); + + /** + * 获取用户的上传资源 + * + * @param userId 用户编号 + * @param offset 偏移 + * @param search 搜索 + * + * @return {@link List} + */ + @SelectProvider(type = FileSqlProvider.class, method = "getUserUploaded") + List listUserUploaded(@Param("userId") int userId, @Param("offset") int offset, @Param("search") + String search); + + /** + * 获取用户的下载资源 + * + * @param userId 用户编号 + * @param offset 偏移 + * @param search 搜索 + * + * @return {@link List} + */ + @SelectProvider(type = FileSqlProvider.class, method = "getUserDownloaded") + List listUserDownloaded(@Param("userId") int userId, @Param("offset") int offset, @Param("search") + String search); + + /** + * 查询文件基本信息 + * + * @param userId 用户编号,不使用用户编号作为条件时设置值小于等于0即可 + * @param fileId 文件编号,不使用文件编号作为条件时设置值小于等于0即可 + * @param categoryId 分类编号,不用分类编号作为条件时设置值小于等于0即可 + * @param fileName 文件名,不使用文件名作为条件时设置值为空即可 + * @param offset 偏移 + * + * @return 上传记录 + */ + @SelectProvider(type = FileSqlProvider.class, method = "getBasicBy") + List listBasicBy(@Param("userId") int userId, @Param("fileId") long fileId, @Param("fileName") + String fileName, @Param("categoryId") int categoryId, @Param("offset") int offset); +} diff --git a/src/main/java/com/mesasoft/cn/dao/UploadedDAO.java b/src/main/java/com/mesasoft/cn/dao/UploadedDAO.java new file mode 100644 index 0000000..8fa32d0 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/dao/UploadedDAO.java @@ -0,0 +1,32 @@ +package com.mesasoft.cn.dao; + +import com.mesasoft.cn.dao.sqlprovider.UploadedSqlProvider; +import com.mesasoft.cn.model.UploadedRecord; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.SelectProvider; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + * @author pantao + * @since 2018/2/28 + */ +@Repository +public interface UploadedDAO { + + /** + * 查询上传记录 + * + * @param userId 用户编号,不使用用户编号作为条件时设置值小于等于0即可 + * @param fileId 文件编号,不使用文件编号作为条件时设置值小于等于0即可 + * @param categoryId 分类编号,不用分类编号作为条件时设置值小于等于0即可 + * @param fileName 文件名,不使用文件名作为条件时设置值为空即可 + * @param offset 偏移 + * + * @return 上传记录 + */ + @SelectProvider(type = UploadedSqlProvider.class, method = "getDownloadBy") + List listUploadedBy(@Param("userId") int userId, @Param("fileId") long fileId, @Param("fileName") + String fileName, @Param("categoryId") int categoryId, @Param("offset") int offset); +} diff --git a/src/main/java/com/mesasoft/cn/dao/UserDAO.java b/src/main/java/com/mesasoft/cn/dao/UserDAO.java new file mode 100644 index 0000000..d2993c3 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/dao/UserDAO.java @@ -0,0 +1,167 @@ +package com.mesasoft.cn.dao; + +import com.mesasoft.cn.dao.sqlprovider.UserSqlProvider; +import com.mesasoft.cn.entity.User; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + * @author pantao + * @since 2018/1/12 + */ +@Repository +public interface UserDAO { + + /** + * 更新用户权限 + * + * @param id 用户编号 + * @param permission 权限 + * + * @return 是否更新成功 + */ + @Update("update user set permission=#{permission} where id=#{id}") + boolean updatePermission(@Param("id") int id, @Param("permission") int permission); + + /** + * 用过用户名获取用户Id + * + * @param usernameOrEmail 用户名或邮箱 + * + * @return 用户编号 + */ + @Select("select id from user where username=#{usernameOrEmail} or email=#{usernameOrEmail}") + int getUserId(String usernameOrEmail); + + /** + * 通过ID更新用户基本信息 + * + * @param id 编号 + * @param avatar 头像 + * @param realName 真实姓名 + * @param email 邮箱 + * + * @return 是否更新成功 + */ + @Update("update user set avatar=#{avatar},real_name=#{realName},email=#{email} where id=#{id}") + boolean updateBasicInfo(@Param("id") int id, @Param("avatar") String avatar, @Param("realName") String realName, + @Param("email") String email); + + /** + * 通过id获取一个用户 + * + * @param id 编号 + * + * @return {@link User} + */ + @Select("select * from user where id=#{id}") + User getUserById(int id); + + /** + * 通过权限获取用户 + * + * @param permission 权限 + * @param condition 条件 + * @param offset 偏移 + * + * @return {@link List} + */ + @SelectProvider(type = UserSqlProvider.class, method = "getUserBy") + List listUserBy(@Param("permission") int permission, @Param("condition") String condition, + @Param("offset") int offset); + + /** + * 用户登录 + * + * @param usernameOrEmail 用户名 + * @param password 密码 + * + * @return {@link User} + */ + @Select("select * from user where (username=#{usernameOrEmail} or email=#{usernameOrEmail}) and password=sha2" + + "(#{password},256)") + User login(@Param("usernameOrEmail") String usernameOrEmail, @Param("password") String password); + + /** + * 添加一个用户 + * + * @param user {@link User} + * + * @return 是否插入成功 + */ + @Insert("insert into user(username,real_name,email,password,is_downloadable,is_uploadable,is_deletable," + + "is_updatable,is_visible) values(#{username},#{realName},#{email},sha2(#{password},256)," + + "#{isDownloadable},#{isUploadable},#{isDeletable},#{isUpdatable},#{isVisible})") + boolean insertUser(User user); + + /** + * 通过id更新用户登录时间 + * + * @param id 编号 + * + * @return {@link Boolean} + */ + @Update("update user set last_login_time=current_timestamp where id=#{id}") + boolean updateUserLoginTime(int id); + + /** + * 更新操作用户权限 + * + * @param id 用户编号 + * @param isDownloadable 下载权限 + * @param isUploadable 上传权限 + * @param isVisible 可查权限 + * @param isDeletable 删除权限 + * @param isUpdatable 更新权限 + * + * @return {@link Boolean} + */ + @UpdateProvider(type = UserSqlProvider.class, method = "updateAuthById") + boolean updateAuthById(@Param("id") int id, @Param("isDownloadable") int isDownloadable, + @Param("isUploadable") int isUploadable, @Param("isDeletable") int isDeletable, @Param( + "isUpdatable") int isUpdatable, @Param("isVisible") int isVisible); + + /** + * 通过编号哦更新密码 + * + * @param id 编号 + * @param password 密码 + * + * @return {@link Boolean} + */ + @Update("update user set password=sha2(#{password},256) where id=#{id}") + boolean updatePasswordById(@Param("id") int id, @Param("password") String password); + + /** + * 通过邮箱更新密码 + * + * @param password 密码 + * @param email 邮箱 + * + * @return {@link Boolean} + */ + @Update("update user set password=sha2(#{password},256) where email=#{email}") + boolean updatePasswordByEmail(@Param("password") String password, @Param("email") String email); + + /** + * 检查用户名 + * + * @param username 用户名 + * + * @return {@link Integer} + */ + @Select("select count(*) from user where username=#{username}") + int checkUsername(String username); + + /** + * 检查邮箱 + * + * @param email 邮箱 + * + * @return {@link Integer} + */ + @Select("select count(*) from user where email=#{email}") + int checkEmail(String email); +} diff --git a/src/main/java/com/mesasoft/cn/dao/sqlprovider/AuthSqlProvider.java b/src/main/java/com/mesasoft/cn/dao/sqlprovider/AuthSqlProvider.java new file mode 100644 index 0000000..3c9c10f --- /dev/null +++ b/src/main/java/com/mesasoft/cn/dao/sqlprovider/AuthSqlProvider.java @@ -0,0 +1,48 @@ +package com.mesasoft.cn.dao.sqlprovider; + +import com.mesasoft.cn.SketchApplication; +import com.mesasoft.cn.modules.constant.ConfigConsts; +import com.zhazhapan.util.Checker; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.jdbc.SQL; + +/** + * @author pantao + * @since 2018/1/19 + */ +public class AuthSqlProvider { + + public String updateAuthById() { + return CommonSqlProvider.updateAuthById("auth"); + } + + public String batchDelete(@Param("ids") String ids) { + return "delete from auth where id in " + (ids.startsWith("(") ? "" : "(") + ids + (ids.endsWith(")") ? "" : + ")"); + } + + public String getAuthBy(@Param("id") long id, @Param("userId") int userId, @Param("fileId") long fileId, @Param + ("fileName") String fileName, @Param("offset") int offset) { + String sql = new SQL() {{ + SELECT("a.id,a.user_id,a.file_id,u.username,f.name file_name,f.local_url,a.is_downloadable,a" + "" + "" + + ".is_uploadable,a.is_deletable,a.is_updatable,a.is_visible,a.create_time"); + FROM("auth a"); + JOIN("user u on u.id=a.user_id"); + JOIN("file f on f.id=a.file_id"); + if (id > 0) { + WHERE("a.id=#{id}"); + } + if (userId > 0) { + WHERE("u.id=#{userId}"); + } + if (fileId > 0) { + WHERE("f.id=#{fileId}"); + } else if (Checker.isNotEmpty(fileName)) { + WHERE("f.local_url like '%" + fileName + "%'"); + } + ORDER_BY("a." + SketchApplication.settings.getStringUseEval(ConfigConsts.AUTH_ORDER_BY_OF_SETTINGS)); + }}.toString(); + int size = SketchApplication.settings.getIntegerUseEval(ConfigConsts.AUTH_PAGE_SIZE_OF_SETTINGS); + return sql + " limit " + (offset * size) + "," + size; + } +} diff --git a/src/main/java/com/mesasoft/cn/dao/sqlprovider/CommonSqlProvider.java b/src/main/java/com/mesasoft/cn/dao/sqlprovider/CommonSqlProvider.java new file mode 100644 index 0000000..ad2d024 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/dao/sqlprovider/CommonSqlProvider.java @@ -0,0 +1,24 @@ +package com.mesasoft.cn.dao.sqlprovider; + +import org.apache.ibatis.jdbc.SQL; + +/** + * @author pantao + * @since 2018/1/19 + */ +public class CommonSqlProvider { + + private CommonSqlProvider() {} + + public static String updateAuthById(String table) { + return new SQL() {{ + UPDATE(table); + SET("is_downloadable=#{isDownloadable}"); + SET("is_uploadable=#{isUploadable}"); + SET("is_deletable=#{isDeletable}"); + SET("is_updatable=#{isUpdatable}"); + SET("is_visible=#{isVisible}"); + WHERE("id=#{id}"); + }}.toString(); + } +} diff --git a/src/main/java/com/mesasoft/cn/dao/sqlprovider/DownloadedSqlProvider.java b/src/main/java/com/mesasoft/cn/dao/sqlprovider/DownloadedSqlProvider.java new file mode 100644 index 0000000..5c1745f --- /dev/null +++ b/src/main/java/com/mesasoft/cn/dao/sqlprovider/DownloadedSqlProvider.java @@ -0,0 +1,48 @@ +package com.mesasoft.cn.dao.sqlprovider; + +import com.mesasoft.cn.SketchApplication; +import com.mesasoft.cn.modules.constant.ConfigConsts; +import com.zhazhapan.util.Checker; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.jdbc.SQL; + +/** + * @author pantao + * @since 2018/1/19 + */ +public class DownloadedSqlProvider { + + /** + * 生成一条下载记录表的查询语句 + * + * @param userId 用户编号 + * @param fileId 文件编号 + * + * @return SQL语句 + */ + public String getDownloadBy(@Param("userId") int userId, @Param("fileId") long fileId, @Param("fileName") String + fileName, @Param("categoryId") int categoryId, @Param("offset") int offset) { + String sql = new SQL() {{ + SELECT("d.id,d.user_id,d.file_id,u.username,u.email,f.name file_name,c.name category_name,f.visit_url,d" + + ".create_time"); + FROM("download d"); + JOIN("user u on d.user_id=u.id"); + JOIN("file f on d.file_id=f.id"); + JOIN("category c on f.category_id=c.id"); + if (userId > 0) { + WHERE("d.user_id=#{userId}"); + } + if (fileId > 0) { + WHERE("d.file_id=#{fileId}"); + } else if (Checker.isNotEmpty(fileName)) { + WHERE("f.local_url like '%" + fileName + "%'"); + } + if (categoryId > 0) { + WHERE("c.id=#{categoryId}"); + } + ORDER_BY("d." + SketchApplication.settings.getStringUseEval(ConfigConsts.DOWNLOAD_ORDER_BY_OF_SETTINGS)); + }}.toString(); + int size = SketchApplication.settings.getIntegerUseEval(ConfigConsts.DOWNLOAD_PAGE_SIZE_OF_SETTINGS); + return sql + " limit " + (offset * size) + "," + size; + } +} diff --git a/src/main/java/com/mesasoft/cn/dao/sqlprovider/FileSqlProvider.java b/src/main/java/com/mesasoft/cn/dao/sqlprovider/FileSqlProvider.java new file mode 100644 index 0000000..8a2ecdc --- /dev/null +++ b/src/main/java/com/mesasoft/cn/dao/sqlprovider/FileSqlProvider.java @@ -0,0 +1,110 @@ +package com.mesasoft.cn.dao.sqlprovider; + +import com.mesasoft.cn.SketchApplication; +import com.mesasoft.cn.modules.constant.ConfigConsts; +import com.zhazhapan.modules.constant.ValueConsts; +import com.zhazhapan.util.Checker; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.jdbc.SQL; +import org.springframework.stereotype.Component; + +/** + * @author pantao + * @since 2018/1/19 + */ +@Component +public class FileSqlProvider { + + public String updateAuthById() { + return CommonSqlProvider.updateAuthById("file"); + } + + /** + * 生成一条文件基本信息的查询语句 + * + * @param userId 用户编号 + * @param fileId 文件编号 + * + * @return SQL语句 + */ + public String getBasicBy(@Param("userId") int userId, @Param("fileId") long fileId, @Param("fileName") String + fileName, @Param("categoryId") int categoryId, @Param("offset") int offset) { + String sql = new SQL() {{ + SELECT("f.id,u.username,f.local_url,c.name category_name,f.visit_url,f.download_times," + "f" + "" + "" + + ".create_time"); + FROM("file f"); + JOIN("user u on f.user_id=u.id"); + JOIN("category c on f.category_id=c.id"); + if (userId > 0) { + WHERE("f.user_id=#{userId}"); + } + if (fileId > 0) { + WHERE("f.id=#{fileId}"); + } else if (Checker.isNotEmpty(fileName)) { + WHERE("f.local_url like '%" + fileName + "%'"); + } + if (categoryId > 0) { + WHERE("c.id=#{categoryId}"); + } + ORDER_BY("f." + SketchApplication.settings.getStringUseEval(ConfigConsts.FILE_ORDER_BY_OF_SETTING)); + }}.toString(); + int size = SketchApplication.settings.getIntegerUseEval(ConfigConsts.FILE_PAGE_SIZE_OF_SETTING); + return sql + " limit " + (offset * size) + "," + size; + } + + private String getSqlEnds(int offset, String orderBy, String search) { + int size = SketchApplication.settings.getIntegerUseEval(ConfigConsts.FILE_PAGE_SIZE_OF_SETTING); + return getSearch(search) + " order by " + (Checker.isEmpty(orderBy) ? SketchApplication.settings + .getStringUseEval(ConfigConsts.FILE_ORDER_BY_OF_SETTING) : orderBy) + " limit " + offset * size + + "," + size; + } + + public String getAll(@Param("offset") int offset, @Param("categoryId") int categoryId, @Param("orderBy") String + orderBy, @Param("search") String search) { + return getBaseSql(ValueConsts.FALSE) + " where f.is_visible=1" + (categoryId < 1 ? "" : " and " + + "category_id=#{categoryId}") + " and ((select a.is_visible from auth a where a.file_id=f.id and a" + + ".user_id=#{userId}) is null or (a.user_id=#{userId} and a.is_visible=1))" + getSqlEnds(offset, + orderBy, search); + } + + public String getUserUploaded(@Param("offset") int offset, @Param("search") String search) { + return getBaseSql(ValueConsts.FALSE) + " where f.is_visible=1 and (f.user_id=#{userId} or a.is_updatable=1 or" + + " a.is_deletable=1)" + getSqlEnds(offset, + ValueConsts.EMPTY_STRING, search); + } + + public String getUserDownloaded(@Param("offset") int offset, @Param("search") String search) { + return getBaseSql(ValueConsts.TRUE) + " where d.user_id=#{userId}" + getSqlEnds(offset, ValueConsts + .EMPTY_STRING, search); + } + + private String getSearch(String search) { + if (Checker.isEmpty(search)) { + return ValueConsts.EMPTY_STRING; + } else { + search = "'%" + search + "%'"; + return " and (f.name like " + search + " or f.visit_url like " + search + " or f.description like " + + search + " or f.tag like " + search + ")"; + } + } + + private String getBaseSql(boolean isDownloaded) { + return new SQL() {{ + SELECT("distinct f.id,f.user_id,u.username,u.avatar,f.name file_name,f.size,f.create_time,c.name " + + "category_name,f" + + ".description,f.tag,f.check_times,f.download_times,f.visit_url,f.is_uploadable,f.is_deletable," + + "f.is_updatable,f.is_downloadable,f.is_visible"); + if (isDownloaded) { + SELECT("d.create_time download_time"); + } + FROM("file f"); + JOIN("user u on u.id=f.user_id"); + JOIN("category c on c.id=f.category_id"); + if (isDownloaded) { + JOIN("download d on d.file_id=f.id"); + } else { + JOIN("auth a on a.file_id=f.id"); + } + }}.toString(); + } +} diff --git a/src/main/java/com/mesasoft/cn/dao/sqlprovider/UploadedSqlProvider.java b/src/main/java/com/mesasoft/cn/dao/sqlprovider/UploadedSqlProvider.java new file mode 100644 index 0000000..720b716 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/dao/sqlprovider/UploadedSqlProvider.java @@ -0,0 +1,47 @@ +package com.mesasoft.cn.dao.sqlprovider; + +import com.mesasoft.cn.SketchApplication; +import com.mesasoft.cn.modules.constant.ConfigConsts; +import com.zhazhapan.util.Checker; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.jdbc.SQL; + +/** + * @author pantao + * @since 2018/2/28 + */ +public class UploadedSqlProvider { + + /** + * 生成一条上传记录表的查询语句 + * + * @param userId 用户编号 + * @param fileId 文件编号 + * + * @return SQL语句 + */ + public String getDownloadBy(@Param("userId") int userId, @Param("fileId") long fileId, @Param("fileName") String + fileName, @Param("categoryId") int categoryId, @Param("offset") int offset) { + String sql = new SQL() {{ + SELECT("f.id,f.user_id,u.username,u.email,f.name file_name,c.name category_name,f.local_url,f.visit_url," + + "" + "" + "" + "f" + ".create_time"); + FROM("file f"); + JOIN("user u on f.user_id=u.id"); + JOIN("category c on f.category_id=c.id"); + if (userId > 0) { + WHERE("f.user_id=#{userId}"); + } + if (fileId > 0) { + WHERE("f.id=#{fileId}"); + } else if (Checker.isNotEmpty(fileName)) { + WHERE("f.local_url like '%" + fileName + "%'"); + } + if (categoryId > 0) { + WHERE("c.id=#{categoryId}"); + } + ORDER_BY("f." + SketchApplication.settings.getStringUseEval(ConfigConsts.FILE_ORDER_BY_OF_SETTING)); + }}.toString(); + int size = SketchApplication.settings.getIntegerUseEval(ConfigConsts.FILE_PAGE_SIZE_OF_SETTING); + return sql + " limit " + (offset * size) + "," + size; + } +} diff --git a/src/main/java/com/mesasoft/cn/dao/sqlprovider/UserSqlProvider.java b/src/main/java/com/mesasoft/cn/dao/sqlprovider/UserSqlProvider.java new file mode 100644 index 0000000..5d36845 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/dao/sqlprovider/UserSqlProvider.java @@ -0,0 +1,41 @@ +package com.mesasoft.cn.dao.sqlprovider; + +import com.mesasoft.cn.SketchApplication; +import com.mesasoft.cn.modules.constant.ConfigConsts; +import com.mesasoft.cn.modules.constant.DefaultValues; +import com.zhazhapan.util.Checker; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.jdbc.SQL; + +/** + * @author pantao + * @since 2018/1/19 + */ +public class UserSqlProvider { + + public String updateAuthById() { + return CommonSqlProvider.updateAuthById("user"); + } + + public String getUserBy(@Param("permission") int permission, @Param("condition") String condition, @Param + ("offset") int offset) { + String sql = new SQL() {{ + SELECT("*"); + FROM("user"); + if (permission == DefaultValues.THREE_INT) { + WHERE("permission<3"); + } else if (permission == DefaultValues.TWO_INT) { + WHERE("permission<2"); + } else { + WHERE("permission<0"); + } + if (Checker.isNotEmpty(condition)) { + WHERE("username like '%" + condition + "%' or email like '%" + condition + "%' or real_name like '" + + condition + "'"); + } + ORDER_BY(SketchApplication.settings.getStringUseEval(ConfigConsts.USER_ORDER_BY_OF_SETTINGS)); + }}.toString(); + int size = SketchApplication.settings.getIntegerUseEval(ConfigConsts.USER_PAGE_SIZE_OF_SETTINGS); + return sql + " limit " + (offset * size) + "," + size; + } +} diff --git a/src/main/java/com/mesasoft/cn/entity/Auth.java b/src/main/java/com/mesasoft/cn/entity/Auth.java new file mode 100644 index 0000000..5249ad0 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/entity/Auth.java @@ -0,0 +1,137 @@ +package com.mesasoft.cn.entity; + +import com.mesasoft.cn.util.BeanUtils; + +import java.sql.Timestamp; + +/** + * @author pantao + * @since 2018/1/18 + */ +public class Auth { + + private long id; + + private int isUploadable; + + private int isDeletable; + + private int isUpdatable; + + private int isDownloadable; + + private int isVisible; + + private int userId; + + private long fileId; + + private Timestamp createTime; + + public Auth(int userId, long fileId) { + this.userId = userId; + this.fileId = fileId; + } + + public Auth(long id, int isUploadable, int isDeletable, int isUpdatable, int userId, long fileId, int isVisible, + int isDownloadable, Timestamp createTime) { + this.id = id; + this.isUploadable = isUploadable; + this.isDeletable = isDeletable; + this.isUpdatable = isUpdatable; + this.isDownloadable = isDownloadable; + this.isVisible = isVisible; + this.userId = userId; + this.fileId = fileId; + this.createTime = createTime; + } + + public void setAuth(int isDownloadable, int isUploadable, int isDeletable, int isUpdatable, int isVisible) { + this.isUploadable = isUploadable; + this.isDeletable = isDeletable; + this.isUpdatable = isUpdatable; + this.isDownloadable = isDownloadable; + this.isVisible = isVisible; + } + + public void setAuth(int[] auth) { + setAuth(auth[0], auth[1], auth[2], auth[3], auth[4]); + } + + @Override + public String toString() { + return BeanUtils.toPrettyJson(this); + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public int getIsUploadable() { + return isUploadable; + } + + public void setIsUploadable(int isUploadable) { + this.isUploadable = isUploadable; + } + + public int getIsDeletable() { + return isDeletable; + } + + public void setIsDeletable(int isDeletable) { + this.isDeletable = isDeletable; + } + + public int getIsUpdatable() { + return isUpdatable; + } + + public void setIsUpdatable(int isUpdatable) { + this.isUpdatable = isUpdatable; + } + + public int getIsDownloadable() { + return isDownloadable; + } + + public void setIsDownloadable(int isDownloadable) { + this.isDownloadable = isDownloadable; + } + + public int getIsVisible() { + return isVisible; + } + + public void setIsVisible(int isVisible) { + this.isVisible = isVisible; + } + + public int getUserId() { + return userId; + } + + public void setUserId(int userId) { + this.userId = userId; + } + + public long getFileId() { + return fileId; + } + + public void setFileId(long fileId) { + this.fileId = fileId; + } + + public Timestamp getCreateTime() { + return createTime; + } + + public void setCreateTime(Timestamp createTime) { + this.createTime = createTime; + } +} diff --git a/src/main/java/com/mesasoft/cn/entity/Category.java b/src/main/java/com/mesasoft/cn/entity/Category.java new file mode 100644 index 0000000..3ea1b55 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/entity/Category.java @@ -0,0 +1,63 @@ +package com.mesasoft.cn.entity; + +import com.mesasoft.cn.util.BeanUtils; + +import java.sql.Timestamp; + +/** + * 分类表 + * + * @author pantao + * @since 2018/1/11 + */ +public class Category { + + private int id; + + /** + * 分类名称 + */ + private String name; + + private Timestamp createTime; + + public Category(String name) { + this.name = name; + } + + public Category(int id, String name, Timestamp createTime) { + this.id = id; + this.name = name; + this.createTime = createTime; + } + + @Override + public String toString() { + return BeanUtils.toPrettyJson(this); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Timestamp getCreateTime() { + return createTime; + } + + public void setCreateTime(Timestamp createTime) { + this.createTime = createTime; + } + + public int getId() { + + return id; + } + + public void setId(int id) { + this.id = id; + } +} diff --git a/src/main/java/com/mesasoft/cn/entity/Download.java b/src/main/java/com/mesasoft/cn/entity/Download.java new file mode 100644 index 0000000..6e13fa9 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/entity/Download.java @@ -0,0 +1,71 @@ +package com.mesasoft.cn.entity; + +import com.mesasoft.cn.util.BeanUtils; + +import java.sql.Timestamp; + +/** + * 下载记录表 + * + * @author pantao + * @since 2018/1/11 + */ +public class Download { + + private long id; + + private Timestamp createTime; + + private int userId; + + private int fileId; + + public Download(int userId, int fileId) { + this.userId = userId; + this.fileId = fileId; + } + + public Download(int id, Timestamp createTime, int userId, int fileId) { + this.id = id; + this.createTime = createTime; + this.userId = userId; + this.fileId = fileId; + } + + @Override + public String toString() { + return BeanUtils.toPrettyJson(this); + } + + public long getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public Timestamp getCreateTime() { + return createTime; + } + + public void setCreateTime(Timestamp createTime) { + this.createTime = createTime; + } + + public int getUserId() { + return userId; + } + + public void setUserId(int userId) { + this.userId = userId; + } + + public int getFileId() { + return fileId; + } + + public void setFileId(int fileId) { + this.fileId = fileId; + } +} diff --git a/src/main/java/com/mesasoft/cn/entity/File.java b/src/main/java/com/mesasoft/cn/entity/File.java new file mode 100644 index 0000000..346373e --- /dev/null +++ b/src/main/java/com/mesasoft/cn/entity/File.java @@ -0,0 +1,255 @@ +package com.mesasoft.cn.entity; + +import com.mesasoft.cn.util.BeanUtils; + +import java.sql.Timestamp; + +/** + * 文件表 + * + * @author pantao + * @since 2018/1/11 + */ +public class File { + + private long id; + + private String name; + + private String suffix; + + private String localUrl; + + private String visitUrl; + + private long size; + + private Timestamp createTime; + + private String description; + + private int checkTimes; + + private int downloadTimes; + + private String tag; + + private int userId; + + private int categoryId; + + private int isUploadable; + + private int isDeletable; + + private int isUpdatable; + + private int isDownloadable; + + private int isVisible; + + private Timestamp lastModifyTime; + + public File(String name, String suffix, String localUrl, String visitUrl, String description, String tag, int + userId, int categoryId) { + this.name = name; + this.suffix = suffix; + this.categoryId = categoryId; + this.description = description; + this.localUrl = localUrl; + this.visitUrl = visitUrl; + this.tag = tag; + this.userId = userId; + this.size = new java.io.File(localUrl).length(); + } + + public File(long id, String name, String suffix, String localUrl, String visitUrl, long size, Timestamp + createTime, String description, int checkTimes, int downloadTimes, String tag, int userId, int + categoryId, int isDownloadable, int isUploadable, int isVisible, int isDeletable, int isUpdatable, + Timestamp lastModifyTime) { + this.id = id; + this.name = name; + this.suffix = suffix; + this.localUrl = localUrl; + this.visitUrl = visitUrl; + this.size = size; + this.createTime = createTime; + this.description = description; + this.checkTimes = checkTimes; + this.downloadTimes = downloadTimes; + this.tag = tag; + this.userId = userId; + this.categoryId = categoryId; + this.isUploadable = isUploadable; + this.isDeletable = isDeletable; + this.isUpdatable = isUpdatable; + this.isDownloadable = isDownloadable; + this.isVisible = isVisible; + this.lastModifyTime = lastModifyTime; + } + + public void setAuth(int isDownloadable, int isUploadable, int isDeletable, int isUpdatable, int isVisible) { + this.isUploadable = isUploadable; + this.isDeletable = isDeletable; + this.isUpdatable = isUpdatable; + this.isDownloadable = isDownloadable; + this.isVisible = isVisible; + } + + public Timestamp getLastModifyTime() { + return lastModifyTime; + } + + public void setLastModifyTime(Timestamp lastModifyTime) { + this.lastModifyTime = lastModifyTime; + } + + @Override + public String toString() { + return BeanUtils.toPrettyJson(this); + } + + public int getIsUploadable() { + return isUploadable; + } + + public void setIsUploadable(int isUploadable) { + this.isUploadable = isUploadable; + } + + public int getIsDeletable() { + return isDeletable; + } + + public void setIsDeletable(int isDeletable) { + this.isDeletable = isDeletable; + } + + public int getIsUpdatable() { + return isUpdatable; + } + + public void setIsUpdatable(int isUpdatable) { + this.isUpdatable = isUpdatable; + } + + public int getIsDownloadable() { + return isDownloadable; + } + + public void setIsDownloadable(int isDownloadable) { + this.isDownloadable = isDownloadable; + } + + public int getIsVisible() { + return isVisible; + } + + public void setIsVisible(int isVisible) { + this.isVisible = isVisible; + } + + public long getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSuffix() { + return suffix; + } + + public void setSuffix(String suffix) { + this.suffix = suffix; + } + + public String getLocalUrl() { + return localUrl; + } + + public void setLocalUrl(String localUrl) { + this.localUrl = localUrl; + } + + public String getVisitUrl() { + return visitUrl; + } + + public void setVisitUrl(String visitUrl) { + this.visitUrl = visitUrl; + } + + public long getSize() { + return size; + } + + public void setSize(long size) { + this.size = size; + } + + public Timestamp getCreateTime() { + return createTime; + } + + public void setCreateTime(Timestamp createTime) { + this.createTime = createTime; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public int getCheckTimes() { + return checkTimes; + } + + public void setCheckTimes(int checkTimes) { + this.checkTimes = checkTimes; + } + + public int getDownloadTimes() { + return downloadTimes; + } + + public void setDownloadTimes(int downloadTimes) { + this.downloadTimes = downloadTimes; + } + + public String getTag() { + return tag; + } + + public void setTag(String tag) { + this.tag = tag; + } + + public int getUserId() { + return userId; + } + + public void setUserId(int userId) { + this.userId = userId; + } + + public int getCategoryId() { + return categoryId; + } + + public void setCategoryId(int categoryId) { + this.categoryId = categoryId; + } +} diff --git a/src/main/java/com/mesasoft/cn/entity/Result.java b/src/main/java/com/mesasoft/cn/entity/Result.java new file mode 100644 index 0000000..0cfcc22 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/entity/Result.java @@ -0,0 +1,46 @@ +package com.mesasoft.cn.entity; + +import com.mesasoft.cn.enums.StatusEnum; + + +/** + * @author zhq + */ +public class Result { + + public static ResultEntity success() { + return new ResultEntity(StatusEnum.SUCCESS.getStatus(), StatusEnum.SUCCESS.getCode(), StatusEnum.SUCCESS.getMessage(), null); + } + + public static ResultEntity success(T data) { + return new ResultEntity(StatusEnum.SUCCESS.getStatus(), StatusEnum.SUCCESS.getCode(), StatusEnum.SUCCESS.getMessage(), data); + } + + public static ResultEntity fail() { + return new ResultEntity(StatusEnum.FAIL.getStatus(), StatusEnum.FAIL.getCode(), StatusEnum.FAIL.getMessage(), null); + } + + public static ResultEntity fail(String message) { + return new ResultEntity(StatusEnum.FAIL.getStatus(), StatusEnum.FAIL.getCode(), message, null); + } + + /** + * @param code + * @param message + * @return ResultEntity + */ + public static ResultEntity fail(String code, String message) { + return new ResultEntity(StatusEnum.FAIL.getStatus(), code, message, null); + } + + /** + * + * @param status + * @param code + * @param message + * @return ResultEntity + */ + public static ResultEntity fail(int status, String code, String message) { + return new ResultEntity(status, code, message, null); + } +} diff --git a/src/main/java/com/mesasoft/cn/entity/ResultEntity.java b/src/main/java/com/mesasoft/cn/entity/ResultEntity.java new file mode 100644 index 0000000..c73e206 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/entity/ResultEntity.java @@ -0,0 +1,20 @@ +package com.mesasoft.cn.entity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ResultEntity implements Serializable { + + private int status; + private String code; + private String message; + private T data; +} \ No newline at end of file diff --git a/src/main/java/com/mesasoft/cn/entity/User.java b/src/main/java/com/mesasoft/cn/entity/User.java new file mode 100644 index 0000000..4c91ef2 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/entity/User.java @@ -0,0 +1,196 @@ +package com.mesasoft.cn.entity; + +import com.mesasoft.cn.util.BeanUtils; + +import java.sql.Timestamp; + +/** + * 用户表 + * + * @author pantao + * @since 2018/1/11 + */ +public class User { + + private int id; + + private String username; + + private String realName; + + private String email; + + private String password; + + private int isUploadable; + + private int isDeletable; + + private int isUpdatable; + + private int isDownloadable; + + private int isVisible; + + /** + * 权限级别:0(禁止登录),1(正常,普通用户),2(正常,管理员),3(正常,超级管理员) + */ + private int permission; + + private Timestamp createTime; + + private Timestamp lastLoginTime; + + private String avatar; + + public User(String username, String realName, String email, String password) { + this.username = username; + this.realName = realName; + this.email = email; + this.password = password; + } + + public User(int id, String username, String realName, String email, String password, int permission, Timestamp + createTime, Timestamp lastLoginTime, int isDownloadable, int isUploadable, int isVisible, int + isDeletable, int isUpdatable, String avatar) { + this.id = id; + this.username = username; + this.realName = realName; + this.email = email; + this.password = password; + this.isUploadable = isUploadable; + this.isDeletable = isDeletable; + this.isUpdatable = isUpdatable; + this.isDownloadable = isDownloadable; + this.isVisible = isVisible; + this.permission = permission; + this.createTime = createTime; + this.lastLoginTime = lastLoginTime; + this.avatar = avatar; + } + + public void setAuth(int isDownloadable, int isUploadable, int isDeletable, int isUpdatable, int isVisible) { + this.isUploadable = isUploadable; + this.isDeletable = isDeletable; + this.isUpdatable = isUpdatable; + this.isDownloadable = isDownloadable; + this.isVisible = isVisible; + } + + @Override + public String toString() { + return BeanUtils.toPrettyJson(this); + } + + public int getIsUploadable() { + return isUploadable; + } + + public void setIsUploadable(int isUploadable) { + this.isUploadable = isUploadable; + } + + public int getIsDeletable() { + return isDeletable; + } + + public void setIsDeletable(int isDeletable) { + this.isDeletable = isDeletable; + } + + public int getIsUpdatable() { + return isUpdatable; + } + + public void setIsUpdatable(int isUpdatable) { + this.isUpdatable = isUpdatable; + } + + public int getIsDownloadable() { + return isDownloadable; + } + + public void setIsDownloadable(int isDownloadable) { + this.isDownloadable = isDownloadable; + } + + public int getIsVisible() { + return isVisible; + } + + public void setIsVisible(int isVisible) { + this.isVisible = isVisible; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getRealName() { + return realName; + } + + public void setRealName(String realName) { + this.realName = realName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public int getPermission() { + return permission; + } + + public void setPermission(int permission) { + this.permission = permission; + } + + public Timestamp getCreateTime() { + return createTime; + } + + public void setCreateTime(Timestamp createTime) { + this.createTime = createTime; + } + + public Timestamp getLastLoginTime() { + return lastLoginTime; + } + + public void setLastLoginTime(Timestamp lastLoginTime) { + this.lastLoginTime = lastLoginTime; + } + + public String getAvatar() { + return avatar; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } +} diff --git a/src/main/java/com/mesasoft/cn/enums/InterceptorLevel.java b/src/main/java/com/mesasoft/cn/enums/InterceptorLevel.java new file mode 100644 index 0000000..6a0d753 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/enums/InterceptorLevel.java @@ -0,0 +1,29 @@ +package com.mesasoft.cn.enums; + +/** + * 拦截级别 + * + * @author pantao + * @since 2018/1/25 + */ +public enum InterceptorLevel { + /** + * 不拦截 + */ + NONE, + + /** + * 用户级别拦截 + */ + USER, + + /** + * 管理员级别拦截 + */ + ADMIN, + + /** + * 系统用户 + */ + SYSTEM +} diff --git a/src/main/java/com/mesasoft/cn/enums/StatusEnum.java b/src/main/java/com/mesasoft/cn/enums/StatusEnum.java new file mode 100644 index 0000000..958fbc3 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/enums/StatusEnum.java @@ -0,0 +1,23 @@ +package com.mesasoft.cn.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @description: + * @author: zhq + * @create: 2022-03-21 + **/ +@Getter +@AllArgsConstructor +public enum StatusEnum { + + SUCCESS(200, "200", "success"), + FAIL(400, "400", "fail"), + NOT_FOUND(404, "404", "not found"), + SERROR_ERROR(500, "500", "not found"); + + private int status; + private String code; + private String message; +} diff --git a/src/main/java/com/mesasoft/cn/exception/BusinessException.java b/src/main/java/com/mesasoft/cn/exception/BusinessException.java new file mode 100644 index 0000000..90f70b1 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/exception/BusinessException.java @@ -0,0 +1,42 @@ +package com.mesasoft.cn.exception; + +import lombok.*; + +@EqualsAndHashCode(callSuper = true) +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BusinessException extends RuntimeException { + + /** + * 异常代码 + */ + @Builder.Default + private int status = 500; + + @Builder.Default + private String code = "500"; + + /** + * 异常信息 + */ + private String message; + + public BusinessException(String message) { + this.message = message; + } + + + public BusinessException(String message, Throwable e) { + super(message, e); + } + + public BusinessException(int status, String code, String message, Throwable e) { + super(message, e); + this.status = status; + this.code = code; + } + + +} diff --git a/src/main/java/com/mesasoft/cn/exception/GlobalExceptionHandler.java b/src/main/java/com/mesasoft/cn/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..7c21c36 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/exception/GlobalExceptionHandler.java @@ -0,0 +1,36 @@ +package com.mesasoft.cn.exception; + +import com.alibaba.fastjson.support.spring.FastJsonJsonView; +import com.zhazhapan.modules.constant.ValueConsts; +import com.zhazhapan.util.Checker; +import org.springframework.http.HttpStatus; +import org.springframework.web.servlet.HandlerExceptionResolver; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.HashMap; +import java.util.Map; + +/** + * @author pantao + * @since 2018/2/5 + */ +public class GlobalExceptionHandler implements HandlerExceptionResolver { + + @Override + public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, + Exception ex) { + ModelAndView mv = new ModelAndView(); + FastJsonJsonView view = new FastJsonJsonView(); + Map attributes = new HashMap<>(ValueConsts.TWO_INT); + attributes.put("code", "502"); + attributes.put("message", ex.getMessage()); + String queryString = request.getQueryString(); + attributes.put("url", request.getRequestURI() + (Checker.isEmpty(queryString) ? "" : "?" + queryString)); + view.setAttributesMap(attributes); + mv.setView(view); + mv.setStatus(HttpStatus.INTERNAL_SERVER_ERROR); + return mv; + } +} diff --git a/src/main/java/com/mesasoft/cn/interceptor/WebInterceptor.java b/src/main/java/com/mesasoft/cn/interceptor/WebInterceptor.java new file mode 100644 index 0000000..50e16f1 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/interceptor/WebInterceptor.java @@ -0,0 +1,79 @@ +package com.mesasoft.cn.interceptor; + +import com.mesasoft.cn.SketchApplication; +import com.mesasoft.cn.modules.constant.DefaultValues; +import com.mesasoft.cn.service.impl.UserServiceImpl; +import com.mesasoft.cn.annotation.AuthInterceptor; +import com.mesasoft.cn.entity.User; +import com.mesasoft.cn.enums.InterceptorLevel; +import com.zhazhapan.modules.constant.ValueConsts; +import com.zhazhapan.util.Checker; +import com.zhazhapan.util.HttpUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * @author pantao + * @since 2018/1/25 + */ +public class WebInterceptor implements HandlerInterceptor { + + @Autowired + UserServiceImpl userService; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws + Exception { + String url = request.getServletPath(); + InterceptorLevel level = InterceptorLevel.NONE; + if (handler instanceof HandlerMethod) { + AuthInterceptor interceptor = ((HandlerMethod) handler).getMethodAnnotation(AuthInterceptor.class); + //注解到类上面的注解,无法直接获取,只能通过扫描 + if (Checker.isNull(interceptor)) { + for (Class type : SketchApplication.controllers) { + RequestMapping mapping = type.getAnnotation(RequestMapping.class); + if (Checker.isNotNull(mapping)) { + for (String path : mapping.value()) { + if (url.startsWith(path)) { + interceptor = type.getAnnotation(AuthInterceptor.class); + break; + } + } + break; + } + } + } + if (Checker.isNotNull(interceptor)) { + level = interceptor.value(); + } + } + User user = (User) request.getSession().getAttribute(ValueConsts.USER_STRING); + if (Checker.isNull(user)) { + //读取token,自动登录 + Cookie cookie = HttpUtils.getCookie(ValueConsts.TOKEN_STRING, request.getCookies()); + if (Checker.isNotNull(cookie)) { + user = userService.login(ValueConsts.EMPTY_STRING, ValueConsts.EMPTY_STRING, cookie.getValue(), + response); + if (Checker.isNotNull(user)) { + request.getSession().setAttribute(ValueConsts.USER_STRING, user); + } + } + } + if (level != InterceptorLevel.NONE) { + boolean isRedirect = Checker.isNull(user) || (level == InterceptorLevel.ADMIN && user.getPermission() < + 2) || (level == InterceptorLevel.SYSTEM && user.getPermission() < 3); + if (isRedirect) { + response.setStatus(401); + response.sendRedirect(DefaultValues.SIGNIN_PAGE); + return false; + } + } + return true; + } +} diff --git a/src/main/java/com/mesasoft/cn/model/AuthRecord.java b/src/main/java/com/mesasoft/cn/model/AuthRecord.java new file mode 100644 index 0000000..c3e4e27 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/model/AuthRecord.java @@ -0,0 +1,153 @@ +package com.mesasoft.cn.model; + +import com.mesasoft.cn.util.BeanUtils; + +import java.sql.Timestamp; + +/** + * @author pantao + * @since 2018/1/19 + */ +public class AuthRecord { + + private long id; + + private int userId; + + private long fileId; + + private String username; + + private String fileName; + + private String localUrl; + + private int isDownloadable; + + private int isUploadable; + + private int isDeletable; + + private int isUpdatable; + + private int isVisible; + + private Timestamp createTime; + + public AuthRecord(long id, int userId, long fileId, String username, String fileName, String localUrl, int + isDownloadable, int isUploadable, int isDeletable, int isUpdatable, int isVisible, Timestamp createTime) { + this.id = id; + this.userId = userId; + this.fileId = fileId; + this.username = username; + this.fileName = fileName; + this.localUrl = localUrl; + this.isDownloadable = isDownloadable; + this.isUploadable = isUploadable; + this.isDeletable = isDeletable; + this.isUpdatable = isUpdatable; + this.isVisible = isVisible; + this.createTime = createTime; + } + + @Override + public String toString() { + return BeanUtils.toPrettyJson(this); + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public int getUserId() { + return userId; + } + + public void setUserId(int userId) { + this.userId = userId; + } + + public long getFileId() { + return fileId; + } + + public void setFileId(long fileId) { + this.fileId = fileId; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public String getLocalUrl() { + return localUrl; + } + + public void setLocalUrl(String localUrl) { + this.localUrl = localUrl; + } + + public int getIsDownloadable() { + return isDownloadable; + } + + public void setIsDownloadable(int isDownloadable) { + this.isDownloadable = isDownloadable; + } + + public int getIsUploadable() { + return isUploadable; + } + + public void setIsUploadable(int isUploadable) { + this.isUploadable = isUploadable; + } + + public int getIsDeletable() { + return isDeletable; + } + + public void setIsDeletable(int isDeletable) { + this.isDeletable = isDeletable; + } + + public int getIsUpdatable() { + return isUpdatable; + } + + public void setIsUpdatable(int isUpdatable) { + this.isUpdatable = isUpdatable; + } + + public int getIsVisible() { + return isVisible; + } + + public void setIsVisible(int isVisible) { + this.isVisible = isVisible; + } + + public Timestamp getCreateTime() { + return createTime; + } + + public void setCreateTime(Timestamp createTime) { + this.createTime = createTime; + } +} diff --git a/src/main/java/com/mesasoft/cn/model/BaseAuthRecord.java b/src/main/java/com/mesasoft/cn/model/BaseAuthRecord.java new file mode 100644 index 0000000..a791a7a --- /dev/null +++ b/src/main/java/com/mesasoft/cn/model/BaseAuthRecord.java @@ -0,0 +1,66 @@ +package com.mesasoft.cn.model; + +/** + * @author pantao + * @since 2018/3/6 + */ +public class BaseAuthRecord { + + private int isDownloadable; + + private int isUploadable; + + private int isDeletable; + + private int isUpdatable; + + private int isVisible; + + public BaseAuthRecord(int isDownloadable, int isUploadable, int isDeletable, int isUpdatable, int isVisible) { + this.isDownloadable = isDownloadable; + this.isUploadable = isUploadable; + this.isDeletable = isDeletable; + this.isUpdatable = isUpdatable; + this.isVisible = isVisible; + } + + public int getIsDownloadable() { + return isDownloadable; + } + + public void setIsDownloadable(int isDownloadable) { + this.isDownloadable = isDownloadable; + } + + public int getIsUploadable() { + return isUploadable; + } + + public void setIsUploadable(int isUploadable) { + this.isUploadable = isUploadable; + } + + public int getIsDeletable() { + return isDeletable; + } + + public void setIsDeletable(int isDeletable) { + this.isDeletable = isDeletable; + } + + public int getIsUpdatable() { + return isUpdatable; + } + + public void setIsUpdatable(int isUpdatable) { + this.isUpdatable = isUpdatable; + } + + public int getIsVisible() { + return isVisible; + } + + public void setIsVisible(int isVisible) { + this.isVisible = isVisible; + } +} diff --git a/src/main/java/com/mesasoft/cn/model/DownloadRecord.java b/src/main/java/com/mesasoft/cn/model/DownloadRecord.java new file mode 100644 index 0000000..c467965 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/model/DownloadRecord.java @@ -0,0 +1,120 @@ +package com.mesasoft.cn.model; + +import com.mesasoft.cn.util.BeanUtils; + +import java.sql.Timestamp; + +/** + * @author pantao + * @since 2018/1/19 + */ +public class DownloadRecord { + + private long id; + + private int userId; + + private long fileId; + + private String username; + + private String email; + + private String fileName; + + private String categoryName; + + private String visitUrl; + + private Timestamp createTime; + + public DownloadRecord(long id, int userId, long fileId, String username, String email, String fileName, String + categoryName, String visitUrl, Timestamp createTime) { + this.id = id; + this.userId = userId; + this.fileId = fileId; + this.username = username; + this.email = email; + this.fileName = fileName; + this.categoryName = categoryName; + this.visitUrl = visitUrl; + this.createTime = createTime; + } + + @Override + public String toString() { + return BeanUtils.toPrettyJson(this); + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public int getUserId() { + return userId; + } + + public void setUserId(int userId) { + this.userId = userId; + } + + public long getFileId() { + return fileId; + } + + public void setFileId(long fileId) { + this.fileId = fileId; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public String getCategoryName() { + return categoryName; + } + + public void setCategoryName(String categoryName) { + this.categoryName = categoryName; + } + + public String getVisitUrl() { + return visitUrl; + } + + public void setVisitUrl(String visitUrl) { + this.visitUrl = visitUrl; + } + + public Timestamp getCreateTime() { + return createTime; + } + + public void setCreateTime(Timestamp createTime) { + this.createTime = createTime; + } +} diff --git a/src/main/java/com/mesasoft/cn/model/FileBasicRecord.java b/src/main/java/com/mesasoft/cn/model/FileBasicRecord.java new file mode 100644 index 0000000..9d3b914 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/model/FileBasicRecord.java @@ -0,0 +1,91 @@ +package com.mesasoft.cn.model; + +import java.sql.Timestamp; + +/** + * @author pantao + * @since 2018/3/1 + */ +public class FileBasicRecord { + + private long id; + + private String username; + + private String localUrl; + + private String categoryName; + + private String visitUrl; + + private int downloadTimes; + + private Timestamp createTime; + + public FileBasicRecord(long id, String username, String localUrl, String categoryName, String visitUrl, int + downloadTimes, Timestamp createTime) { + this.id = id; + this.username = username; + this.localUrl = localUrl; + this.categoryName = categoryName; + this.visitUrl = visitUrl; + this.downloadTimes = downloadTimes; + this.createTime = createTime; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getLocalUrl() { + return localUrl; + } + + public void setLocalUrl(String localUrl) { + this.localUrl = localUrl; + } + + public String getCategoryName() { + return categoryName; + } + + public void setCategoryName(String categoryName) { + this.categoryName = categoryName; + } + + public String getVisitUrl() { + return visitUrl; + } + + public void setVisitUrl(String visitUrl) { + this.visitUrl = visitUrl; + } + + public int getDownloadTimes() { + return downloadTimes; + } + + public void setDownloadTimes(int downloadTimes) { + this.downloadTimes = downloadTimes; + } + + public Timestamp getCreateTime() { + return createTime; + } + + public void setCreateTime(Timestamp createTime) { + this.createTime = createTime; + } +} diff --git a/src/main/java/com/mesasoft/cn/model/FileRecord.java b/src/main/java/com/mesasoft/cn/model/FileRecord.java new file mode 100644 index 0000000..dc6b3fa --- /dev/null +++ b/src/main/java/com/mesasoft/cn/model/FileRecord.java @@ -0,0 +1,261 @@ +package com.mesasoft.cn.model; + +import com.mesasoft.cn.util.BeanUtils; + +import java.sql.Timestamp; + +/** + * @author pantao + * @since 2018/1/19 + */ +public class FileRecord { + + private long id; + + private int userId; + + private String username; + + private String avatar; + + private String fileName; + + private long size; + + private String categoryName; + + private String description; + + private String tag; + + private int checkTimes; + + private int downloadTimes; + + private String visitUrl; + + private int isUploadable; + + private int isDeletable; + + private int isUpdatable; + + private int isDownloadable; + + private int isVisible; + + private Timestamp createTime; + + private Timestamp downloadTime; + + public FileRecord(long id, int userId, String username, String avatar, String fileName, long size, Timestamp + createTime, String categoryName, String description, String tag, int checkTimes, int downloadTimes, + String visitUrl, int isUploadable, int isDeletable, int isUpdatable, int isDownloadable, int + isVisible, Timestamp downloadTime) { + this.id = id; + this.userId = userId; + this.username = username; + this.avatar = avatar; + this.fileName = fileName; + this.size = size; + this.createTime = createTime; + this.categoryName = categoryName; + this.description = description; + this.tag = tag; + this.checkTimes = checkTimes; + this.downloadTimes = downloadTimes; + this.visitUrl = visitUrl; + this.isUploadable = isUploadable; + this.isDeletable = isDeletable; + this.isUpdatable = isUpdatable; + this.isDownloadable = isDownloadable; + this.isVisible = isVisible; + this.downloadTime = downloadTime; + } + + public FileRecord(long id, int userId, String username, String avatar, String fileName, long size, Timestamp + createTime, String categoryName, String description, String tag, int checkTimes, int downloadTimes, + String visitUrl, int isUploadable, int isDeletable, int isUpdatable, int isDownloadable, int + isVisible) { + this.id = id; + this.userId = userId; + this.username = username; + this.avatar = avatar; + this.fileName = fileName; + this.size = size; + this.createTime = createTime; + this.categoryName = categoryName; + this.description = description; + this.tag = tag; + this.checkTimes = checkTimes; + this.downloadTimes = downloadTimes; + this.visitUrl = visitUrl; + this.isUploadable = isUploadable; + this.isDeletable = isDeletable; + this.isUpdatable = isUpdatable; + this.isDownloadable = isDownloadable; + this.isVisible = isVisible; + } + + @Override + public String toString() { + return BeanUtils.toPrettyJson(this); + } + + public Timestamp getDownloadTime() { + return downloadTime; + } + + public void setDownloadTime(Timestamp downloadTime) { + this.downloadTime = downloadTime; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public void setId(int id) { + this.id = id; + } + + public String getAvatar() { + return avatar; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public Timestamp getCreateTime() { + + return createTime; + } + + public void setCreateTime(Timestamp createTime) { + this.createTime = createTime; + } + + public int getUserId() { + return userId; + } + + public void setUserId(int userId) { + this.userId = userId; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public long getSize() { + return size; + } + + public void setSize(long size) { + this.size = size; + } + + public String getCategoryName() { + return categoryName; + } + + public void setCategoryName(String categoryName) { + this.categoryName = categoryName; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getTag() { + return tag; + } + + public void setTag(String tag) { + this.tag = tag; + } + + public int getCheckTimes() { + return checkTimes; + } + + public void setCheckTimes(int checkTimes) { + this.checkTimes = checkTimes; + } + + public int getDownloadTimes() { + return downloadTimes; + } + + public void setDownloadTimes(int downloadTimes) { + this.downloadTimes = downloadTimes; + } + + public String getVisitUrl() { + return visitUrl; + } + + public void setVisitUrl(String visitUrl) { + this.visitUrl = visitUrl; + } + + public int getIsUploadable() { + return isUploadable; + } + + public void setIsUploadable(int isUploadable) { + this.isUploadable = isUploadable; + } + + public int getIsDeletable() { + return isDeletable; + } + + public void setIsDeletable(int isDeletable) { + this.isDeletable = isDeletable; + } + + public int getIsUpdatable() { + return isUpdatable; + } + + public void setIsUpdatable(int isUpdatable) { + this.isUpdatable = isUpdatable; + } + + public int getIsDownloadable() { + return isDownloadable; + } + + public void setIsDownloadable(int isDownloadable) { + this.isDownloadable = isDownloadable; + } + + public int getIsVisible() { + return isVisible; + } + + public void setIsVisible(int isVisible) { + this.isVisible = isVisible; + } +} diff --git a/src/main/java/com/mesasoft/cn/model/UploadedRecord.java b/src/main/java/com/mesasoft/cn/model/UploadedRecord.java new file mode 100644 index 0000000..b9410da --- /dev/null +++ b/src/main/java/com/mesasoft/cn/model/UploadedRecord.java @@ -0,0 +1,113 @@ +package com.mesasoft.cn.model; + +import java.sql.Timestamp; + +/** + * @author pantao + * @since 2018/2/28 + */ +public class UploadedRecord { + + private long id; + + private int userId; + + private String username; + + private String email; + + private String fileName; + + private String categoryName; + + private String localUrl; + + private String visitUrl; + + private Timestamp createTime; + + public UploadedRecord(long id, int userId, String username, String email, String fileName, String categoryName, + String localUrl, String visitUrl, Timestamp createTime) { + this.id = id; + this.userId = userId; + this.username = username; + this.email = email; + this.fileName = fileName; + this.categoryName = categoryName; + this.localUrl = localUrl; + this.visitUrl = visitUrl; + this.createTime = createTime; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public int getUserId() { + return userId; + } + + public void setUserId(int userId) { + this.userId = userId; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public String getCategoryName() { + return categoryName; + } + + public void setCategoryName(String categoryName) { + this.categoryName = categoryName; + } + + public String getLocalUrl() { + return localUrl; + } + + public void setLocalUrl(String localUrl) { + this.localUrl = localUrl; + } + + public String getVisitUrl() { + return visitUrl; + } + + public void setVisitUrl(String visitUrl) { + this.visitUrl = visitUrl; + } + + public Timestamp getCreateTime() { + return createTime; + } + + public void setCreateTime(Timestamp createTime) { + this.createTime = createTime; + } +} diff --git a/src/main/java/com/mesasoft/cn/modules/constant/ConfigConsts.java b/src/main/java/com/mesasoft/cn/modules/constant/ConfigConsts.java new file mode 100644 index 0000000..15a563d --- /dev/null +++ b/src/main/java/com/mesasoft/cn/modules/constant/ConfigConsts.java @@ -0,0 +1,201 @@ +package com.mesasoft.cn.modules.constant; + +/** + * @author pantao + * @since 2018/1/12 + */ +public class ConfigConsts { + + /** + * 认证方式 + */ + public static final String AUTHENTICATION_OF_SETTING = "system.authentication"; + + /** + * 最大标签长度 + */ + public static final String TAG_SIZE_OF_SETTING = "file.tag.maxSize"; + + /** + * 每个标签最大长度 + */ + public static final String TAG_LENGTH_OF_SETTING = "file.tag.maxLength"; + + /** + * 文件标签 + */ + public static final String TAG_REQUIRE_OF_SETTING = "file.tag.require"; + + /** + * 文件默认排序方式 + */ + public static final String FILE_ORDER_BY_OF_SETTING = "file.orderBy"; + + /** + * 文件分页大小 + */ + public static final String FILE_PAGE_SIZE_OF_SETTING = "file.pageSize"; + + /** + * 匿名用户下载权限 + */ + public static final String ANONYMOUS_DOWNLOADABLE_OF_SETTING = "global.anonymousUser.downloadable"; + + /** + * 匿名用户访问权限 + */ + public static final String ANONYMOUS_VISIBLE_OF_SETTING = "global.anonymousUser.visible"; + + /** + * 文件后缀匹配 + */ + public static final String FILE_SUFFIX_MATCH_OF_SETTING = "file.suffixMatch.pattern"; + + /** + * 是否覆盖文件 + */ + public static final String FILE_COVER_OF_SETTING = "file.coverIfExists"; + + /** + * 自定义文件上传链接 + */ + public static final String CUSTOM_LINK_RULE_OF_SETTING = "file.linkRule.custom"; + + /** + * 最大上传大小路径 + */ + public static final String FILE_MAX_SIZE_OF_SETTING = "file.maxSize"; + + /** + * 上传路径在全局中的路径 + */ + public static final String UPLOAD_PATH_OF_SETTING = "global.uploadPath"; + + /** + * 上传形式路径 + */ + public static final String UPLOAD_FORM_OF_SETTING = "global.uploadForm"; + + /** + * token的路径 + */ + public static final String TOKEN_OF_SETTINGS = "global.tokenPath"; + + /** + * 是否验证邮箱的路径 + */ + public static final String EMAIL_VERIFY_OF_SETTINGS = "user.emailVerify"; + + /** + * 用户默认权限 + */ + public static final String FILE_DEFAULT_AUTH_OF_SETTING = "file.defaultAuth"; + + /** + * 默认权限 + */ + public static final String AUTH_DEFAULT_OF_SETTING = "auth.default"; + + /** + * 用户默认权限 + */ + public static final String USER_DEFAULT_AUTH_OF_SETTING = "user.defaultAuth"; + + /** + * 默认权限路径 + */ + public static final String[] AUTH_OF_SETTINGS = {"isDownloadable", "isUploadable", "isDeletable", "isUpdatable", + "isVisible"}; + + /** + * 密码最短长度的路径 + */ + public static final String PASSWORD_MIN_LENGTH_OF_SETTINGS = "user.password.minLength"; + + /** + * 密码最长长度的路径 + */ + public static final String PASSWORD_MAX_LENGTH_OF_SETTINGS = "user.password.maxLength"; + + /** + * 用户名匹配模式的路径 + */ + public static final String USERNAME_PATTERN_OF_SETTINGS = "user.usernameMatch.pattern"; + + /** + * 邮件配置的路径 + */ + public static final String EMAIL_CONFIG_OF_SETTINGS = "user.emailConfig"; + + /** + * 邮件配置在用户配置中的路径 + */ + public static final String EMAIL_CONFIG_OF_USER = "emailConfig"; + + /** + * 配置文件中用户配置的路径 + */ + public static final String USER_OF_SETTINGS = "user"; + + /** + * 是否允许用户注册的路径 + */ + public static final String ALLOW_REGISTER_OF_SETTINGS = "global.allowRegister"; + + /** + * 配置文件中登录的路径 + */ + public static final String ALLOW_LOGIN_OF_SETTINGS = "global.allowLogin"; + + /** + * token在全局中的路径 + */ + public static final String TOKEN_PATH_OF_GLOBAL = "tokenPath"; + + /** + * 上传路径在全局配置中的路径 + */ + public static final String UPLOAD_PATH_OF_GLOBAL = "uploadPath"; + + /** + * 配置文件中全局配置的路径 + */ + public static final String GLOBAL_OF_SETTINGS = "global"; + + /** + * 配置文件中用户表的order by路径 + */ + public static final String USER_ORDER_BY_OF_SETTINGS = "user.orderBy"; + + /** + * 配置文件中用户表的page size路径 + */ + public static final String USER_PAGE_SIZE_OF_SETTINGS = "user.pageSize"; + + /** + * 配置文件中下载记录表的order by路径 + */ + public static final String DOWNLOAD_ORDER_BY_OF_SETTINGS = "download.orderBy"; + + /** + * 配置文件中下载表的page size路径 + */ + public static final String DOWNLOAD_PAGE_SIZE_OF_SETTINGS = "download.pageSize"; + + /** + * 配置文件中权限记录表的order by路径 + */ + public static final String AUTH_ORDER_BY_OF_SETTINGS = "auth.orderBy"; + + /** + * 配置文件中权限表的page size路径 + */ + public static final String AUTH_PAGE_SIZE_OF_SETTINGS = "auth.pageSize"; + + /** + * 默认上传路径,如果配置文件中的上传路径无法创建,将使用默认的上传路径 + */ + public static final String DEFAULT_UPLOAD_PATH = DefaultValues.STORAGE_PATH + "upload"; + + private ConfigConsts() {} +} diff --git a/src/main/java/com/mesasoft/cn/modules/constant/DefaultValues.java b/src/main/java/com/mesasoft/cn/modules/constant/DefaultValues.java new file mode 100644 index 0000000..76de50b --- /dev/null +++ b/src/main/java/com/mesasoft/cn/modules/constant/DefaultValues.java @@ -0,0 +1,82 @@ +package com.mesasoft.cn.modules.constant; + +import com.zhazhapan.modules.constant.ValueConsts; + +/** + * @author pantao + * @since 2018/1/19 + */ +public class DefaultValues { + + /** + * 未分类 + */ + public static final String UNCATEGORIZED = "未分类"; + + /** + * 404页面路径 + */ + public static final String NOT_FOUND_PAGE = "/404.html"; + + /** + * Controller包路径 + */ + public static final String CONTROLLER_PACKAGE = "com.zhazhapan.efo.web.controller"; + + /** + * 配置文件路径 + */ + public static final String SETTING_PATH = "/config.json"; + + /** + * 冒号 + */ + public static final String COLON = ":"; + + /** + * 默认存储路径 + */ + public static final String STORAGE_PATH = ValueConsts.USER_HOME + ValueConsts.SEPARATOR + "Desktop" + ValueConsts + .SEPARATOR; + + /** + * 首页映射路径 + */ + public static final String INDEX_PAGE = "/index"; + + /** + * 配置映射的路径 + */ + public static final String CONFIG_PAGE = "/config"; + + /** + * 资源映射的路径 + */ + public static final String ASSETS_PAGE = "/assets"; + + /** + * 登陆注册页面映射路径 + */ + public static final String SIGNIN_PAGE = "/signin"; + + /** + * 管理员页面映射路径 + */ + public static final String ADMIN_PAGE = "/admin"; + + /** + * int型数值3 + */ + public static final int THREE_INT = 3; + /** + * int型数值2 + */ + public static final int TWO_INT = 2; + + /** + * code字符 + */ + public static final String CODE_STRING = "code"; + + private DefaultValues() {} +} diff --git a/src/main/java/com/mesasoft/cn/service/IAuthService.java b/src/main/java/com/mesasoft/cn/service/IAuthService.java new file mode 100644 index 0000000..4f1759c --- /dev/null +++ b/src/main/java/com/mesasoft/cn/service/IAuthService.java @@ -0,0 +1,92 @@ +package com.mesasoft.cn.service; + +import com.mesasoft.cn.entity.Auth; +import com.mesasoft.cn.model.AuthRecord; + +import java.util.List; + +/** + * @author pantao + * @since 2018/2/1 + */ +public interface IAuthService { + + /** + * 添加权限集 + * + * @param files 文件 + * @param users 用户 + * @param auths 权限集 + * + * @return 是否添加成功 + */ + boolean addAuth(String files, String users, String auths); + + /** + * 批量删除权限记录 + * + * @param ids 权限编号集 + * + * @return 是否删除成功 + */ + boolean batchDelete(String ids); + + /** + * 更新权限 + * + * @param id 权限编号 + * @param auths 权限 + * + * @return 是否更新成功 + */ + boolean updateAuth(long id, String auths); + + /** + * 获取权限表数据 + * + * @param usernameOrEmail 用户名或邮箱 + * @param fileName 文件名 + * @param offset 偏移 + * + * @return {@link List} + */ + List listAuth(String usernameOrEmail, String fileName, int offset); + + /** + * 获取一个权限 + * + * @param fileId 文件编号 + * @param userId 用户编号 + * + * @return {@link AuthRecord} + */ + AuthRecord getByFileIdAndUserId(long fileId, int userId); + + /** + * 添加一个默认权限 + * + * @param userId 用户编号 + * @param fileId 文件编号 + * + * @return 是否添加成功 + */ + boolean insertDefaultAuth(int userId, long fileId); + + /** + * 添加一个权限 + * + * @param auth {@link Auth} + * + * @return 是否添加成功 + */ + boolean insertAuth(Auth auth); + + /** + * 通过文件编号删除权限 + * + * @param fileId 文件编号 + * + * @return 是否删除成功 + */ + boolean removeByFileId(long fileId); +} diff --git a/src/main/java/com/mesasoft/cn/service/ICategoryService.java b/src/main/java/com/mesasoft/cn/service/ICategoryService.java new file mode 100644 index 0000000..bf85404 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/service/ICategoryService.java @@ -0,0 +1,65 @@ +package com.mesasoft.cn.service; + +import com.mesasoft.cn.entity.Category; + +import java.util.List; + +/** + * @author pantao + * @since 2018/1/30 + */ +public interface ICategoryService { + + /** + * 添加一个分类 + * + * @param name 分类名称 + * + * @return 是否添加成功 + */ + boolean insert(String name); + + /** + * 删除一个分类 + * + * @param id 分类编号 + * + * @return 是否删除成功 + */ + boolean remove(int id); + + /** + * 更新分类 + * + * @param id 分类编号 + * @param name 分类名称 + * + * @return 是否更新成功 + */ + boolean update(int id, String name); + + /** + * 获取一个分类 + * + * @param id 分类编号 + * + * @return {@link Category} + */ + Category getById(int id); + + /** + * 获取所有的分类 + * + * @return {@link List} + */ + List list(); + + /** + * 通过分类名获取ID + * + * @param name 分类名 + * + * @return {@link Integer} + */ + int getIdByName(String name); +} diff --git a/src/main/java/com/mesasoft/cn/service/ICommonService.java b/src/main/java/com/mesasoft/cn/service/ICommonService.java new file mode 100644 index 0000000..de5fbc4 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/service/ICommonService.java @@ -0,0 +1,28 @@ +package com.mesasoft.cn.service; + +import org.springframework.web.multipart.MultipartFile; + +/** + * @author pantao + * @since 2018/1/23 + */ +public interface ICommonService { + + /** + * 发送验证码 + * + * @param email 邮箱 + * + * @return 验证码 + */ + int sendVerifyCode(String email); + + /** + * 上传头像 + * + * @param multipartFile 头像文件 + * + * @return 头像文件名 + */ + String uploadAvatar(MultipartFile multipartFile); +} diff --git a/src/main/java/com/mesasoft/cn/service/IConfigService.java b/src/main/java/com/mesasoft/cn/service/IConfigService.java new file mode 100644 index 0000000..e5acfef --- /dev/null +++ b/src/main/java/com/mesasoft/cn/service/IConfigService.java @@ -0,0 +1,22 @@ +package com.mesasoft.cn.service; + +/** + * @author pantao + * @since 2018/1/22 + */ +public interface IConfigService { + + /** + * 获取全局配置 + * + * @return {@link String} + */ + String getGlobalConfig(); + + /** + * 获取用户配置 + * + * @return {@link String} + */ + String getUserConfig(); +} diff --git a/src/main/java/com/mesasoft/cn/service/IDownloadedService.java b/src/main/java/com/mesasoft/cn/service/IDownloadedService.java new file mode 100644 index 0000000..bb47ecd --- /dev/null +++ b/src/main/java/com/mesasoft/cn/service/IDownloadedService.java @@ -0,0 +1,39 @@ +package com.mesasoft.cn.service; + +import com.mesasoft.cn.model.DownloadRecord; + +import java.util.List; + +/** + * @author pantao + * @since 2018/2/1 + */ +public interface IDownloadedService { + + /** + * 添加下载记录 + * + * @param userId 用户编号 + * @param fileId 文件编号 + */ + void insertDownload(int userId, long fileId); + + /** + * 通过文件编号删除下载记录 + * + * @param fileId 文件编号 + */ + void removeByFileId(long fileId); + + /** + * 获取所有下载记录 + * + * @param user 用户名或邮箱 + * @param category 分类名称 + * @param file 文件名 + * @param offset 偏移 + * + * @return {@link DownloadRecord} + */ + List list(String user, String file, String category, int offset); +} diff --git a/src/main/java/com/mesasoft/cn/service/IFileManagerService.java b/src/main/java/com/mesasoft/cn/service/IFileManagerService.java new file mode 100644 index 0000000..44c3a4f --- /dev/null +++ b/src/main/java/com/mesasoft/cn/service/IFileManagerService.java @@ -0,0 +1,136 @@ +package com.mesasoft.cn.service; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @author pantao + * @since 2018/1/29 + */ +public interface IFileManagerService { + + + /** + * 下载多个文件 + * + * @param response {@link HttpServletResponse} + * @param items 文件集 + * @param destFile 目标文件名 + * + * @throws IOException 异常 + */ + void multiDownload(HttpServletResponse response, String[] items, String destFile) throws IOException; + + /** + * 上传文件(暂时还没有实现) + * + * @param destination 目标文件 + * @param files {@link MultipartFile} + * + * @return {@link JSONObject} + */ + JSONObject upload(String destination, MultipartFile... files); + + /** + * 解压文件 + * + * @param object {@link JSONObject} + * + * @return {@link JSONObject} + */ + JSONObject extract(JSONObject object); + + /** + * 压缩文件 + * + * @param object {@link JSONObject} + * + * @return {@link JSONObject} + */ + JSONObject compress(JSONObject object); + + /** + * 设置文件权限 + * + * @param object {@link JSONObject} + * + * @return {@link JSONObject} + */ + JSONObject setPermission(JSONObject object); + + /** + * 创建文件夹 + * + * @param object {@link JSONObject} + * + * @return {@link JSONObject} + */ + JSONObject createFolder(JSONObject object); + + /** + * 获取文件内容 + * + * @param object {@link JSONObject} + * + * @return 文件内容 + */ + String getContent(JSONObject object); + + /** + * 编辑文件 + * + * @param object {@link JSONObject} + * + * @return {@link JSONObject} + */ + JSONObject edit(JSONObject object); + + /** + * 移除文件 + * + * @param object {@link JSONObject} + * + * @return {@link JSONObject} + */ + JSONObject remove(JSONObject object); + + /** + * 复制文件 + * + * @param object {@link JSONObject} + * + * @return {@link JSONObject} + */ + JSONObject copy(JSONObject object); + + /** + * 移动文件 + * + * @param object {@link JSONObject} + * + * @return {@link JSONObject} + */ + JSONObject move(JSONObject object); + + /** + * 重命名 + * + * @param object {@link JSONObject} + * + * @return {@link JSONObject} + */ + JSONObject rename(JSONObject object); + + /** + * 列出文件 + * + * @param object {@link JSONObject} + * + * @return {@link JSONObject} + */ + JSONArray list(JSONObject object); +} diff --git a/src/main/java/com/mesasoft/cn/service/IFileService.java b/src/main/java/com/mesasoft/cn/service/IFileService.java new file mode 100644 index 0000000..3024830 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/service/IFileService.java @@ -0,0 +1,228 @@ +package com.mesasoft.cn.service; + +import com.mesasoft.cn.entity.User; +import com.mesasoft.cn.model.BaseAuthRecord; +import com.mesasoft.cn.model.FileBasicRecord; +import com.mesasoft.cn.model.FileRecord; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import java.io.File; +import java.util.List; + +/** + * @author pantao + * @since 2018/1/29 + */ +public interface IFileService { + + /** + * 更新文件权限 + * + * @param id 文件编号 + * @param auth 权限集 + * + * @return 是否更新成功 + */ + boolean updateAuth(long id, String auth); + + /** + * 获取文件权限 + * + * @param id 文件编号 + * + * @return {@link BaseAuthRecord} + */ + BaseAuthRecord getAuth(long id); + + /** + * 批量删除文件 + * + * @param ids 所有文件编号 + * + * @return 是否删除成功 + */ + boolean deleteFiles(String ids); + + /** + * 更新文件路径 + * + * @param id 文件编号 + * @param oldLocalUrl 原本地路径 + * @param localUrl 本地路径 + * @param visitUrl 访问路径 + * + * @return 是否更新成功 + */ + boolean[] updateUrl(int id, String oldLocalUrl, String localUrl, String visitUrl); + + /** + * 更新文件信息 + * + * @param id 文件编号 + * @param user 用户对象 + * @param name 文件名 + * @param category 文件分类 + * @param tag 标签 + * @param description 文件描述 + * + * @return 是否更新成功 + */ + boolean updateFileInfo(long id, User user, String name, String category, String tag, String description); + + /** + * 删除文件,验证权限 + * + * @param user 用户对象 + * @param fileId 文件编号 + * + * @return {@link Boolean} + */ + boolean removeFile(User user, long fileId); + + /** + * 获取用户的下载资源 + * + * @param userId 用户编号 + * @param offset 偏移 + * @param search 搜索 + * + * @return {@link List} + */ + List listUserDownloaded(int userId, int offset, String search); + + /** + * 获取用户的上传资源 + * + * @param userId 用户编号 + * @param offset 偏移 + * @param search 搜索 + * + * @return {@link List} + */ + List listUserUploaded(int userId, int offset, String search); + + /** + * 通过编号删除,不验证权限 + * + * @param id 编号 + * + * @return 是否删除成功 + */ + boolean removeById(long id); + + /** + * 通过访问路径删除 + * + * @param visitUrl 访问路径 + * + * @return 是否删除成功 + */ + boolean removeByVisitUrl(String visitUrl); + + /** + * 通过本地路径删除 + * + * @param localUrl 访问路径 + * + * @return 是否删除成功 + */ + boolean removeByLocalUrl(String localUrl); + + /** + * 获取资源 + * + * @param visitUrl 访问路径 + * @param request {@link HttpServletRequest} + * + * @return {@link File} + */ + String getResource(String visitUrl, HttpServletRequest request); + + /** + * 通过访问路径获取本地文件路径 + * + * @param visitUrl 访问路径 + * + * @return {@link String} + */ + String getLocalUrlByVisitUrl(String visitUrl); + + /** + * 获取所有文件 + * + * @param userId 用户编号 + * @param offset 偏移 + * @param categoryId 分类编号 + * @param orderBy 排序方式 + * @param search 搜索 + * + * @return {@link List} + */ + List listAll(int userId, int offset, int categoryId, String orderBy, String search); + + /** + * 上传文件 + * + * @param categoryId 分类ID + * @param tag 标签 + * @param description 描述 + * @param prefix 自定义前缀 + * @param multipartFile 文件 + * @param user {@link User} + * + * @return 是否上传成功 + */ + boolean upload(int categoryId, String tag, String description, String prefix, MultipartFile multipartFile, User + user); + + /** + * 分享服务器本地文件 + * + * @param prefix 链接前缀 + * @param files 文件 + * @param user 用户对象 + * + * @return 是否添加成功 + */ + boolean shareFiles(String prefix, String files, User user); + + /** + * 本地路径是否存在 + * + * @param localUrl 本地路径 + * + * @return {@link Boolean} + */ + boolean localUrlExists(String localUrl); + + /** + * 访问路径是否存在 + * + * @param visitUrl 访问路径 + * + * @return {@link Boolean} + */ + boolean visitUrlExists(String visitUrl); + + /** + * 通过本地路径获取文件编号 + * + * @param localUrl 本地路径 + * + * @return 文件编号 + */ + long getFileId(String localUrl); + + /** + * 获取所有文件基本信息 + * + * @param user 用户名或邮箱 + * @param category 分类名称 + * @param file 文件名 + * @param offset 偏移 + * + * @return {@link List} + */ + List listBasicAll(String user, String file, String category, int offset); +} diff --git a/src/main/java/com/mesasoft/cn/service/IUploadedService.java b/src/main/java/com/mesasoft/cn/service/IUploadedService.java new file mode 100644 index 0000000..25cc002 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/service/IUploadedService.java @@ -0,0 +1,24 @@ +package com.mesasoft.cn.service; + +import com.mesasoft.cn.model.UploadedRecord; + +import java.util.List; + +/** + * @author pantao + * @since 2018/2/28 + */ +public interface IUploadedService { + + /** + * 获取所有上传记录 + * + * @param user 用户名或邮箱 + * @param category 分类名称 + * @param file 文件名 + * @param offset 偏移 + * + * @return {@link List} + */ + List list(String user, String file, String category, int offset); +} diff --git a/src/main/java/com/mesasoft/cn/service/IUserService.java b/src/main/java/com/mesasoft/cn/service/IUserService.java new file mode 100644 index 0000000..485e65d --- /dev/null +++ b/src/main/java/com/mesasoft/cn/service/IUserService.java @@ -0,0 +1,161 @@ +package com.mesasoft.cn.service; + +import com.mesasoft.cn.entity.User; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * @author pantao + * @since 2018/1/22 + */ +public interface IUserService { + + /** + * 更新用户权限 + * + * @param id 用户编号 + * @param permission 权限 + * + * @return 是否更新成功 + */ + boolean updatePermission(int id, int permission); + + /** + * 重置用户密码 + * + * @param id 用户编号 + * @param password 密码 + * + * @return 是否重置成功 + */ + boolean resetPassword(int id, String password); + + /** + * 更新用户权限 + * + * @param id 编号 + * @param auths 操作文件的权限集 + * + * @return 是否更新成功 + */ + boolean updateFileAuth(int id, String auths); + + /** + * 获取用户 + * + * @param permission 当前用户权限 + * @param condition 筛选条件 + * @param offset 偏移 + * + * @return {@link List} + */ + List listUser(int permission, String condition, int offset); + + /** + * 登录 + * + * @param loginName 登录名 + * @param password 密码 + * @param token 自动登录 + * @param response 响应 + * + * @return {@link User} + */ + User login(String loginName, String password, String token, HttpServletResponse response); + + /** + * 注册 + * + * @param username 用户名 + * @param email 邮箱 + * @param password 密码 + * + * @return 是否插入成功 + */ + boolean register(String username, String email, String password); + + /** + * 重置密码 + * + * @param email 邮箱 + * @param password 密码 + * + * @return {@link Boolean} + */ + boolean resetPasswordByEmail(String email, String password); + + /** + * 检查用户名是否存在 + * + * @param username 用户名 + * + * @return {@link Boolean} + */ + boolean usernameExists(String username); + + /** + * 通过编号获取用户 + * + * @param id 编号 + * + * @return {@link User} + */ + User getUserById(int id); + + /** + * 更新用户登录时间 + * + * @param user {@link User} + */ + void updateUserLoginTime(User user); + + /** + * 更新密码 + * + * @param password 密码 + * @param id 用户编号 + * + * @return 是否更新成功 + */ + boolean updatePasswordById(String password, int id); + + /** + * 检查密码是否合法 + * + * @param password 密码 + * + * @return {@link Boolean} + */ + boolean checkPassword(String password); + + /** + * 检查邮箱是否存在 + * + * @param email 邮箱 + * + * @return {@link Boolean} + */ + boolean emailExists(String email); + + /** + * 更新用户基本信息 + * + * @param id 编号 + * @param avatar 头像 + * @param realName 真实姓名 + * @param email 邮箱 + * + * @return 是否更新成功 + */ + boolean updateBasicInfoById(int id, String avatar, String realName, String email); + + /** + * 用过用户名获取用户Id + * + * @param usernameOrEmail 用户名或邮箱 + * + * @return 用户编号 + */ + int getUserId(String usernameOrEmail); +} diff --git a/src/main/java/com/mesasoft/cn/service/impl/AuthServiceImpl.java b/src/main/java/com/mesasoft/cn/service/impl/AuthServiceImpl.java new file mode 100644 index 0000000..d4d0e53 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/service/impl/AuthServiceImpl.java @@ -0,0 +1,96 @@ +package com.mesasoft.cn.service.impl; + +import com.mesasoft.cn.dao.AuthDAO; +import com.mesasoft.cn.util.BeanUtils; +import com.mesasoft.cn.config.SettingConfig; +import com.mesasoft.cn.entity.Auth; +import com.mesasoft.cn.model.AuthRecord; +import com.mesasoft.cn.modules.constant.ConfigConsts; +import com.mesasoft.cn.service.IAuthService; +import com.mesasoft.cn.util.ServiceUtils; +import com.zhazhapan.modules.constant.ValueConsts; +import com.zhazhapan.util.Checker; +import com.zhazhapan.util.Formatter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @author pantao + * @since 2018/2/1 + */ +@Service +public class AuthServiceImpl implements IAuthService { + + private final AuthDAO authDAO; + + @Autowired + public AuthServiceImpl(AuthDAO authDAO) {this.authDAO = authDAO;} + + @Override + public boolean addAuth(String files, String users, String auths) { + if (Checker.isNotEmpty(files) && Checker.isNotEmpty(users) && Checker.isNotEmpty(auths)) { + String[] file = files.split(ValueConsts.COMMA_SIGN); + String[] user = users.split(ValueConsts.COMMA_SIGN); + for (String f : file) { + long fileId = Formatter.stringToLong(f); + for (String u : user) { + int userId = Formatter.stringToInt(u); + if (Checker.isNull(authDAO.exists(userId, fileId))) { + Auth auth = new Auth(userId, fileId); + auth.setAuth(BeanUtils.getAuth(auths)); + authDAO.insertAuth(auth); + } + } + } + } + return true; + } + + @Override + public boolean batchDelete(String ids) { + return Checker.isNotEmpty(ids) && authDAO.batchDelete(ids); + } + + @Override + public boolean updateAuth(long id, String auths) { + int[] auth = BeanUtils.getAuth(auths); + return authDAO.updateAuthById(id, auth[0], auth[1], auth[2], auth[3], auth[4]); + } + + @Override + public List listAuth(String usernameOrEmail, String fileName, int offset) { + long fileId = ServiceUtils.getFileId(fileName); + int userId = ServiceUtils.getUserId(usernameOrEmail); + return authDAO.listAuthBy(ValueConsts.ZERO_INT, userId, fileId, fileName, offset); + } + + @Override + public AuthRecord getByFileIdAndUserId(long fileId, int userId) { + List authRecords = authDAO.listAuthBy(ValueConsts.ZERO_INT, userId, fileId, ValueConsts + .EMPTY_STRING, ValueConsts.ZERO_INT); + if (Checker.isNotEmpty(authRecords)) { + return authRecords.get(0); + } + return null; + } + + @Override + public boolean insertDefaultAuth(int userId, long fileId) { + int[] defaultAuth = SettingConfig.getAuth(ConfigConsts.AUTH_DEFAULT_OF_SETTING); + Auth auth = new Auth(userId, fileId); + auth.setAuth(defaultAuth[0], defaultAuth[1], defaultAuth[2], defaultAuth[3], defaultAuth[4]); + return insertAuth(auth); + } + + @Override + public boolean insertAuth(Auth auth) { + return authDAO.insertAuth(auth); + } + + @Override + public boolean removeByFileId(long fileId) { + return authDAO.removeAuthByFileId(fileId); + } +} diff --git a/src/main/java/com/mesasoft/cn/service/impl/CategoryServiceImpl.java b/src/main/java/com/mesasoft/cn/service/impl/CategoryServiceImpl.java new file mode 100644 index 0000000..12f9a2d --- /dev/null +++ b/src/main/java/com/mesasoft/cn/service/impl/CategoryServiceImpl.java @@ -0,0 +1,62 @@ +package com.mesasoft.cn.service.impl; + +import com.mesasoft.cn.dao.CategoryDAO; +import com.mesasoft.cn.entity.Category; +import com.mesasoft.cn.modules.constant.DefaultValues; +import com.mesasoft.cn.service.ICategoryService; +import com.zhazhapan.util.Checker; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @author pantao + * @since 2018/1/30 + */ +@Service +public class CategoryServiceImpl implements ICategoryService { + + private final CategoryDAO categoryDAO; + + @Autowired + public CategoryServiceImpl(CategoryDAO categoryDAO) {this.categoryDAO = categoryDAO;} + + @Override + public boolean insert(String name) { + return Checker.isNotNull(name) && categoryDAO.insertCategory(name); + } + + @Override + public boolean remove(int id) { + return isCategorized(id) && categoryDAO.removeCategoryById(id); + } + + @Override + public boolean update(int id, String name) { + return Checker.isNotEmpty(name) && isCategorized(id) && categoryDAO.updateNameById(id, name); + } + + private boolean isCategorized(int id) { + return !DefaultValues.UNCATEGORIZED.equals(getById(id).getName()); + } + + @Override + public Category getById(int id) { + return categoryDAO.getCategoryById(id); + } + + @Override + public List list() { + return categoryDAO.listCategory(); + } + + @Override + public int getIdByName(String name) { + try { + return categoryDAO.getIdByName(name); + } catch (Exception e) { + return Integer.MAX_VALUE; + } + } +} diff --git a/src/main/java/com/mesasoft/cn/service/impl/CommonServiceImpl.java b/src/main/java/com/mesasoft/cn/service/impl/CommonServiceImpl.java new file mode 100644 index 0000000..05f1d10 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/service/impl/CommonServiceImpl.java @@ -0,0 +1,59 @@ +package com.mesasoft.cn.service.impl; + +import com.mesasoft.cn.modules.constant.DefaultValues; +import com.mesasoft.cn.config.SettingConfig; +import com.mesasoft.cn.service.ICommonService; +import com.zhazhapan.modules.constant.ValueConsts; +import com.zhazhapan.util.Checker; +import com.zhazhapan.util.FileExecutor; +import com.zhazhapan.util.MailSender; +import com.zhazhapan.util.RandomUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; + +/** + * @author pantao + * @since 2018/1/23 + */ +@Service +public class CommonServiceImpl implements ICommonService { + + private static final String EMAIL_TITLE = "请查收您的验证码"; + private static Logger logger = LoggerFactory.getLogger(CommonServiceImpl.class); + + @Override + public int sendVerifyCode(String email) { + int code = RandomUtils.getRandomInteger(ValueConsts.VERIFY_CODE_FLOOR, ValueConsts.VERIFY_CODE_CEIL); + String content = "

您的验证码:" + code + "



如非本人操作,请忽略本条消息。

"; + try { + MailSender.sendMail(email, EMAIL_TITLE, content); + return code; + } catch (Exception e) { + logger.error(e.getMessage()); + return 0; + } + } + + @Override + public String uploadAvatar(MultipartFile multipartFile) { + if (!multipartFile.isEmpty()) { + String name = RandomUtils.getRandomStringOnlyLowerCase(ValueConsts.SIXTEEN_INT) + ValueConsts.DOT_SIGN + + FileExecutor.getFileSuffix(multipartFile.getOriginalFilename()); + if (Checker.isImage(name) && multipartFile.getSize() < ValueConsts.MB * DefaultValues.TWO_INT) { + String path = SettingConfig.getAvatarStoragePath() + ValueConsts.SEPARATOR + name; + try { + FileExecutor.writeByteArrayToFile(new File(path), multipartFile.getBytes()); + return name; + } catch (IOException e) { + logger.error("upload avatar error: " + e.getMessage()); + } + } + } + return ""; + } +} diff --git a/src/main/java/com/mesasoft/cn/service/impl/ConfigServiceImpl.java b/src/main/java/com/mesasoft/cn/service/impl/ConfigServiceImpl.java new file mode 100644 index 0000000..1de92b2 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/service/impl/ConfigServiceImpl.java @@ -0,0 +1,33 @@ +package com.mesasoft.cn.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.mesasoft.cn.SketchApplication; +import com.mesasoft.cn.modules.constant.ConfigConsts; +import com.mesasoft.cn.service.IConfigService; +import org.springframework.stereotype.Service; + +/** + * @author pantao + * @since 2018/1/22 + */ +@Service +public class ConfigServiceImpl implements IConfigService { + + @Override + public String getGlobalConfig() { + JSONObject jsonObject = (JSONObject) SketchApplication.settings.getObjectUseEval(ConfigConsts + .GLOBAL_OF_SETTINGS).clone(); + jsonObject.remove(ConfigConsts.UPLOAD_PATH_OF_GLOBAL); + jsonObject.remove(ConfigConsts.TOKEN_PATH_OF_GLOBAL); + jsonObject.remove(ConfigConsts.UPLOAD_FORM_OF_SETTING); + return jsonObject.toString(); + } + + @Override + public String getUserConfig() { + JSONObject jsonObject = (JSONObject) SketchApplication.settings.getObjectUseEval(ConfigConsts.USER_OF_SETTINGS) + .clone(); + jsonObject.remove(ConfigConsts.EMAIL_CONFIG_OF_USER); + return jsonObject.toString(); + } +} diff --git a/src/main/java/com/mesasoft/cn/service/impl/DownloadedServiceImpl.java b/src/main/java/com/mesasoft/cn/service/impl/DownloadedServiceImpl.java new file mode 100644 index 0000000..3c2b546 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/service/impl/DownloadedServiceImpl.java @@ -0,0 +1,42 @@ +package com.mesasoft.cn.service.impl; + +import com.mesasoft.cn.dao.DownloadedDAO; +import com.mesasoft.cn.model.DownloadRecord; +import com.mesasoft.cn.service.IDownloadedService; +import com.mesasoft.cn.util.ServiceUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @author pantao + * @since 2018/2/1 + */ +@Service +public class DownloadedServiceImpl implements IDownloadedService { + + private final DownloadedDAO downloadDAO; + + @Autowired + public DownloadedServiceImpl(DownloadedDAO downloadDAO) { + this.downloadDAO = downloadDAO; + } + + @Override + public void insertDownload(int userId, long fileId) { + downloadDAO.insertDownload(userId, fileId); + } + + @Override + public void removeByFileId(long fileId) { + downloadDAO.removeByFileId(fileId); + } + + @SuppressWarnings("unchecked") + @Override + public List list(String user, String file, String category, int offset) { + return (List) ServiceUtils.invokeFileFilter(downloadDAO, "listDownloadedBy", user, file, + category, offset); + } +} diff --git a/src/main/java/com/mesasoft/cn/service/impl/FileManagerServiceImpl.java b/src/main/java/com/mesasoft/cn/service/impl/FileManagerServiceImpl.java new file mode 100644 index 0000000..0ae179b --- /dev/null +++ b/src/main/java/com/mesasoft/cn/service/impl/FileManagerServiceImpl.java @@ -0,0 +1,236 @@ +package com.mesasoft.cn.service.impl; + +import cn.hutool.core.util.ZipUtil; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.mesasoft.cn.modules.constant.DefaultValues; +import com.mesasoft.cn.service.IFileManagerService; +import com.zhazhapan.modules.constant.ValueConsts; +import com.zhazhapan.util.Checker; +import com.zhazhapan.util.FileExecutor; +import com.zhazhapan.util.Formatter; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; +import javax.swing.filechooser.FileSystemView; +import java.io.File; +import java.io.IOException; +import java.util.Date; + +/** + * @author pantao + * @since 2018/1/29 + */ +@Service +public class FileManagerServiceImpl implements IFileManagerService { + + private static Logger logger = Logger.getLogger(FileManagerServiceImpl.class); + + @Override + public void multiDownload(HttpServletResponse response, String[] items, String destFile) throws IOException { + File zip = ZipUtil.zip(new File(ValueConsts.USER_DESKTOP + File.separator + destFile), ValueConsts.FALSE, + FileExecutor.getFiles(items)); + if (zip.exists()) { + response.getOutputStream().write(FileExecutor.readFileToByteArray(zip)); + FileExecutor.deleteFile(zip); + } + } + + @Override + public JSONObject upload(String destination, MultipartFile... files) { + System.out.println(files.length); + if (Checker.isNotEmpty(files)) { + if (Checker.isWindows() && destination.length() < ValueConsts.TWO_INT) { + destination = "C:"; + } + for (MultipartFile file : files) { + if (Checker.isNotNull(file) && !file.isEmpty()) { + try { + file.transferTo(new File(destination + File.separator + file.getOriginalFilename())); + } catch (IOException e) { + logger.error(e.getMessage()); + return getBasicResponse(ValueConsts.FALSE); + } + } + } + } + return getBasicResponse(ValueConsts.TRUE); + } + + @Override + public JSONObject extract(JSONObject object) { + String destination = object.getString("destination") + File.separator + object.getString("folderName"); + String zipFile = object.getString("item"); + return getBasicResponse(ZipUtil.unzip(zipFile, destination).exists()); + } + + @Override + public JSONObject compress(JSONObject object) { + JSONArray array = object.getJSONArray("items"); + File[] files = new File[array.size()]; + int i = 0; + for (Object file : array) { + files[i++] = new File(file.toString()); + } + String dest = object.getString("destination"); + String name = object.getString("compressedFilename"); + File zip = ZipUtil.zip(new File(dest + File.separator + name), ValueConsts.FALSE, files); + return getBasicResponse(zip.exists()); + } + + @Override + public JSONObject setPermission(JSONObject object) { + if (Checker.isLinux()) { + JSONArray array = object.getJSONArray("items"); + int code = object.getInteger("permsCode"); + for (Object file : array) { + try { + Runtime.getRuntime().exec("chmod -R " + code + " " + file.toString()); + } catch (IOException e) { + logger.error(e.getMessage()); + return getBasicResponse(ValueConsts.FALSE); + } + } + } + return getBasicResponse(ValueConsts.TRUE); + } + + @Override + public JSONObject createFolder(JSONObject object) { + String folder = object.getString("newPath"); + return getBasicResponse(FileExecutor.createFolder(folder)); + } + + @Override + public String getContent(JSONObject object) { + String fileName = object.getString("item"); + try { + return FileExecutor.readFile(fileName); + } catch (IOException e) { + logger.error(e.getMessage()); + return ""; + } + } + + @Override + public JSONObject edit(JSONObject object) { + String file = object.getString("item"); + String content = object.getString("content"); + try { + FileExecutor.saveFile(file, content); + return getBasicResponse(ValueConsts.TRUE); + } catch (IOException e) { + logger.error(e.getMessage()); + return getBasicResponse(ValueConsts.FALSE); + } + } + + @Override + public JSONObject remove(JSONObject object) { + JSONArray array = object.getJSONArray("items"); + array.forEach(file -> FileExecutor.deleteFile(file.toString())); + return getBasicResponse(ValueConsts.TRUE); + } + + @Override + public JSONObject copy(JSONObject object) { + JSONArray array = object.getJSONArray("items"); + String dest = object.getString("newPath"); + File[] files = new File[array.size()]; + int i = 0; + for (Object file : array) { + files[i++] = new File(file.toString()); + } + try { + FileExecutor.copyFiles(files, dest); + return getBasicResponse(ValueConsts.TRUE); + } catch (IOException e) { + logger.error(e.getMessage()); + return getBasicResponse(ValueConsts.FALSE); + } + } + + @Override + public JSONObject move(JSONObject object) { + JSONArray array = object.getJSONArray("items"); + String dest = object.getString("newPath"); + for (Object file : array) { + try { + FileExecutor.moveToDirectory(new File(file.toString()), new File(dest), ValueConsts.TRUE); + } catch (IOException e) { + logger.error(e.getMessage()); + return getBasicResponse(ValueConsts.FALSE); + } + } + return getBasicResponse(ValueConsts.TRUE); + } + + @Override + public JSONObject rename(JSONObject object) { + String fileName = object.getString("item"); + String newFileName = object.getString("newItemPath"); + FileExecutor.renameTo(fileName, newFileName); + return getBasicResponse(ValueConsts.TRUE); + } + + @Override + public JSONArray list(JSONObject object) { + String path = object.getString("path"); + JSONArray array = new JSONArray(); + File[] files = null; + if (Checker.isWindows()) { + if (Checker.isNotEmpty(path) && path.startsWith(ValueConsts.SPLASH_STRING)) { + path = path.substring(1); + } + if (Checker.isEmpty(path)) { + FileSystemView fsv = FileSystemView.getFileSystemView(); + File[] fs = File.listRoots(); + for (File file : fs) { + if (file.getTotalSpace() > 0) { + String displayName = fsv.getSystemDisplayName(file); + int len = displayName.length(); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("name", displayName.substring(len - 3, len - 1)); + jsonObject.put("rights", "----------"); + jsonObject.put("size", file.getTotalSpace() - file.getFreeSpace()); + jsonObject.put("date", Formatter.datetimeToString(new Date(file.lastModified()))); + jsonObject.put("type", file.isDirectory() ? "dir" : "file"); + array.add(jsonObject); + } + } + } else if (path.startsWith(DefaultValues.COLON, 1)) { + files = FileExecutor.listFile(path.endsWith(DefaultValues.COLON) ? path + File.separator : path); + } else { + logger.error("path error"); + } + } else { + files = FileExecutor.listFile(Checker.isEmpty(path) ? "/" : (path.startsWith("/") ? path : "/" + path)); + } + if (Checker.isNotNull(files)) { + for (File file : files) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("name", file.getName()); + jsonObject.put("rights", "----------"); + jsonObject.put("size", file.length()); + jsonObject.put("date", Formatter.datetimeToString(new Date(file.lastModified()))); + jsonObject.put("type", file.isDirectory() ? "dir" : "file"); + array.add(jsonObject); + } + } + return array; + } + + private JSONObject getBasicResponse(boolean isSuccess) { + JSONObject jsonObject = new JSONObject(); + if (isSuccess) { + jsonObject.put("success", true); + jsonObject.put("error", null); + } else { + jsonObject.put("success", null); + jsonObject.put("error", "服务器异常"); + } + return jsonObject; + } +} diff --git a/src/main/java/com/mesasoft/cn/service/impl/FileServiceImpl.java b/src/main/java/com/mesasoft/cn/service/impl/FileServiceImpl.java new file mode 100644 index 0000000..2c6e6e9 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/service/impl/FileServiceImpl.java @@ -0,0 +1,383 @@ +package com.mesasoft.cn.service.impl; + +import com.mesasoft.cn.SketchApplication; +import com.mesasoft.cn.dao.DownloadedDAO; +import com.mesasoft.cn.dao.FileDAO; +import com.mesasoft.cn.util.BeanUtils; +import com.mesasoft.cn.config.SettingConfig; +import com.mesasoft.cn.entity.Category; +import com.mesasoft.cn.entity.File; +import com.mesasoft.cn.entity.User; +import com.mesasoft.cn.model.AuthRecord; +import com.mesasoft.cn.model.BaseAuthRecord; +import com.mesasoft.cn.model.FileBasicRecord; +import com.mesasoft.cn.model.FileRecord; +import com.mesasoft.cn.modules.constant.ConfigConsts; +import com.mesasoft.cn.modules.constant.DefaultValues; +import com.mesasoft.cn.service.IAuthService; +import com.mesasoft.cn.service.ICategoryService; +import com.mesasoft.cn.service.IFileService; +import com.mesasoft.cn.util.ServiceUtils; +import com.zhazhapan.modules.constant.ValueConsts; +import com.zhazhapan.util.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.Date; +import java.util.List; +import java.util.regex.Pattern; + +/** + * @author pantao + * @since 2018/1/29 + */ +@Service +public class FileServiceImpl implements IFileService { + + private final static Logger logger = LoggerFactory.getLogger(FileServiceImpl.class); + + private static final String FILE_SUFFIX = "{fileSuffix}"; + + private static final String RANDOM_ID = "{randomId}"; + + private static final String YEAR = "{year}"; + + private static final String MONTH = "{month}"; + + private static final String DAY = "{day}"; + + private static final String AUTHOR = "{author}"; + + private static final String FILE_NAME = "{fileName}"; + + private static final String CATEGORY_NAME = "{categoryName}"; + + private final FileDAO fileDAO; + + private final ICategoryService categoryService; + + private final IAuthService authService; + + private final DownloadedDAO downloadDAO; + + @Autowired + public FileServiceImpl(FileDAO fileDAO, ICategoryService categoryService, IAuthService authService, + DownloadedDAO downloadDAO) { + this.fileDAO = fileDAO; + this.categoryService = categoryService; + this.authService = authService; + this.downloadDAO = downloadDAO; + } + + @Override + public boolean updateAuth(long id, String auth) { + int[] au = BeanUtils.getAuth(auth); + return fileDAO.updateAuthById(id, au[0], au[1], au[2], au[3], au[4]); + } + + @Override + public BaseAuthRecord getAuth(long id) { + return fileDAO.getAuth(id); + } + + @Override + @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor = + Exception.class) + public boolean deleteFiles(String ids) { + if (Checker.isNotEmpty(ids)) { + String[] id = ids.split(ValueConsts.COMMA_SIGN); + for (String s : id) { + long fileId = Formatter.stringToLong(s); + String localUrl = fileDAO.getLocalUrlById(fileId); + FileExecutor.deleteFile(localUrl); + removeById(fileId); + } + return true; + } + return false; + } + + @Override + @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor = + Exception.class) + public boolean[] updateUrl(int id, String oldLocalUrl, String localUrl, String visitUrl) { + boolean[] b = new boolean[]{false, false}; + boolean canUpdateLocalUrl = Checker.isExists(oldLocalUrl) && Checker.isNotEmpty(localUrl) && Checker + .isNotExists(localUrl) && !localUrlExists(localUrl); + if (canUpdateLocalUrl) { + FileExecutor.renameTo(oldLocalUrl, localUrl); + fileDAO.updateLocalUrlById(id, localUrl); + b[0] = true; + } + if (Checker.isNotEmpty(visitUrl) && !visitUrlExists(visitUrl)) { + fileDAO.updateVisitUrlById(id, visitUrl); + b[1] = true; + } + return b; + } + + @Override + @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor = + Exception.class) + public boolean updateFileInfo(long id, User user, String name, String category, String tag, String description) { + File file = fileDAO.getById(id); + if (Checker.isNotNull(file) && file.getIsUpdatable() == 1) { + AuthRecord authRecord = authService.getByFileIdAndUserId(id, user.getId()); + String suffix = FileExecutor.getFileSuffix(name); + boolean canUpdate = (Checker.isNull(authRecord) ? user.getIsUpdatable() == 1 : + authRecord.getIsUpdatable() == 1) && Checker.isNotEmpty(name) && Pattern.compile(SketchApplication.settings.getStringUseEval(ConfigConsts.FILE_SUFFIX_MATCH_OF_SETTING)).matcher(suffix).matches(); + if (canUpdate) { + String localUrl = file.getLocalUrl(); + java.io.File newFile = new java.io.File(localUrl); + String visitUrl = file.getVisitUrl(); + String newLocalUrl = localUrl.substring(0, localUrl.lastIndexOf(ValueConsts.SEPARATOR) + 1) + name; + String newVisitUrl = visitUrl.substring(0, visitUrl.lastIndexOf(ValueConsts.SPLASH_STRING) + 1) + name; + file.setName(name); + file.setSuffix(suffix); + file.setLocalUrl(newLocalUrl); + file.setVisitUrl(newVisitUrl); + file.setCategoryId(categoryService.getIdByName(category)); + file.setTag(tag); + file.setDescription(description); + boolean isValid = (localUrl.endsWith(ValueConsts.SEPARATOR + name) || (!Checker.isExists(newLocalUrl) + && !localUrlExists(newLocalUrl) && !visitUrlExists(newVisitUrl))); + if (isValid && fileDAO.updateFileInfo(file)) { + return newFile.renameTo(new java.io.File(newLocalUrl)); + } + } + } + return false; + } + + @Override + public boolean removeFile(User user, long fileId) { + File file = fileDAO.getById(fileId); + boolean isDeleted = false; + if (Checker.isNotNull(file) && file.getIsDeletable() == 1) { + AuthRecord authRecord = authService.getByFileIdAndUserId(fileId, user.getId()); + String localUrl = fileDAO.getLocalUrlById(fileId); + isDeleted = (Checker.isNull(authRecord) ? user.getIsDeletable() == 1 : authRecord.getIsDeletable() == 1) + && removeById(fileId); + if (isDeleted) { + FileExecutor.deleteFile(localUrl); + } + } + return isDeleted; + } + + @Override + public List listUserDownloaded(int userId, int offset, String search) { + return fileDAO.listUserDownloaded(userId, offset, search); + } + + @Override + public List listUserUploaded(int userId, int offset, String search) { + return fileDAO.listUserUploaded(userId, offset, search); + } + + @Override + @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor = + Exception.class) + public boolean removeById(long id) { + downloadDAO.removeByFileId(id); + authService.removeByFileId(id); + return fileDAO.removeById(id); + } + + @Override + public boolean removeByVisitUrl(String visitUrl) { + long fileId = fileDAO.getIdByVisitUrl(visitUrl); + return removeById(fileId); + } + + @Override + public boolean removeByLocalUrl(String localUrl) { + long fileId = fileDAO.getIdByLocalUrl(localUrl); + return removeById(fileId); + } + + @Override + public String getResource(String visitUrl, HttpServletRequest request) { + logger.info("visit url: " + visitUrl); + boolean downloadable = SketchApplication.settings.getBooleanUseEval(ConfigConsts + .ANONYMOUS_DOWNLOADABLE_OF_SETTING); + User user = (User) request.getSession().getAttribute(ValueConsts.USER_STRING); + File file = fileDAO.getFileByVisitUrl(visitUrl); + AuthRecord authRecord = null; + if (Checker.isNotNull(file)) { + authRecord = authService.getByFileIdAndUserId(file.getId(), Checker.isNull(user) ? 0 : user.getId()); + } + boolean canDownload = Checker.isNotNull(file) && file.getIsDownloadable() == 1 && (downloadable || (Checker + .isNull(authRecord) ? (Checker.isNotNull(user) && user.getIsDownloadable() == 1) : authRecord + .getIsDownloadable() == 1)); + if (canDownload) { + fileDAO.updateDownloadTimesById(file.getId()); + if (Checker.isNotNull(user)) { + downloadDAO.insertDownload(user.getId(), file.getId()); + } + return file.getLocalUrl(); + } + return ""; + } + + @Override + public String getLocalUrlByVisitUrl(String visitUrl) { + return fileDAO.getLocalUrlByVisitUrl(Checker.checkNull(visitUrl)); + } + + @Override + public List listAll(int userId, int offset, int categoryId, String orderBy, String search) { + return fileDAO.listAll(userId, offset, categoryId, orderBy, search); + } + + @Override + @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor = + Exception.class) + public boolean upload(int categoryId, String tag, String description, String prefix, MultipartFile multipartFile, + User user) { + if (user.getIsUploadable() == 1) { + String name = multipartFile.getOriginalFilename(); + String suffix = FileExecutor.getFileSuffix(name); + String localUrl = SettingConfig.getUploadStoragePath() + ValueConsts.SEPARATOR + name; + Category category = categoryService.getById(categoryId); + long maxSize = Formatter.sizeToLong(SketchApplication.settings.getStringUseEval(ConfigConsts + .FILE_MAX_SIZE_OF_SETTING)); + long size = multipartFile.getSize(); + boolean fileExists = localUrlExists(localUrl); + //检测标签是否合法 + if (SketchApplication.settings.getBooleanUseEval(ConfigConsts.TAG_REQUIRE_OF_SETTING)) { + String[] tags = Checker.checkNull(tag).split(ValueConsts.SPACE); + if (tags.length <= SketchApplication.settings.getIntegerUseEval(ConfigConsts.TAG_SIZE_OF_SETTING)) { + int maxLength = SketchApplication.settings.getIntegerUseEval(ConfigConsts.TAG_LENGTH_OF_SETTING); + for (String t : tags) { + if (t.length() > maxLength) { + return false; + } + } + } else { + return false; + } + } + //是否可以上传 + boolean canUpload = !multipartFile.isEmpty() && size <= maxSize && Pattern.compile(SketchApplication + .settings.getStringUseEval(ConfigConsts.FILE_SUFFIX_MATCH_OF_SETTING)).matcher(suffix).matches() + && (Checker.isNotExists(localUrl) || !fileExists || SketchApplication.settings.getBooleanUseEval + (ConfigConsts.FILE_COVER_OF_SETTING)); + logger.info("is empty [" + multipartFile.isEmpty() + "], file size [" + size + "], max file size [" + + maxSize + "]"); + if (canUpload) { + String visitUrl = getRegularVisitUrl(Checker.isNotEmpty(prefix) && user.getPermission() > 1 ? prefix + : SketchApplication.settings.getStringUseEval(ConfigConsts.CUSTOM_LINK_RULE_OF_SETTING), user, + name, suffix, category); + if (fileExists) { + removeByLocalUrl(localUrl); + } + if (visitUrlExists(visitUrl)) { + removeByVisitUrl(visitUrl); + } + try { + multipartFile.transferTo(new java.io.File(localUrl)); + logger.info("local url of upload file: " + localUrl); + File file = new File(name, suffix, localUrl, visitUrl, WebUtils.scriptFilter(description), + WebUtils.scriptFilter(tag), user.getId(), categoryId); + int[] auth = SettingConfig.getAuth(ConfigConsts.FILE_DEFAULT_AUTH_OF_SETTING); + file.setAuth(auth[0], auth[1], auth[2], auth[3], auth[4]); + boolean isSuccess = fileDAO.insertFile(file); + if (isSuccess) { + long fileId = fileDAO.getIdByLocalUrl(localUrl); + if (fileId > 0) { + authService.insertDefaultAuth(user.getId(), fileId); + } + } else { + FileExecutor.deleteFile(localUrl); + } + return isSuccess; + } catch (Exception e) { + FileExecutor.deleteFile(localUrl); + logger.error("save file error: " + e.getMessage()); + } + } + } + return false; + } + + private String getRegularVisitUrl(String customUrl, User user, String fileName, String suffix, Category category) { + Date date = new Date(); + suffix = suffix.startsWith(".") ? "" : "." + suffix; + if (Checker.isNotEmpty(customUrl)) { + try { + customUrl = URLDecoder.decode(customUrl, "utf-8"); + } catch (UnsupportedEncodingException e) { + logger.error(e.getMessage()); + } + } + if (!customUrl.contains(FILE_NAME) && !customUrl.contains(RANDOM_ID)) { + customUrl += (customUrl.endsWith("/") ? "" : "/") + fileName; + } + customUrl = customUrl.replace(YEAR, DateUtils.getYear(date)).replace(MONTH, DateUtils.getMonth(date)).replace + (DAY, DateUtils.getDay(date)).replace(AUTHOR, user.getUsername()).replace(FILE_NAME, fileName) + .replace(CATEGORY_NAME, Checker.checkNull(Checker.isNull(category) ? "uncategorized" : category + .getName())).replace(RANDOM_ID, String.valueOf(RandomUtils.getRandomInteger(ValueConsts + .NINE_INT))).replace(FILE_SUFFIX, suffix); + return "/file" + (customUrl.startsWith("/") ? "" : "/") + customUrl; + } + + @Override + @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor = + Exception.class) + public boolean shareFiles(String prefix, String files, User user) { + if (Checker.isNotEmpty(files)) { + String[] paths = files.split(ValueConsts.COMMA_SIGN); + for (String path : paths) { + java.io.File f = new java.io.File(path); + String name = f.getName(); + String suffix = FileExecutor.getFileSuffix(name); + String visitUrl = getRegularVisitUrl(prefix, user, name, suffix, null); + if (f.exists() && f.isFile() && !localUrlExists(path) && !visitUrlExists(visitUrl)) { + File file = new File(name, suffix, path, visitUrl, ValueConsts.EMPTY_STRING, + ValueConsts.EMPTY_STRING, user.getId(), + categoryService.getIdByName(DefaultValues.UNCATEGORIZED)); + file.setAuth(ValueConsts.ONE_INT, ValueConsts.ZERO_INT, ValueConsts.ZERO_INT, + ValueConsts.ZERO_INT, ValueConsts.ONE_INT); + fileDAO.insertFile(file); + } + } + } + return true; + } + + @Override + public boolean localUrlExists(String localUrl) { + return fileDAO.checkLocalUrl(localUrl) > 0; + } + + @Override + public boolean visitUrlExists(String visitUrl) { + return fileDAO.checkVisitUrl(visitUrl) > 0; + } + + @Override + public long getFileId(String localUrl) { + try { + return fileDAO.getIdByLocalUrl(localUrl); + } catch (Exception e) { + return 0; + } + } + + @SuppressWarnings("unchecked") + @Override + public List listBasicAll(String user, String file, String category, int offset) { + return (List) ServiceUtils.invokeFileFilter(fileDAO, "listBasicBy", user, file, category, + offset); + } +} diff --git a/src/main/java/com/mesasoft/cn/service/impl/UploadedServiceImpl.java b/src/main/java/com/mesasoft/cn/service/impl/UploadedServiceImpl.java new file mode 100644 index 0000000..a02afff --- /dev/null +++ b/src/main/java/com/mesasoft/cn/service/impl/UploadedServiceImpl.java @@ -0,0 +1,30 @@ +package com.mesasoft.cn.service.impl; + +import com.mesasoft.cn.dao.UploadedDAO; +import com.mesasoft.cn.model.UploadedRecord; +import com.mesasoft.cn.service.IUploadedService; +import com.mesasoft.cn.util.ServiceUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @author pantao + * @since 2018/2/28 + */ +@Service +public class UploadedServiceImpl implements IUploadedService { + + private final UploadedDAO uploadedDAO; + + @Autowired + public UploadedServiceImpl(UploadedDAO uploadedDAO) {this.uploadedDAO = uploadedDAO;} + + @SuppressWarnings("unchecked") + @Override + public List list(String user, String file, String category, int offset) { + return (List) ServiceUtils.invokeFileFilter(uploadedDAO, "listUploadedBy", user, file, + category, offset); + } +} diff --git a/src/main/java/com/mesasoft/cn/service/impl/UserServiceImpl.java b/src/main/java/com/mesasoft/cn/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..8974f95 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/service/impl/UserServiceImpl.java @@ -0,0 +1,163 @@ +package com.mesasoft.cn.service.impl; + +import com.mesasoft.cn.SketchApplication; +import com.mesasoft.cn.dao.UserDAO; +import com.mesasoft.cn.util.BeanUtils; +import com.mesasoft.cn.config.SettingConfig; +import com.mesasoft.cn.config.TokenConfig; +import com.mesasoft.cn.entity.User; +import com.mesasoft.cn.modules.constant.ConfigConsts; +import com.mesasoft.cn.service.IUserService; +import com.zhazhapan.modules.constant.ValueConsts; +import com.zhazhapan.util.Checker; +import com.zhazhapan.util.DateUtils; +import com.zhazhapan.util.MailSender; +import org.apache.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; +import java.util.List; +import java.util.regex.Pattern; + +/** + * @author pantao + * @since 2018/1/22 + */ +@Service +public class UserServiceImpl implements IUserService { + + private final UserDAO userDAO; + + private Logger logger = Logger.getLogger(UserServiceImpl.class); + + @Autowired + public UserServiceImpl(UserDAO userDAO) {this.userDAO = userDAO;} + + @Override + public boolean updatePermission(int id, int permission) { + return userDAO.updatePermission(id, permission > 2 ? 2 : permission); + } + + @Override + public boolean resetPassword(int id, String password) { + boolean result = Checker.isNotEmpty(password) && userDAO.updatePasswordById(id, password); + if (result) { + TokenConfig.removeTokenByValue(id); + try { + MailSender.sendMail(getUserById(id).getEmail(), "密码重置通知", "您的密码已被管理员重置为:" + password); + } catch (Exception e) { + logger.error(e.getMessage()); + } + } + return result; + } + + @Override + public boolean updateFileAuth(int id, String auths) { + int[] auth = BeanUtils.getAuth(auths); + return userDAO.updateAuthById(id, auth[0], auth[1], auth[2], auth[3], auth[4]); + } + + @Override + public List listUser(int permission, String condition, int offset) { + return userDAO.listUserBy(permission, condition, offset); + } + + @Override + public User login(String loginName, String password, String token, HttpServletResponse response) { + boolean allowLogin = SketchApplication.settings.getBooleanUseEval(ConfigConsts.ALLOW_LOGIN_OF_SETTINGS); + User user = null; + if (allowLogin) { + if (Checker.isNotEmpty(token) && SketchApplication.tokens.containsKey(token)) { + user = userDAO.getUserById(SketchApplication.tokens.get(token)); + if (Checker.isNotNull(response)) { + Cookie cookie = new Cookie(ValueConsts.TOKEN_STRING, TokenConfig.generateToken(token, user.getId + ())); + cookie.setMaxAge(30 * 24 * 60 * 60); + response.addCookie(cookie); + } + } + if (Checker.isNull(user) && Checker.isNotEmpty(loginName) && Checker.isNotEmpty(password)) { + user = userDAO.login(loginName, password); + if (Checker.isNotNull(user)) { + TokenConfig.removeTokenByValue(user.getId()); + } + } + updateUserLoginTime(user); + } + return user; + } + + @Override + public boolean register(String username, String email, String password) { + boolean allowRegister = SketchApplication.settings.getBooleanUseEval(ConfigConsts.ALLOW_REGISTER_OF_SETTINGS); + if (allowRegister) { + boolean isValid = Checker.isEmail(email) && checkPassword(password) && Pattern.compile(SketchApplication.settings + .getStringUseEval(ConfigConsts.USERNAME_PATTERN_OF_SETTINGS)).matcher(username).matches(); + if (isValid) { + User user = new User(username, ValueConsts.EMPTY_STRING, email, password); + int[] auth = SettingConfig.getAuth(ConfigConsts.USER_DEFAULT_AUTH_OF_SETTING); + user.setAuth(auth[0], auth[1], auth[2], auth[3], auth[4]); + return userDAO.insertUser(user); + } + } + return false; + } + + @Override + public boolean resetPasswordByEmail(String email, String password) { + return Checker.isEmail(email) && checkPassword(password) && userDAO.updatePasswordByEmail(password, email); + } + + @Override + public boolean checkPassword(String password) { + int min = SketchApplication.settings.getIntegerUseEval(ConfigConsts.PASSWORD_MIN_LENGTH_OF_SETTINGS); + int max = SketchApplication.settings.getIntegerUseEval(ConfigConsts.PASSWORD_MAX_LENGTH_OF_SETTINGS); + return Checker.isLimited(password, min, max); + } + + @Override + public boolean emailExists(String email) { + return Checker.isEmail(email) && userDAO.checkEmail(email) > 0; + } + + @Override + public boolean updateBasicInfoById(int id, String avatar, String realName, String email) { + return Checker.isEmail(email) && userDAO.updateBasicInfo(id, Checker.checkNull(avatar), Checker.checkNull + (realName), email); + } + + @Override + public int getUserId(String usernameOrEmail) { + try { + return userDAO.getUserId(Checker.checkNull(usernameOrEmail)); + } catch (Exception e) { + return Integer.MAX_VALUE; + } + } + + @Override + public boolean usernameExists(String username) { + return Checker.isNotEmpty(username) && userDAO.checkUsername(username) > 0; + } + + @Override + public User getUserById(int id) { + return userDAO.getUserById(id); + } + + @Override + public void updateUserLoginTime(User user) { + if (Checker.isNotNull(user)) { + user.setLastLoginTime(DateUtils.getCurrentTimestamp()); + userDAO.updateUserLoginTime(user.getId()); + } + } + + @Override + public boolean updatePasswordById(String password, int id) { + return checkPassword(password) && userDAO.updatePasswordById(id, password); + } +} diff --git a/src/main/java/com/mesasoft/cn/sketch/api/BrightCloud.java b/src/main/java/com/mesasoft/cn/sketch/api/BrightCloud.java new file mode 100644 index 0000000..3a51220 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/sketch/api/BrightCloud.java @@ -0,0 +1,193 @@ +package com.mesasoft.cn.sketch.api; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.mesasoft.cn.sketch.config.ApplicationConfig; +import com.mesasoft.cn.sketch.entity.DomainCategory; +import com.mesasoft.cn.util.FileUtils; +import com.mesasoft.cn.util.ValidationUtils; +import org.apache.log4j.Logger; + +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.*; + +/** + * @author yjy + * @version 1.0 + * @date 2021/2/22 2:37 下午 + */ + +public class BrightCloud { + private static final Logger LOG = Logger.getLogger(BrightCloud.class); + + private final Integer maxObjectNum = ApplicationConfig.API_BC_MAXIMUM_QUERYNUM; + private final HashMap> catId2Info = new HashMap<>(); + private HttpURLConnection con; + + public List getQueryFiles(List domains){ + JSONObject queryResults = getQueryResults(domains); + return responseSparse(queryResults); + } + + // 获取json格式查询结果 + public JSONObject getQueryResults(List domains) { + if (domains.size()> ApplicationConfig.API_BC_MAXIMUM_QUERYNUM){ + LOG.warn("Too many domains in a http post request, the number of fqdn/url should be no more than " + + ApplicationConfig.API_BC_MAXIMUM_QUERYNUM + "!"); + } + JSONObject jsonRes = new JSONObject(); + try { + URL url = new URL(ApplicationConfig.API_BC_URL); + con = (HttpURLConnection) url.openConnection(); + con.setRequestMethod(ApplicationConfig.API_BC_METHOD); + con.setDoOutput(true); + con.setDoInput(true); + + con.setRequestProperty("Content-Type", "application/json"); + + JSONObject param = new JSONObject(); + param.put("oemid", ApplicationConfig.API_BC_OEMID); + param.put("deviceid", ApplicationConfig.API_BC_DEVICEID); + param.put("uid", ApplicationConfig.API_BC_UID); + + param.put("queries", Collections.singletonList(ApplicationConfig.API_BC_QUERYTYPE)); + param.put("a1cat", ApplicationConfig.API_BC_ISA1CAT); + param.put("reputation", ApplicationConfig.API_BC_ISREPU); + param.put("xml", ApplicationConfig.API_BC_ISXML); // json or xml格式 + + param.put("urls", domains); + + //建立实际的连接 + con.connect(); + OutputStreamWriter writer = new OutputStreamWriter(this.con.getOutputStream(), StandardCharsets.UTF_8); + writer.write(param.toString()); + writer.flush(); + } catch (IOException e) { + e.printStackTrace(); + } + + try { + // 获取服务端响应,通过输入流来读取URL的响应 + InputStream is = con.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); + StringBuilder sbf = new StringBuilder(); + String strRead = null; + while ((strRead = reader.readLine()) != null) { + sbf.append(strRead); + sbf.append("\r\n"); + } + reader.close(); + + jsonRes = JSONObject.parseObject(sbf.toString()); + con.disconnect(); + } catch (IOException e) { + e.printStackTrace(); + } + return jsonRes; + } + + // json响应内容解析 + public List responseSparse(JSONObject records){ + List domainFiles = new ArrayList<>(); + Boolean querySucess = records.get("status").equals(200); + + if (!querySucess) { + System.out.print(records); + LOG.error("Wrong query. Query type: " + records.get("type")); + } else { + JSONArray array = records.getJSONArray("results"); + for (int i = 0; i < array.size(); i++) { + JSONObject jo = array.getJSONObject(i); + + // json处理 + JSONObject queries = jo.getJSONObject("queries"); + JSONObject getInfo = queries.getJSONObject(ApplicationConfig.API_BC_QUERYTYPE); + + JSONObject cat = getInfo.getJSONArray("cats").getJSONObject(0); + Integer catId = cat.getInteger("catid"); + String fqdn = jo.getString("url"); + domainFiles.add(new DomainCategory( + fqdn, + "brightcloud", + querySucess, + ValidationUtils.getMatchPattern(fqdn), + getInfo.getInteger("reputation"), + getRepLevel(getInfo.getInteger("reputation")), + catId, + getCatInfo(catId).get(0), + getCatInfo(catId).get(1), + cat.getInteger("conf"), + getInfo.getBoolean("a1cat"))); + } + } + return domainFiles; + } + + private String getRepLevel(Integer repScore){ + String level = null; //用str存放数据 + if (repScore > 80){ level="Trustworthy";} + else if (repScore > 60){ level="Low Risk";} + else if (repScore > 40){ level="Moderate Risk";} + else if (repScore > 20){ level="Suspicious";} + else if (repScore > 0){ level="High Risk";} + return level; + } + + // 获取类别id对应信息 + public void geneCatInfo(){ + if (catId2Info.size()==0){ + JSONObject jsonObject; +// String filePath = Objects.requireNonNull(BrightCloud.class.getClassLoader() +// .getResource(ApplicationConfig.API_BC_CATEINFO_FILE)).getFile(); + String filePath =ApplicationConfig.API_BC_CATEINFO_FILE; + String s = FileUtils.readJsonFile(filePath); + jsonObject = JSON.parseObject(s); + + if (!(jsonObject==null)){ + JSONObject tmp = (JSONObject) jsonObject.getJSONArray("results").get(0); + JSONArray catInfoArray = tmp.getJSONObject("queries").getJSONObject("getcatlist").getJSONArray("cats"); + + for (int i = 0; i < catInfoArray.size(); i++){ + JSONObject keyObject = catInfoArray.getJSONObject(i); + List value = new ArrayList<>(Arrays.asList( + keyObject.getString("catname"), + keyObject.getString("catgroup"))); + catId2Info.put(i+1, value); + } + } + } + } + + public List getCatInfo(Integer catId){ + List info = Arrays.asList("", ""); + + if (0 < catId && catId <= 83) { + if (catId2Info.size()==0){ + geneCatInfo(); + } + + info = catId2Info.get(catId); + + if (info == null){ + LOG.error("Failed at geneCatInfo function"); + System.out.print("Failed at geneCatInfo function"); + } + } + + return info; + } + + public Integer getMaxObjectNum() { + return maxObjectNum; + } + + public static void main(String[] args) { + JSONObject queryResults = new BrightCloud().getQueryResults(Arrays.asList("baidu.com")); + new BrightCloud().responseSparse(queryResults); + } +} + diff --git a/src/main/java/com/mesasoft/cn/sketch/api/ChinaZ.java b/src/main/java/com/mesasoft/cn/sketch/api/ChinaZ.java new file mode 100644 index 0000000..1c75093 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/sketch/api/ChinaZ.java @@ -0,0 +1,336 @@ +package com.mesasoft.cn.sketch.api; +/* + * @Description: + * @Author: chenxu + * @Date: 2021-12-27 13:59:29 + * @LastEditTime: 2021-12-29 17:05:45 + * @LastEditors: chenxu + */ + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.mesasoft.cn.sketch.config.ApplicationConfig; +import com.mesasoft.cn.sketch.entity.DomainWhois; +import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.message.BasicNameValuePair; +import org.apache.log4j.Logger; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.sql.Date; +import java.util.*; +import java.util.concurrent.TimeUnit; + +public class ChinaZ { + private static final Logger LOG = Logger.getLogger(ChinaZ.class); + private final String apiKey = ApplicationConfig.API_CHINAZ_KEY; + + public List getQueryFiles(List objectList) { + List queryResults = getQueryResults(objectList); + return responseSparse(queryResults); + } + + /** + * @Description: 站长之家单查询 + * @Param : 域名 + * @Return: 查询结果 + */ + public JSONObject getQueryResult(String domain){ + String urlString = ApplicationConfig.API_CHINAZ_URL_SINGLE; + Map params = new LinkedHashMap(); + params.put("key", apiKey); + params.put("domain", domain); + return whoisInfoResolve(doPost(urlString, params),domain); + } + + /** + * @Description: 站长之家多域名查询 + * @Param : 域名 + * @Return: 查询结果 + */ + public List getQueryResults(List domainList){ + String urlString = ApplicationConfig.API_CHINAZ_URL_SINGLE; + List whoisInfoList = new ArrayList<>(); + for (String s : domainList) { + Map params = new LinkedHashMap(); + params.put("key", apiKey); + params.put("domain", s); + JSONObject r = doPost(urlString, params); + whoisInfoList.add(whoisInfoResolve(r, s)); + } + return whoisInfoList; + } + + public List responseSparse(List records){ + List whoisFiles = new ArrayList<>(); + + for(JSONObject record: records) { + Boolean querySucess = record.getBoolean("isSuccess"); + if (!querySucess) { + LOG.error("Failed query. Query response: " + record); + break; + } + + String fqdn = record.getString("domain_name"); + String domainName = record.getString("domain_host"); + Integer matchPattern = fqdn.equals(domainName)? 1 : 2 ; + String source = "chinaz"; + + // json处理 + Date creatDate = null; + Date expiraDate = null; + java.util.Date tmpDate = record.getDate("domain_whois_create_time"); + if(tmpDate!=null){ + creatDate = new Date(tmpDate.getTime()); + } + tmpDate = record.getDate("domain_whois_expiration_time"); + if(tmpDate!=null){ + expiraDate = new Date(tmpDate.getTime()); + } + whoisFiles.add(new DomainWhois( + fqdn, + source, + matchPattern, + record.getBoolean("isSuccess"), + record.getString("domain_host"), + null, + creatDate, + expiraDate, + record.getString("domain_whois_email"), + record.getString("domain_whois_name_servers"), + record.getString("domain_whois_registrar"), + null, + null, + null, + null, + null, + null, + null, + record.getString("domain_whois_phone") + )); + } + return whoisFiles; + } + /** + * @Description: 解析并重构JSON串 + * @Param : 查询得到的“单个”JSON串 + * @Return: 返回重构的JSON串 + */ + public JSONObject whoisInfoResolve(JSONObject jsonRes,String queryDomain){ + JSONObject whoisInfo = new JSONObject(true); + JSONObject res = jsonRes.getJSONObject("Result"); + if(jsonRes.get("StateCode").equals(1)){ + whoisInfo.put("isSuccess", jsonRes.get("StateCode")); + whoisInfo.put("domain_name",queryDomain); + whoisInfo.put("domain_host", res.get("Host")); + whoisInfo.put("domain_whois_create_time", res.get("CreationDate")); + whoisInfo.put("domain_whois_expiration_time", res.get("ExpirationDate")); + whoisInfo.put("domain_whois_registrar", res.get("Registrar")); + whoisInfo.put("whois_registrar_name", res.get("ContactPerson")); + whoisInfo.put("domain_whois_email", res.get("Email")); + whoisInfo.put("domain_whois_phone", res.get("Phone")); + whoisInfo.put("domain_whois_name_servers", res.get("DnsServer")); + whoisInfo.put("domain_whois_status", res.get("DomainStatus")); + }else{ + whoisInfo.put("isSuccess", jsonRes.get("StateCode")); + } + return whoisInfo; + } + + /** + * @Description: 构造批量查询需要的URL + * @Param : 待查询域名 + * @Return: 拼接好的URL + */ + public List queryStringBuilder(List domainList){ + //将域名每50个划分一组 + int CHINAZ_REQUEST_LIMIT = 50 ; + int domainListSize = domainList.size(); + int toIndex = CHINAZ_REQUEST_LIMIT; + Map domainListMap = new HashMap(); + int keyToken = 0; + for(int i = 0;idomainListSize){ //作用为toIndex最后没有50条数据则剩余几条newList中就装几条 + toIndex=domainListSize-i; + } + List newList = domainList.subList(i,i+toIndex); + domainListMap.put("keyName"+keyToken, newList); + keyToken++; + } + //将批量查询的域名,构造成CHINAZ的格式 + List domainListString = new ArrayList<>(); + Iterator iter = domainListMap.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = (Map.Entry) iter.next(); + Object key = entry.getKey(); + Object val = entry.getValue(); + String urlString = ""; + urlString = String.valueOf(val); + urlString = urlString.replace(", ","|"); + urlString =urlString.replace("[","").replace("]",""); + domainListString.add(urlString); + } + return domainListString; + } + + /** + * @Description: 站长之家批量查询-批量提交查询请求,并获得提取任务ID + * @Param : 域名集合(不能超过50个) + * @Return: 查询结果 + */ + public String batchRequest_step1(String domainsString){ + String TaskID = ""; + String urlString = ApplicationConfig.API_CHINAZ_URL_BATCH; + + Map params = new LinkedHashMap(); + if (!Objects.equals(domainsString, "overflow")){ + params.put("domains",domainsString); + params.put("key", apiKey); + JSONObject r = doPost(urlString, params); + TaskID = r.get("TaskID").toString(); + return TaskID; + }else{ + return TaskID; + } + + } + + /** + * @Description: 站长之家查询-根据提取任务ID查询数据是否采集完成,如果完成则得到Json格式结果 + * @Param : 任务ID + * @Return: 查询结果 + */ + public JSONObject batchRequest_step2(String TaskID){ + String urlString = ApplicationConfig.API_CHINAZ_URL_BATCH; + + Map params = new LinkedHashMap(); + params.put("taskid",TaskID); + JSONObject requestTotal = null; + requestTotal = doPost(urlString, params); + return requestTotal; + } + + /** + * @Description: 完成如下内容:1)将domain拼接成50个一组;2)调用step_1的API上传数据;3)调用step_2的API获取数据;4)格式整理,输出数据 + * @Param : 域名列表 + * @Return: whois记录列表 + */ + public List batchRequestController(List domainList){ + + List result = new ArrayList<>(); + if (domainList.size()> 5000){ + System.out.println("Too many urls in a http post request!"); + } + List domainListString = new ArrayList<>(); + List TaskID = new ArrayList<>(); + + + // Queue queue = new LinkedList(); + domainListString = queryStringBuilder(domainList); + //循环发送请求,收集每个请求的TaskID + for (String domainParam : domainListString) { + TaskID.add(batchRequest_step1(domainParam)); + } + + for (String s : TaskID) { + int flag = 0; + //查询接口数据,如果API仍在查询中,则等待10秒继续访问 + while (flag == 0) { + JSONObject data = batchRequest_step2(s); + if (data.get("StateCode").equals(0)) { + long timeToSleep = 10L; + TimeUnit time = TimeUnit.SECONDS; + try { + time.sleep(timeToSleep); + } catch (InterruptedException e) { + System.out.println("Interrupted " + "while Sleeping"); + } + } else { + flag = 1; + + JSONObject json_result = data.getJSONObject("Result"); + + JSONArray json_data = json_result.getJSONArray("Data"); + // //对Data内部的数据进行遍历 + for (int j = 0; j < json_data.size(); j++) { + String queryDomain = (String) json_data.getJSONObject(j).get("Domain"); + result.add(whoisInfoResolve(json_data.getJSONObject(j), queryDomain).toJSONString()); + } + } + } + } + + return result; + } + + /** + * @Description: POST调用API数据 + * @Param : 请求对URL,POST请求体需要添加的 k-v 数据 + * @Return: API JSON数据 + */ + public JSONObject doPost(String url, Map params){ + JSONObject jsonRes = null; + try { + // 定义HttpClient + CloseableHttpClient client = HttpClients.createDefault(); + // 实例化HTTP方法 + HttpPost request = new HttpPost(); + request.setURI(new URI(url)); + + //设置参数 + List nvps = new ArrayList(); + for (Object o : params.keySet()) { + String name = (String) o; + String value = String.valueOf(params.get(name)); + nvps.add(new BasicNameValuePair(name, value)); + + //System.out.println(name +"-"+value); + } + request.setEntity(new UrlEncodedFormEntity(nvps)); + //发送请求 + HttpResponse httpResponse = client.execute(request); + // 获取响应输入流 + InputStream inStream = httpResponse.getEntity().getContent(); + //对放回数据进行处理 + BufferedReader reader = new BufferedReader(new InputStreamReader(inStream , StandardCharsets.UTF_8)); + StringBuilder strber = new StringBuilder(); + StringBuilder sbf = new StringBuilder(); + String strRead = null; + while ((strRead = reader.readLine()) != null) { + sbf.append(strRead); + sbf.append("\r\n"); + } + // 关闭输入流 + inStream.close(); + jsonRes = JSONObject.parseObject(sbf.toString()); + }catch (Exception e) { + System.out.println("请求接口异常"); + } + return jsonRes; + } + + + public static void main(String[] args){ + ChinaZ t = new ChinaZ(); + + //单查询测试 +// System.out.println(t.singleRequest("aaa.baidu.com")); + + //批量查询测试 + List domainList = new ArrayList<>(); + domainList.add("www.baidu.com"); +// domainList.add("aaa.qq.com"); +// domainList.add("doc.mesalab.com"); + +// System.out.println(t.batchRequestController(domainList)); + System.out.println(t.getQueryResults(domainList)); + } +} diff --git a/src/main/java/com/mesasoft/cn/sketch/config/ApplicationConfig.java b/src/main/java/com/mesasoft/cn/sketch/config/ApplicationConfig.java new file mode 100644 index 0000000..099205e --- /dev/null +++ b/src/main/java/com/mesasoft/cn/sketch/config/ApplicationConfig.java @@ -0,0 +1,54 @@ +package com.mesasoft.cn.sketch.config; + + +import com.mesasoft.cn.util.ConfigUtils; + +public class ApplicationConfig { + public static final String QUERY_OUTPUT_DIR = ConfigUtils.getStringProperty("query.output.dir"); + + public static final String QUERY_TYPES_DOMAIN = ConfigUtils.getStringProperty("query.types.domain"); + public static final String QUERY_TYPES_IP = ConfigUtils.getStringProperty("query.types.ip"); + + public static final Integer UPDATE_EXPIRED_DAY = ConfigUtils.getIntProperty("update.expired.day"); // 更新任务中过期时间长度(天数) + + public static final Integer QUERY_READIN_BATCH = ConfigUtils.getIntProperty("query.readin.batch"); + public static final Integer QUERY_LOG_FILE_LINE_INTERVAL = ConfigUtils.getIntProperty("query.log.file.line.interval"); // 文件查询时,打印log的读取行数间隔 + + // api参数 + // brightcloud + public static final String API_BC_OEMID = ConfigUtils.getStringProperty("bc.oemid"); + public static final String API_BC_DEVICEID = ConfigUtils.getStringProperty("bc.deviceid"); + public static final String API_BC_UID = ConfigUtils.getStringProperty("bc.uid"); + public static final String API_BC_URL = ConfigUtils.getStringProperty("bc.url"); + public static final String API_BC_METHOD = ConfigUtils.getStringProperty("bc.method"); + + public static final String API_BC_ISA1CAT = ConfigUtils.getStringProperty("bc.isa1cat"); + public static final String API_BC_ISREPU = ConfigUtils.getStringProperty("bc.isReputation"); + public static final String API_BC_ISXML = ConfigUtils.getStringProperty("bc.isxml"); + + + public static final Integer API_BC_MAXIMUM_QUERYNUM = ConfigUtils.getIntProperty("bc.maximum.query.num"); // brightcloud单次查询条数上线 + public static final String API_BC_QUERYTYPE = ConfigUtils.getStringProperty("bc.queryType"); + + public static final String API_BC_USE_REPORT_FILE = ConfigUtils.getStringProperty("bc.usereport.filepath"); // brightcloud使用报告导出文件目录 + public static final String API_BC_CATEINFO_FILE = ConfigUtils.getStringProperty("bc.cateinfo.filepath"); // brightcloud使用报告导出文件目录 + + + // chinaz + public static final String API_CHINAZ_URL_SINGLE = ConfigUtils.getStringProperty("chinaz.url.single"); + public static final String API_CHINAZ_URL_BATCH = ConfigUtils.getStringProperty("chinaz.url.batch"); + public static final String API_CHINAZ_KEY = ConfigUtils.getStringProperty("chinaz.key"); + public static final Integer API_CHINAZ_MAXIMUM_QUERYNUM = ConfigUtils.getIntProperty("chinaz.maximum.query.num"); + public static final String API_CHINAZ_USE_REPORT_FILE = ConfigUtils.getStringProperty("chinaz.usereport.filepath"); + + // Mariadb + public static final String DATABASE = ConfigUtils.getStringProperty("database"); + public static final String DOMAIN_CATE_TABLENAME = ConfigUtils.getStringProperty("tablename.domain.category"); + public static final String DOMAIN_WHOIS_TABLENAME = ConfigUtils.getStringProperty("tablename.domain.whois"); + public static final String DNS_SERVER_TABLENAME = ConfigUtils.getStringProperty("tablename.dns.server"); + public static final Integer DB_QUERY_BATCH_SIZE = ConfigUtils.getIntProperty("db.query.batch.size"); + + // 其他 + public static final String TLD_FILE = ConfigUtils.getStringProperty("tld.file"); // 顶级域名公开列表文件 + +} diff --git a/src/main/java/com/mesasoft/cn/sketch/config/MariaDbBase.java b/src/main/java/com/mesasoft/cn/sketch/config/MariaDbBase.java new file mode 100644 index 0000000..ba617ce --- /dev/null +++ b/src/main/java/com/mesasoft/cn/sketch/config/MariaDbBase.java @@ -0,0 +1,84 @@ +package com.mesasoft.cn.sketch.config; + +import com.mesasoft.cn.util.TimeUtils; +import org.apache.log4j.Logger; + +import java.sql.*; +import java.util.Date; +import java.util.Properties; + +/** + * Created with IntelliJ IDEA. + * User: joy + * Date: 2021/12/28 + * Time: 2:56 PM + * Description: No Description + */ +public class MariaDbBase { + + private static final Logger LOG = Logger.getLogger(MariaDbBase.class); + private static final Properties props = new Properties(); + + private final Statement statement; + + public MariaDbBase(Connection conn, Statement stat) { + statement = stat; + } + + /** + * 执行写入sql + */ + public void writeSqlExecute(String sql){ + try { + statement.executeUpdate(sql); + } catch (SQLIntegrityConstraintViolationException e){ + LOG.error("Duplicated entry for key 'PRIMARY'"); + } catch (SQLException exception) { + LOG.error("Sql : " + sql); + exception.printStackTrace(); + } + } + + /** + * 执行查询sql + */ + public ResultSet querySqlExecute(String sql){ + ResultSet set = null; + try { + set = statement.executeQuery(sql); + } catch (SQLException exception) { + exception.printStackTrace(); + } + return set; + } + + + /** + * 获得指定表格、按指定时间字段的过期记录 + * @param tableName 库表名称 + * @param timeColumnName 时间列名 + * @return 查询结果 + */ + public ResultSet getExpiredRecord(String tableName, String timeColumnName){ + Date lastUpdateTime = new Timestamp(getExpiredTime(ApplicationConfig.UPDATE_EXPIRED_DAY).getTime()); + + String resSql = "SELECT *" + + " FROM " + ApplicationConfig.DATABASE + "." + tableName + + " WHERE " + timeColumnName + " < '" + lastUpdateTime + '\''; + + LOG.debug("Update task: expired query sql" + resSql); + + return querySqlExecute(resSql); + } + + /** + * TODO: getUnlabeledRecord() 考虑多个来源的情况 + */ + + /** + * 获得过期时间, 当前时间的expiredRangeDays天之前的日期为过期日期 + */ + public static Date getExpiredTime(int expiredRangeDays){ + return new Timestamp(TimeUtils.getStartOfDay(-expiredRangeDays).getTime()); + } +} diff --git a/src/main/java/com/mesasoft/cn/sketch/config/SketchDatabaseConfig.java b/src/main/java/com/mesasoft/cn/sketch/config/SketchDatabaseConfig.java new file mode 100644 index 0000000..dc69bbd --- /dev/null +++ b/src/main/java/com/mesasoft/cn/sketch/config/SketchDatabaseConfig.java @@ -0,0 +1,125 @@ +//package com.zhazhapan.efo.sketch.config; +// +//import com.alibaba.druid.pool.DruidDataSource; +//import lombok.Data; +//import org.apache.ibatis.session.SqlSessionFactory; +//import org.mybatis.spring.SqlSessionFactoryBean; +//import org.mybatis.spring.annotation.MapperScan; +//import org.springframework.beans.factory.annotation.Qualifier; +//import org.springframework.beans.factory.annotation.Value; +//import org.springframework.boot.context.properties.ConfigurationProperties; +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +//import org.springframework.jdbc.datasource.DataSourceTransactionManager; +// +//import javax.sql.DataSource; +//import java.sql.SQLException; +// +///** +// * @ProjectName +// * @Description: 后台数据源配置类 +// */ +//@Data +//@Configuration +//@ConfigurationProperties(prefix = "sketch.datasource.druid") +//@MapperScan(basePackages = SketchDatabaseConfig.PACKAGE, sqlSessionFactoryRef = "sketchSqlSessionFactory") +//public class SketchDatabaseConfig { +// /** +// * dao层的包路径 +// */ +// static final String PACKAGE = "com.mao.mysqlhive.demomh.mapper.sketch"; +// +// /** +// * mapper文件的相对路径 +// */ +// private static final String MAPPER_LOCATION = "classpath:mappers/sketch/*Mapper.xml"; +// +// @Value("${sketch.datasource.druid.filters}") +// private String filters; +// @Value("${sketch.datasource.druid.driverClassName}") +// private String url; +// @Value("${sketch.datasource.druid.url}") +// private String username; +// @Value("${sketch.datasource.druid.username}") +// private String password; +// @Value("${sketch.datasource.druid.password}") +// private String driverClassName; +// @Value("${sketch.datasource.druid.initialSize}") +// private int initialSize; +// @Value("${sketch.datasource.druid.minIdle}") +// private int minIdle; +// @Value("${sketch.datasource.druid.maxActive}") +// private int maxActive; +// @Value("${sketch.datasource.druid.maxWait}") +// private long maxWait; +// @Value("${sketch.datasource.druid.timeBetweenEvictionRunsMillis}") +// private long timeBetweenEvictionRunsMillis; +// @Value("${sketch.datasource.druid.minEvictableIdleTimeMillis}") +// private long minEvictableIdleTimeMillis; +// @Value("${sketch.datasource.druid.validationQuery}") +// private String validationQuery; +// @Value("${sketch.datasource.druid.testWhileIdle}") +// private boolean testWhileIdle; +// @Value("${sketch.datasource.druid.testOnBorrow}") +// private boolean testOnBorrow; +// @Value("${sketch.datasource.druid.testOnReturn}") +// private boolean testOnReturn; +// @Value("${sketch.datasource.druid.poolPreparedStatements}") +// private boolean poolPreparedStatements; +// @Value("${sketch.datasource.druid.maxPoolPreparedStatementPerConnectionSize}") +// private int maxPoolPreparedStatementPerConnectionSize; +// +// +// @Bean(name = "sketchDataSource") +// public DataSource sketchDataSource() throws SQLException { +// DruidDataSource druid = new DruidDataSource(); +// // 监控统计拦截的filters +// druid.setFilters(filters); +// +// // 配置基本属性 +// druid.setDriverClassName(driverClassName); +// druid.setUsername(username); +// druid.setPassword(password); +// druid.setUrl(url); +// +// //初始化时建立物理连接的个数 +// druid.setInitialSize(initialSize); +// //最大连接池数量 +// druid.setMaxActive(maxActive); +// //最小连接池数量 +// druid.setMinIdle(minIdle); +// //获取连接时最大等待时间,单位毫秒。 +// druid.setMaxWait(maxWait); +// //间隔多久进行一次检测,检测需要关闭的空闲连接 +// druid.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); +// //一个连接在池中最小生存的时间 +// druid.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); +// //用来检测连接是否有效的sql +// druid.setValidationQuery(validationQuery); +// //建议配置为true,不影响性能,并且保证安全性。 +// druid.setTestWhileIdle(testWhileIdle); +// //申请连接时执行validationQuery检测连接是否有效 +// druid.setTestOnBorrow(testOnBorrow); +// druid.setTestOnReturn(testOnReturn); +// //是否缓存preparedStatement,也就是PSCache,oracle设为true,mysql设为false。分库分表较多推荐设置为false +// druid.setPoolPreparedStatements(poolPreparedStatements); +// // 打开PSCache时,指定每个连接上PSCache的大小 +// druid.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize); +// return druid; +// } +// +// @Bean(name = "sketchTransactionManager") +// public DataSourceTransactionManager sketchTransactionManager() throws SQLException { +// return new DataSourceTransactionManager(sketchDataSource()); +// } +// +// @Bean(name = "sketchSqlSessionFactory") +// public SqlSessionFactory sketchSqlSessionFactory(@Qualifier("sketchDataSource") DataSource sketchDataSource) throws Exception { +// final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); +// sessionFactory.setDataSource(sketchDataSource); +// sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(SketchDatabaseConfig.MAPPER_LOCATION)); +// +// return sessionFactory.getObject(); +// } +//} diff --git a/src/main/java/com/mesasoft/cn/sketch/controller/SketchDomainController.java b/src/main/java/com/mesasoft/cn/sketch/controller/SketchDomainController.java new file mode 100644 index 0000000..736a5be --- /dev/null +++ b/src/main/java/com/mesasoft/cn/sketch/controller/SketchDomainController.java @@ -0,0 +1,54 @@ +package com.mesasoft.cn.sketch.controller; + +import com.alibaba.fastjson.JSONObject; +import com.mesasoft.cn.annotation.AuthInterceptor; +import com.mesasoft.cn.enums.InterceptorLevel; +import com.mesasoft.cn.service.IFileManagerService; +import io.swagger.annotations.Api; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; + +/** + * @description: + * @author: zhq + * @create: 2022-03-10 + **/ +@RestController +@RequestMapping("/sketch/domain") +@Api(value = "/sketch/domain", description = "文件相关操作") +@Slf4j +public class SketchDomainController { + + private final IFileManagerService fileManagerService; + private final HttpServletRequest request; + private JSONObject jsonObject; + + @Value("${sketch.path.admin}") + private String pathAdmin; + @Value("${sketch.path.user}") + private String pathUser; + + @Autowired + public SketchDomainController(HttpServletRequest request, IFileManagerService fileManagerService, JSONObject jsonObject) { + this.fileManagerService = fileManagerService; + this.jsonObject = jsonObject; + this.request = request; + } + + @AuthInterceptor(InterceptorLevel.USER) + @RequestMapping(value = "/list", method = RequestMethod.GET) + public String list(String searchPath) { + + log.info("search path :", searchPath); + + + return jsonObject.toJSONString(); + } + +} diff --git a/src/main/java/com/mesasoft/cn/sketch/controller/SketchFileController.java b/src/main/java/com/mesasoft/cn/sketch/controller/SketchFileController.java new file mode 100644 index 0000000..b9c9ba1 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/sketch/controller/SketchFileController.java @@ -0,0 +1,93 @@ +package com.mesasoft.cn.sketch.controller; + +import com.alibaba.fastjson.JSONObject; +import com.mesasoft.cn.annotation.AuthInterceptor; +import com.mesasoft.cn.entity.User; +import com.mesasoft.cn.enums.InterceptorLevel; +import com.mesasoft.cn.service.IFileManagerService; +import com.mesasoft.cn.util.ControllerUtils; +import com.zhazhapan.modules.constant.ValueConsts; +import com.zhazhapan.util.ArrayUtils; +import io.swagger.annotations.Api; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.MultipartHttpServletRequest; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Map; + +/** + * @description: + * @author: zhq + * @create: 2022-03-10 + **/ +@RestController +@RequestMapping("/sketch") +@Api(value = "/sketch", description = "文件相关操作") +@Slf4j +public class SketchFileController { + + private final IFileManagerService fileManagerService; + private final HttpServletRequest request; + private JSONObject jsonObject; + + @Value("${sketch.path.admin}") + private String pathAdmin; + @Value("${sketch.path.user}") + private String pathUser; + + @Autowired + public SketchFileController(HttpServletRequest request, IFileManagerService fileManagerService, JSONObject jsonObject) { + this.fileManagerService = fileManagerService; + this.jsonObject = jsonObject; + this.request = request; + } + + @AuthInterceptor(InterceptorLevel.USER) + @RequestMapping(value = "/list", method = RequestMethod.GET) + public String list(String searchPath) { + + log.info("search path :", searchPath); + + User user = (User) request.getSession().getAttribute(ValueConsts.USER_STRING); + JSONObject json = new JSONObject(); + String rootPath = ""; + if ("system".equals(user.getUsername())) { + rootPath = pathAdmin; + } else { + rootPath = pathUser; + } + searchPath = searchPath.replace("\\\\", ""); + searchPath = searchPath.replace("//", ""); + if (StringUtils.isNotBlank(searchPath) && !rootPath.contains(searchPath)) { + json.fluentPut("path", searchPath); + } else { + json.fluentPut("path", rootPath); + } + //返回结果 + jsonObject.put("path", json.getString("path")); + jsonObject.put("result", fileManagerService.list(json)); + return jsonObject.toJSONString(); + } + + @RequestMapping(value = "/upload", method = RequestMethod.POST) + public String upload(String destination, MultipartHttpServletRequest request) { + Map fileMap = request.getFileMap(); + MultipartFile[] files = ArrayUtils.mapToArray(fileMap, MultipartFile.class); + jsonObject.put("result", fileManagerService.upload(destination, files)); + return jsonObject.toJSONString(); + } + + @RequestMapping(value = "/download", method = RequestMethod.GET) + public void download(HttpServletRequest request, HttpServletResponse response, String path) throws IOException, ClassNotFoundException { + ControllerUtils.loadResource2(response, path, ValueConsts.TRUE); + } +} diff --git a/src/main/java/com/mesasoft/cn/sketch/dao/SketchDAO.java b/src/main/java/com/mesasoft/cn/sketch/dao/SketchDAO.java new file mode 100644 index 0000000..0473403 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/sketch/dao/SketchDAO.java @@ -0,0 +1,81 @@ +package com.mesasoft.cn.sketch.dao; + +import com.mesasoft.cn.sketch.dao.sqlprovider.SketchSqlProvider; +import com.mesasoft.cn.sketch.entity.DomainCategory; +import com.mesasoft.cn.dao.sqlprovider.UserSqlProvider; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface SketchDAO { + + /** + * 通过域名查询 + */ + @Select("select * from domain_category_reputation where fqdn=#{fqdn}") + DomainCategory getDomainCategoryByFqdn(String usernameOrEmail); + + /** + * 获取列表 + * + * @return {@link List} + */ + @SelectProvider(type = SketchSqlProvider.class, method = "getDomainCategoryListBy") + List getDomainCategoryList(@Param("condition") String condition, @Param("offset") int offset); + + + /** + * 添加一个用户 + * + * @param domainCategory {@link DomainCategory} + * @return 是否插入成功 + */ + @Insert("insert into domain_category_reputation(username,real_name,email,password,is_downloadable,is_uploadable,is_deletable," + + "is_updatable,is_visible) values(#{username},#{realName},#{email},sha2(#{password},256)," + + "#{isDownloadable},#{isUploadable},#{isDeletable},#{isUpdatable},#{isVisible})") + boolean insertDomainCategory(DomainCategory domainCategory); + + /** + * 通过ID更新用户基本信息 + * + * @param id 编号 + * @param avatar 头像 + * @param realName 真实姓名 + * @param email 邮箱 + * @return 是否更新成功 + */ + @Update("update user set avatar=#{avatar},real_name=#{realName},email=#{email} where id=#{id}") + boolean updateBasicInfo(@Param("id") int id, @Param("avatar") String avatar, @Param("realName") String realName, + @Param("email") String email); + + + + /** + * 通过id更新用户登录时间 + * + * @param id 编号 + * @return {@link Boolean} + */ + @Update("update user set last_login_time=current_timestamp where id=#{id}") + boolean updateUserLoginTime(int id); + + /** + * 更新操作用户权限 + * + * @param id 用户编号 + * @param isDownloadable 下载权限 + * @param isUploadable 上传权限 + * @param isVisible 可查权限 + * @param isDeletable 删除权限 + * @param isUpdatable 更新权限 + * @return {@link Boolean} + */ + @UpdateProvider(type = UserSqlProvider.class, method = "updateAuthById") + boolean updateAuthById(@Param("id") int id, @Param("isDownloadable") int isDownloadable, + @Param("isUploadable") int isUploadable, @Param("isDeletable") int isDeletable, @Param( + "isUpdatable") int isUpdatable, @Param("isVisible") int isVisible); + + +} diff --git a/src/main/java/com/mesasoft/cn/sketch/dao/sqlprovider/SketchSqlProvider.java b/src/main/java/com/mesasoft/cn/sketch/dao/sqlprovider/SketchSqlProvider.java new file mode 100644 index 0000000..53e24a2 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/sketch/dao/sqlprovider/SketchSqlProvider.java @@ -0,0 +1,27 @@ +package com.mesasoft.cn.sketch.dao.sqlprovider; + +import com.mesasoft.cn.SketchApplication; +import com.mesasoft.cn.modules.constant.ConfigConsts; +import com.zhazhapan.util.Checker; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.jdbc.SQL; + +/** + * @author pantao + * @since 2018/1/19 + */ +public class SketchSqlProvider { + + public String getDomainCategoryListBy(@Param("condition") String condition, @Param("offset") int offset) { + String sql = new SQL() {{ + SELECT("*"); + FROM("domain_category_reputation"); + if (Checker.isNotEmpty(condition)) { + WHERE("fqdn like '%" + condition + "'"); + } + ORDER_BY(SketchApplication.settings.getStringUseEval(ConfigConsts.USER_ORDER_BY_OF_SETTINGS)); + }}.toString(); + int size = SketchApplication.settings.getIntegerUseEval(ConfigConsts.USER_PAGE_SIZE_OF_SETTINGS); + return sql + " limit " + (offset * size) + "," + size; + } +} diff --git a/src/main/java/com/mesasoft/cn/sketch/entity/DomainCategory.java b/src/main/java/com/mesasoft/cn/sketch/entity/DomainCategory.java new file mode 100644 index 0000000..dbcfdc1 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/sketch/entity/DomainCategory.java @@ -0,0 +1,340 @@ +package com.mesasoft.cn.sketch.entity; + +import com.alibaba.fastjson.JSONObject; +import com.mesasoft.cn.util.ConfigUtils; +import com.mesasoft.cn.sketch.config.ApplicationConfig; +import com.mesasoft.cn.util.MariaDbBase; +import com.mesasoft.cn.util.ValidationUtils; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Created with IntelliJ IDEA. + * User: joy + * Date: 2021/12/29 + * Time: 9:27 AM + * Description: No Description + */ +public class DomainCategory { + private static final String dataBase = ApplicationConfig.DATABASE; + private static final String tableName = ApplicationConfig.DOMAIN_CATE_TABLENAME; + + private String fqdn; + private String source; + private Boolean query_success; + private Integer match_pattern; + private Integer reputation_score; + private String reputation_level; + private Integer category_id; + private String category_name; + private String category_group; + private Integer category_conf; + private Boolean is_a1_cat; + private Integer status_code = 0; + private String submit_user ="''"; + + + // category schema + public DomainCategory(String fqdn, + String source, + Boolean query_success, + Integer match_pattern, + Integer reputation_score, + String reputationLevel, + Integer categoryId, + String categoryName, + String categoryGroup, + Integer categoryConf, + Boolean isA1Cat, Integer statusCode, String submitUser) { + + this.fqdn = fqdn; + this.source = source; // 默认应为为brightcloud + this.query_success = query_success; + + // 没有设置match_pattern,则二级域名为右匹配,其余为全匹配 + if (match_pattern == null) { + this.match_pattern = ValidationUtils.getMatchPattern(fqdn); + } else { + this.match_pattern = match_pattern; + } + + this.reputation_score = reputation_score; + this.reputation_level = ConfigUtils.getEffectiveString(reputationLevel); + this.category_id = categoryId; + this.category_name = ConfigUtils.getEffectiveString(categoryName); + this.category_group = ConfigUtils.getEffectiveString(categoryGroup); + this.category_conf = categoryConf; + this.is_a1_cat = isA1Cat; + this.status_code = statusCode; + this.submit_user = submitUser; + + } + public DomainCategory(String fqdn, + String source, + Boolean query_success, + Integer match_pattern, + Integer reputation_score, + String reputationLevel, + Integer categoryId, + String categoryName, + String categoryGroup, + Integer categoryConf, + Boolean isA1Cat) { + + this.fqdn = fqdn; + this.source = source; // 默认应为为brightcloud + this.query_success = query_success; + + // 没有设置match_pattern,则二级域名为右匹配,其余为全匹配 + if (match_pattern == null) { + this.match_pattern = ValidationUtils.getMatchPattern(fqdn); + } else { + this.match_pattern = match_pattern; + } + + this.reputation_score = reputation_score; + this.reputation_level = ConfigUtils.getEffectiveString(reputationLevel); + this.category_id = categoryId; + this.category_name = ConfigUtils.getEffectiveString(categoryName); + this.category_group = ConfigUtils.getEffectiveString(categoryGroup); + this.category_conf = categoryConf; + this.is_a1_cat = isA1Cat; + + } + public static void insertRecords(List categoryFiles, MariaDbBase mariaDbBase) { + for (DomainCategory categoryFile : categoryFiles) { + // 生成sql + String resSql = "INSERT INTO " + dataBase + "." + tableName + ' ' + + " (" + categoryFile.getKeys() + ") values" + + '(' + categoryFile.getValues() + ')'; + resSql = resSql.replace("'null'", "null"); + + mariaDbBase.writeSqlExecute(resSql); + } + } + + public void updateRecords(List categoryFiles, MariaDbBase mariaDbBase) { + for (DomainCategory categoryFile : categoryFiles) { + + String resSql = "UPDATE " + dataBase + "." + + tableName + ' ' + + "SET " + categoryFile.getKeyValues() + + ", update_time = current_time() " + + " WHERE fqdn = '" + categoryFile.getFqdn() + '\''; + resSql = resSql.replace("'null'", "null"); + + mariaDbBase.writeSqlExecute(resSql); + } + } + + public static List getDbRecord(List fqdns, MariaDbBase mariaDbBase, String source) throws SQLException { + String queryFqdns = fqdns.stream().map(s -> "'" + s + "'").collect(Collectors.joining(",")); + String sql = "SELECT * FROM " + dataBase + "." + + tableName + ' ' + + " WHERE fqdn in (" + queryFqdns + ") and source = '" + source + "'"; + + return rs2schema(mariaDbBase.querySqlExecute(sql)); + } + + public static List rs2schema(ResultSet rs) throws SQLException { + List schemaFiles = new ArrayList<>(); + while (rs.next()) { + schemaFiles.add( + new DomainCategory( + rs.getString("fqdn"), + rs.getString("source"), + rs.getBoolean("query_success"), + rs.getInt("match_pattern"), + rs.getInt("reputation_score"), + rs.getString("reputation_level"), + rs.getInt("category_id"), + rs.getString("category_name"), + rs.getString("category_group"), + rs.getInt("category_conf"), + rs.getBoolean("is_a1_cat"), + rs.getInt("status_code"), + rs.getString("submit_user") + + )); + } + return schemaFiles; + } + + public static JSONObject schema2json(DomainCategory schema) throws SQLException { + JSONObject jsonObject = new JSONObject(true); + jsonObject.put("fqdn", schema.getFqdn()); + jsonObject.put("source", schema.getSource()); + jsonObject.put("query_success", schema.getQuery_success()); + jsonObject.put("match_pattern", schema.getMatch_pattern()); + jsonObject.put("reputation_score", schema.getReputation_score()); + jsonObject.put("reputation_level", schema.getReputation_level()); + jsonObject.put("category_id", schema.getCategory_id()); + jsonObject.put("category_group", schema.getCategory_group()); + jsonObject.put("category_name", schema.getCategory_name()); + jsonObject.put("category_conf", schema.getCategory_conf()); + jsonObject.put("is_a1_cat", schema.getIs_a1_cat()); + jsonObject.put("status_code", schema.getStatus_code()); + jsonObject.put("submit_user", schema.getSubmit_user()); + + return jsonObject; + } + + public String getValues() { + String resString = "'" + fqdn + '\'' + + ", '" + source + '\'' + + ", " + query_success + + ", " + match_pattern + + ", " + reputation_score + + ", '" + reputation_level + '\'' + + ", " + category_id + + ", '" + category_name + '\'' + + ", '" + category_group + '\'' + + ", " + category_conf + + ", " + status_code + + ", " + submit_user + + ", " + is_a1_cat; + + return resString.replace("'null'", "null"); + } + + public String getKeys() { + String resString; + resString = "fqdn" + + ", source" + + ", query_success" + + ", match_pattern" + + ", reputation_score" + + ", reputation_level" + + ", category_id" + + ", category_name" + + ", category_group" + + ", category_conf" + + ", status_code" + + ", submit_user" + + ", is_a1_cat"; + return resString; + } + + public String getKeyValues() { + String resString = "source='" + source + '\'' + + ", query_success=" + query_success + + ", match_pattern=" + match_pattern + + ", reputation_score=" + reputation_score + + ", reputation_level='" + reputation_level + '\'' + + ", category_id=" + category_id + + ", category_name='" + category_name + '\'' + + ", category_group='" + category_group + '\'' + + ", category_conf=" + category_conf + + ", is_a1_cat=" + is_a1_cat; + + return resString.replace("'null'", "null"); + } + + public String getFqdn() { + return fqdn; + } + + public void setFqdn(String fqdn) { + this.fqdn = fqdn; + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } + + public Boolean getQuery_success() { + return query_success; + } + + public void setQuery_success(Boolean query_success) { + this.query_success = query_success; + } + + public Integer getMatch_pattern() { + return match_pattern; + } + + public void setMatch_pattern(Integer match_pattern) { + this.match_pattern = match_pattern; + } + + public Integer getReputation_score() { + return reputation_score; + } + + public void setReputation_score(Integer reputation_score) { + this.reputation_score = reputation_score; + } + + public String getReputation_level() { + return reputation_level; + } + + public void setReputation_level(String reputation_level) { + this.reputation_level = reputation_level; + } + + public Integer getCategory_id() { + return category_id; + } + + public void setCategory_id(Integer category_id) { + this.category_id = category_id; + } + + public String getCategory_name() { + return category_name; + } + + public void setCategory_name(String category_name) { + this.category_name = category_name; + } + + public String getCategory_group() { + return category_group; + } + + public void setCategory_group(String category_group) { + this.category_group = category_group; + } + + public Integer getCategory_conf() { + return category_conf; + } + + public void setCategory_conf(Integer category_conf) { + this.category_conf = category_conf; + } + + public Boolean getIs_a1_cat() { + return is_a1_cat; + } + + public void setIs_a1_cat(Boolean is_a1_cat) { + this.is_a1_cat = is_a1_cat; + } + + public Integer getStatus_code() { + return status_code; + } + + public void setStatus_code(Integer status_code) { + this.status_code = status_code; + } + + public String getSubmit_user() { + return submit_user; + } + + public void setSubmit_user(String submit_user) { + this.submit_user = submit_user; + } +} diff --git a/src/main/java/com/mesasoft/cn/sketch/entity/DomainWhois.java b/src/main/java/com/mesasoft/cn/sketch/entity/DomainWhois.java new file mode 100644 index 0000000..55679a8 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/sketch/entity/DomainWhois.java @@ -0,0 +1,423 @@ +package com.mesasoft.cn.sketch.entity; + +import com.alibaba.fastjson.JSONObject; +import com.mesasoft.cn.sketch.config.ApplicationConfig; +import com.mesasoft.cn.util.ConfigUtils; +import com.mesasoft.cn.util.MariaDbBase; +import com.mesasoft.cn.util.ValidationUtils; + +import java.sql.Date; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Created with IntelliJ IDEA. + * User: joy + * Date: 2021/12/29 + * Time: 9:27 AM + * Description: No Description + */ +public class DomainWhois { + private static final String dataBase = ApplicationConfig.DATABASE; + private static final String tableName = ApplicationConfig.DOMAIN_WHOIS_TABLENAME; + + private String fqdn; + private String source; + private Boolean query_success; + private Integer match_pattern; + private String whois_domain; + private Timestamp whois_update_date; + private Timestamp whois_create_date; + private Timestamp whois_expire_date; + private String whois_email; + private String whois_ns; + private String whois_registrar_name; + private String whois_registrant_org; + private String whois_registrant_name; + private String whois_registrant_street; + private String whois_registrant_city; + private String whois_registrant_state; + private String whois_registrant_postcode; + private String whois_registrant_country; + private String whois_registrant_phone; + + + // category schema + public DomainWhois(String fqdn, + String source, + Integer match_pattern, + Boolean query_success, + String whoisDomain, + Date whoisUpdateDate, + Date whoisCreateDate, + Date whoisExpireDate, + String whoisEmail, + String whoisNs, + String whoisRegistrarName, + String whoisRegistrantOrg, + String whoisRegistrantName, + String whoisRegistrantStreet, + String whoisRegistrantCity, + String whoisRegistrantState, + String whoisRegistrantPostcode, + String whoisRegistrantCountry, + String whoisRegistrantPhone + ) { + + this.fqdn = fqdn; + this.source = source; + + // 没有设置match_pattern,则二级域名为右匹配,其余为全匹配 + if (match_pattern == null) { + this.match_pattern = ValidationUtils.getMatchPattern(fqdn); + } else { + this.match_pattern = match_pattern; + } + + this.query_success = query_success; + this.whois_domain = ConfigUtils.getEffectiveString(whoisDomain); + this.whois_update_date = whoisUpdateDate == null ? null : new Timestamp(whoisUpdateDate.getTime()); + this.whois_create_date = whoisCreateDate == null ? null : new Timestamp(whoisCreateDate.getTime()); + this.whois_expire_date = whoisExpireDate == null ? null : new Timestamp(whoisExpireDate.getTime()); + this.whois_email = ConfigUtils.getEffectiveString(whoisEmail); + this.whois_ns = ConfigUtils.getEffectiveString(whoisNs); + this.whois_registrar_name = ConfigUtils.getEffectiveString(whoisRegistrarName); + this.whois_registrant_org = ConfigUtils.getEffectiveString(whoisRegistrantOrg); + this.whois_registrant_name = ConfigUtils.getEffectiveString(whoisRegistrantName); + this.whois_registrant_street = ConfigUtils.getEffectiveString(whoisRegistrantStreet); + this.whois_registrant_city = ConfigUtils.getEffectiveString(whoisRegistrantCity); + this.whois_registrant_state = ConfigUtils.getEffectiveString(whoisRegistrantState); + this.whois_registrant_postcode = ConfigUtils.getEffectiveString(whoisRegistrantPostcode); + this.whois_registrant_country = ConfigUtils.getEffectiveString(whoisRegistrantCountry); + this.whois_registrant_phone = ConfigUtils.getEffectiveString(whoisRegistrantPhone); + } + + public static void insertRecords(List whoisFiles, MariaDbBase mariaDbBase) { + for (DomainWhois whoisFile : whoisFiles) { + // 生成sql + String resSql = "INSERT INTO " + dataBase + "." + tableName + ' ' + + " (" + whoisFile.getKeys() + ") values" + + '(' + whoisFile.getValues() + ')'; + resSql = resSql.replace("'null'", "null"); + + mariaDbBase.writeSqlExecute(resSql); + } + } + + public static String insertSql(List whoisFiles) { + DomainWhois whoisFile = whoisFiles.get(0); + // 生成sql + String resSql = "INSERT INTO " + dataBase + "." + tableName + ' ' + + " (" + whoisFile.getKeys() + ") values" + + '(' + whoisFile.getValues() + ')'; + resSql = resSql.replace("'null'", "null"); + + return resSql; + } + + public static void updateRecords(List categoryFiles, MariaDbBase mariaDbBase) { + for (DomainWhois categoryFile : categoryFiles) { + + String resSql = "UPDATE " + dataBase + "." + + tableName + ' ' + + "SET " + categoryFile.getKeyValues() + + ", update_time = current_time() " + + " WHERE fqdn = '" + categoryFile.getFqdn() + '\''; + resSql = resSql.replace("'null'", "null"); + + mariaDbBase.writeSqlExecute(resSql); + } + } + + public static List getDbRecord(List fqdns, MariaDbBase mariaDbBase, String source) throws SQLException { + String queryFqdns = fqdns.stream().map(s -> "'" + s + "'").collect(Collectors.joining(",")); + String sql = "SELECT * FROM " + dataBase + "." + + tableName + ' ' + + " WHERE fqdn in (" + queryFqdns + ") "; + + return rs2schema(mariaDbBase.querySqlExecute(sql)); + } + + public String getValues() { + String resString = "'" + fqdn + '\'' + + ", '" + source + '\'' + + ", " + query_success + + ", " + match_pattern + + ", '" + whois_domain + '\'' + + ", '" + whois_update_date + '\'' + + ", '" + whois_create_date + '\'' + + ", '" + whois_expire_date + '\'' + + ", '" + whois_email + '\'' + + ", '" + whois_ns + '\'' + + ", '" + whois_registrar_name + '\'' + + ", '" + whois_registrant_org + '\'' + + ", '" + whois_registrant_name + '\'' + + ", '" + whois_registrant_street + '\'' + + ", '" + whois_registrant_city + '\'' + + ", '" + whois_registrant_state + '\'' + + ", '" + whois_registrant_postcode + '\'' + + ", '" + whois_registrant_country + '\'' + + ", '" + whois_registrant_phone + '\''; + + return resString.replace("'null'", "null"); + } + + public static List rs2schema(ResultSet rs) throws SQLException { + List schemaFiles = new ArrayList<>(); + while (rs.next()) { + schemaFiles.add( + new DomainWhois( + rs.getString("fqdn"), + rs.getString("source"), + rs.getInt("match_pattern"), + rs.getBoolean("query_success"), + rs.getString("whois_domain"), + (Date) rs.getDate("whois_update_date"), + (Date) rs.getDate("whois_create_date"), + (Date) rs.getDate("whois_expire_date"), + rs.getString("whois_email"), + rs.getString("whois_ns"), + rs.getString("whois_registrar_name"), + rs.getString("whois_registrant_org"), + rs.getString("whois_registrant_name"), + rs.getString("whois_registrant_street"), + rs.getString("whois_registrant_city"), + rs.getString("whois_registrant_state"), + rs.getString("whois_registrant_postcode"), + rs.getString("whois_registrant_country"), + rs.getString("whois_registrant_phone") + )); + } + return schemaFiles; + } + + public static JSONObject schema2json(DomainWhois schema) throws SQLException { + JSONObject jsonObject = new JSONObject(true); + jsonObject.put("fqdn", schema.getFqdn()); + jsonObject.put("source", schema.getSource()); + jsonObject.put("match_pattern", schema.getMatch_pattern()); + jsonObject.put("query_success", schema.getQuery_success()); + jsonObject.put("whois_domain", schema.getWhois_domain()); + jsonObject.put("whois_update_date", schema.getWhois_update_date()); + jsonObject.put("whois_create_date", schema.getWhois_create_date()); + jsonObject.put("whois_expire_date", schema.getWhois_expire_date()); + jsonObject.put("whois_email", schema.getWhois_email()); + jsonObject.put("whois_ns", schema.getWhois_ns()); + jsonObject.put("whois_registrar_name", schema.getWhois_registrar_name()); + jsonObject.put("whois_registrant_org", schema.getWhois_registrant_org()); + jsonObject.put("whois_registrant_name", schema.getWhois_registrant_name()); + jsonObject.put("whois_registrant_street", schema.getWhois_registrant_street()); + jsonObject.put("whois_registrant_city", schema.getWhois_registrant_city()); + jsonObject.put("whois_registrant_state", schema.getWhois_registrant_state()); + jsonObject.put("whois_registrant_postcode", schema.getWhois_registrant_postcode()); + jsonObject.put("whois_registrant_country", schema.getWhois_registrant_country()); + jsonObject.put("whois_registrant_phone", schema.getWhois_registrant_phone()); + + return jsonObject; + } + + public String getKeys() { + String resString; + resString = "fqdn" + + ", source" + + ", query_success" + + ", match_pattern" + + ", whois_domain" + + ", whois_update_date" + + ", whois_create_date" + + ", whois_expire_date" + + ", whois_email" + + ", whois_ns" + + ", whois_registrar_name" + + ", whois_registrant_org" + + ", whois_registrant_name" + + ", whois_registrant_street" + + ", whois_registrant_city" + + ", whois_registrant_state" + + ", whois_registrant_postcode" + + ", whois_registrant_country" + + ", whois_registrant_phone"; + return resString; + } + + public String getKeyValues() { + String resString = "query_success=" + query_success + + ", source='" + source + '\'' + + ", match_pattern=" + match_pattern + + ", whois_domain='" + whois_domain + '\'' + + ", whois_update_date='" + whois_update_date + '\'' + + ", whois_create_date='" + whois_create_date + '\'' + + ", whois_expire_date='" + whois_expire_date + '\'' + + ", whois_email='" + whois_email + '\'' + + ", whois_ns='" + whois_ns + '\'' + + ", whois_registrar_name='" + whois_registrar_name + '\'' + + ", whois_registrant_org='" + whois_registrant_org + '\'' + + ", whois_registrant_name='" + whois_registrant_name + '\'' + + ", whois_registrant_street='" + whois_registrant_street + '\'' + + ", whois_registrant_city='" + whois_registrant_city + '\'' + + ", whois_registrant_state='" + whois_registrant_state + '\'' + + ", whois_registrant_postcode='" + whois_registrant_postcode + '\'' + + ", whois_registrant_country='" + whois_registrant_country + '\'' + + ", whois_registrant_phone='" + whois_registrant_phone + '\''; + + return resString.replace("'null'", "null"); + } + + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } + + public String getFqdn() { + return fqdn; + } + + public void setFqdn(String fqdn) { + this.fqdn = fqdn; + } + + public Boolean getQuery_success() { + return query_success; + } + + public void setQuery_success(Boolean query_success) { + this.query_success = query_success; + } + + public Integer getMatch_pattern() { + return match_pattern; + } + + public void setMatch_pattern(Integer match_pattern) { + this.match_pattern = match_pattern; + } + + public String getWhois_domain() { + return whois_domain; + } + + public void setWhois_domain(String whois_domain) { + this.whois_domain = whois_domain; + } + + public Timestamp getWhois_update_date() { + return whois_update_date; + } + + public void setWhois_update_date(Timestamp whois_update_date) { + this.whois_update_date = whois_update_date; + } + + public Timestamp getWhois_create_date() { + return whois_create_date; + } + + public void setWhois_create_date(Timestamp whois_create_date) { + this.whois_create_date = whois_create_date; + } + + public Timestamp getWhois_expire_date() { + return whois_expire_date; + } + + public void setWhois_expire_date(Timestamp whois_expire_date) { + this.whois_expire_date = whois_expire_date; + } + + public String getWhois_email() { + return whois_email; + } + + public void setWhois_email(String whois_email) { + this.whois_email = whois_email; + } + + public String getWhois_ns() { + return whois_ns; + } + + public void setWhois_ns(String whois_ns) { + this.whois_ns = whois_ns; + } + + public String getWhois_registrar_name() { + return whois_registrar_name; + } + + public void setWhois_registrar_name(String whois_registrar_name) { + this.whois_registrar_name = whois_registrar_name; + } + + public String getWhois_registrant_org() { + return whois_registrant_org; + } + + public void setWhois_registrant_org(String whois_registrant_org) { + this.whois_registrant_org = whois_registrant_org; + } + + public String getWhois_registrant_name() { + return whois_registrant_name; + } + + public void setWhois_registrant_name(String whois_registrant_name) { + this.whois_registrant_name = whois_registrant_name; + } + + public String getWhois_registrant_street() { + return whois_registrant_street; + } + + public void setWhois_registrant_street(String whois_registrant_street) { + this.whois_registrant_street = whois_registrant_street; + } + + public String getWhois_registrant_city() { + return whois_registrant_city; + } + + public void setWhois_registrant_city(String whois_registrant_city) { + this.whois_registrant_city = whois_registrant_city; + } + + public String getWhois_registrant_state() { + return whois_registrant_state; + } + + public void setWhois_registrant_state(String whois_registrant_state) { + this.whois_registrant_state = whois_registrant_state; + } + + public String getWhois_registrant_postcode() { + return whois_registrant_postcode; + } + + public void setWhois_registrant_postcode(String whois_registrant_postcode) { + this.whois_registrant_postcode = whois_registrant_postcode; + } + + public String getWhois_registrant_country() { + return whois_registrant_country; + } + + public void setWhois_registrant_country(String whois_registrant_country) { + this.whois_registrant_country = whois_registrant_country; + } + + public String getWhois_registrant_phone() { + return whois_registrant_phone; + } + + public void setWhois_registrant_phone(String whois_registrant_phone) { + this.whois_registrant_phone = whois_registrant_phone; + } +} + diff --git a/src/main/java/com/mesasoft/cn/sketch/service/SketchService.java b/src/main/java/com/mesasoft/cn/sketch/service/SketchService.java new file mode 100644 index 0000000..bb15d13 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/sketch/service/SketchService.java @@ -0,0 +1,232 @@ +/* +package com.zhazhapan.efo.sketch.service; + +import cn.ac.iie.api.ChinaZ; +import cn.ac.iie.dao.MariaDbBase; +import cn.ac.iie.util.MariaDBUtils; +import cn.ac.iie.util.ValidationUtils; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.google.common.collect.Lists; +import com.zhazhapan.efo.sketch.api.BrightCloud; +import com.zhazhapan.efo.sketch.api.ChinaZ; +import com.zhazhapan.efo.sketch.config.ApplicationConfig; +import com.zhazhapan.efo.sketch.entity.DomainCategory; +import com.zhazhapan.efo.sketch.entity.DomainWhois; +import com.zhazhapan.efo.util.FileUtils; +import org.apache.log4j.Logger; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.sql.Connection; +import java.sql.Date; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +*/ +/** + * Created with IntelliJ IDEA. + * User: joy + * Date: 2021/12/31 + * Time: 11:28 AM + * Description: No Description + *//* + +public class SketchService { + private static final Logger LOG = Logger.getLogger(SketchService.class); + private static final int MAX_DB_BATCH_SIZE = ApplicationConfig.DB_QUERY_BATCH_SIZE; + + private List queryObjects; + private String curQueryType; + + private MariaDbBase mariaDB; + + long queryNum; + long dbResultNum; + long apiResultNum; + long failedQueryNum; + + public JSONArray getQueryResults(String objectType, String queryType, List queryObjects, String username, Boolean isLocal) throws SQLException, IOException { + queryNum = 0; + dbResultNum = 0; + apiResultNum = 0; + failedQueryNum = 0; + + this.queryObjects = queryObjects; + this.curQueryType = queryType; + JSONArray json = null; + + Connection mariaConn = MariaDBUtils.getConnection(); + Statement mariaStat = mariaConn.createStatement(); + mariaDB = new MariaDbBase(mariaConn, mariaStat); + + //去重 + queryObjects = queryObjects.stream().distinct().collect(Collectors.toList()); + + // 校验 + queryObjects = ValidationUtils.getChecked(queryObjects, objectType); + this.queryNum = queryObjects.size(); + + // 执行查询 + if (queryType.equals("domain_category")) { + json = getDomainCategory(queryObjects, username, isLocal); + } else if (queryType.equals("domain_whois")) { + json = getDomainWhois(queryObjects, isLocal); + } else { + // TODO: get dns server info + LOG.error("Wrong query type: " + queryType); + } + + LOG.info("Query Statistic - number of query objects: " + queryNum + + "\nresults from local db: " + dbResultNum + + " results from external api: " + apiResultNum); + MariaDBUtils.close(mariaStat, mariaConn); + return json; + } + + public JSONArray getDomainCategory(List domains, String username, boolean isLocal) throws SQLException, IOException { + + JSONArray results = new JSONArray(); + + // 查询本地数据库 + ArrayList objectsFromDB = new ArrayList<>(); + List> partitions = Lists.partition(queryObjects, MAX_DB_BATCH_SIZE); + for (List partition : partitions) { // 批量查询 + List dbRecords = DomainCategory.getDbRecord(partition, mariaDB, "brightcloud"); + for (DomainCategory record : dbRecords) { + objectsFromDB.add(record.getFqdn()); // 保存查询记录 + JSONObject jsonObject = DomainCategory.schema2json(record); + results.add(jsonObject); // 保存查询结果 + } + } + dbResultNum = results.size(); + + // 调用api + List bcResults = new ArrayList<>(); + List objectsFromApi = new ArrayList<>(queryObjects); + objectsFromApi.removeAll(objectsFromDB); + if (!isLocal && objectsFromApi.size() > 0) { + BrightCloud brightCloud = new BrightCloud(); + + List> apiPartitions = Lists.partition(objectsFromApi, ApplicationConfig.API_BC_MAXIMUM_QUERYNUM); + for (List partition : apiPartitions) { // 批量查询 + List recordsFromBcApi = brightCloud.responseSparse(brightCloud.getQueryResults(partition)); + for (DomainCategory record : recordsFromBcApi) { + if (record.getQuery_success().equals(true)) { //查询成功的结果 + record.setSubmit_user(username); + bcResults.add(record); + if (bcResults.size() > MAX_DB_BATCH_SIZE) { //超过一定量时写入数据库 + DomainCategory.insertRecords(bcResults, mariaDB); + bcResults.clear(); + } + results.add(DomainCategory.schema2json(record)); + } else { // 查询失败的结果 + failedQueryNum += 1; + } + } + } + } + apiResultNum = results.size() - dbResultNum - failedQueryNum; + DomainCategory.insertRecords(bcResults, mariaDB); + + if (apiResultNum > 0) { // 记录api调用次数 + OutputStream bcQueryLogStream = new FileOutputStream(ApplicationConfig.API_BC_USE_REPORT_FILE, true); + OutputStreamWriter bcQueryLogWriter = new OutputStreamWriter(bcQueryLogStream, StandardCharsets.UTF_8); + Date d = new Date(System.currentTimeMillis()); + bcQueryLogWriter.write(d + "," + "List Query," + "-" + "," + curQueryType + "," + apiResultNum + "\n"); + FileUtils.writerClose(bcQueryLogWriter, bcQueryLogStream); + } + + return results; + } + + public JSONArray getDomainWhois(List domains, boolean isLocal) throws SQLException, IOException { + + JSONArray results = new JSONArray(); + + // 查询本地数据库 + ArrayList objectsFromDB = new ArrayList<>(); + List> partitions = Lists.partition(queryObjects, MAX_DB_BATCH_SIZE); + for (List partition : partitions) { // 批量查询 + List dbRecords = + DomainWhois.getDbRecord(partition, mariaDB, "chinaz"); + for (DomainWhois record : dbRecords) { + objectsFromDB.add(record.getFqdn()); // 保存查询记录 + JSONObject jsonObject = DomainWhois.schema2json(record); + results.add(jsonObject); // 保存查询结果 + } + } + dbResultNum = results.size(); + + // 调用api + List chinazResults = new ArrayList<>(); + if (!isLocal) { + ChinaZ chinaz = new ChinaZ(); + + List objectsFromApi = new ArrayList<>(queryObjects); + objectsFromApi.removeAll(objectsFromDB); // 需要调用api查询的部分对象 + + List> apiPartitions = Lists.partition(objectsFromApi, ApplicationConfig.API_CHINAZ_MAXIMUM_QUERYNUM); + for (List partition : apiPartitions) { // 批量查询 + List recordsFromApi = chinaz.responseSparse(chinaz.getQueryResults(partition)); + for (DomainWhois record : recordsFromApi) { + if (record.getQuery_success().equals(true)) { // 查询成功的结果 + chinazResults.add(record); + if (chinazResults.size() > MAX_DB_BATCH_SIZE) { // 超过一定量时写入数据库 + DomainWhois.insertRecords(chinazResults, mariaDB); + chinazResults.clear(); + } + results.add(DomainWhois.schema2json(record)); + } else { // 查询失败的结果 + failedQueryNum += 1; + } + } + } + } + + DomainWhois.insertRecords(chinazResults, mariaDB); + apiResultNum = results.size() - dbResultNum - failedQueryNum; + // todo 不直接写入,维护变量,导出一次 + if (apiResultNum > 0) { // 记录api调用次数 + OutputStream bcQueryLogStream = new FileOutputStream(ApplicationConfig.API_CHINAZ_USE_REPORT_FILE, true); + OutputStreamWriter bcQueryLogWriter = new OutputStreamWriter(bcQueryLogStream, StandardCharsets.UTF_8); + Date d = new Date(System.currentTimeMillis()); + bcQueryLogWriter.write(d + "," + "List Query," + "-" + "," + curQueryType + "," + apiResultNum + "\n"); + FileUtils.writerClose(bcQueryLogWriter, bcQueryLogStream); + } + + + return results; + } + + + public static void main(String[] args) throws Exception { +// String objectType = args[0]; +// String queryType = args[1]; +// List queryObjects = Arrays.asList(args[2].split(",")); +// String username = args[4]; +// boolean isLocal = true; +// if (args.length >= 3) { +// isLocal = Boolean.parseBoolean(args[3]); +// } + +// System.out.println(new SketchService().getQueryResults(objectType, queryType, queryObjects, username, isLocal).toString()); + + JSONArray queryResults = new SketchService().getQueryResults("domain", + "domain_category", + Arrays.asList("baidu.com", "cctv.com"), "", true); + System.err.println(queryResults.toJSONString()); + String json = "{\"namespace\":\"log.session\",\"type\":\"record\",\"name\":\"session\",\"fields\":[{\"name\":\"common_log_id\",\"type\":\"long\"},{\"name\":\"common_service\",\"type\":\"long\"},{\"name\":\"common_recv_time\",\"type\":\"long\"},{\"name\":\"common_direction\",\"type\":\"long\"},{\"name\":\"common_l4_protocol\",\"type\":\"string\"},{\"name\":\"common_address_type\",\"type\":\"long\"},{\"name\":\"common_schema_type\",\"type\":\"string\"},{\"name\":\"common_policy_id\",\"type\":\"long\"},{\"name\":\"common_user_tags\",\"type\":\"string\"},{\"name\":\"common_action\",\"type\":\"long\"},{\"name\":\"common_sub_action\",\"type\":\"string\"},{\"name\":\"common_user_region\",\"type\":\"string\"},{\"name\":\"common_client_ip\",\"type\":\"string\"},{\"name\":\"common_client_port\",\"type\":\"long\"},{\"name\":\"common_internal_ip\",\"type\":\"string\"},{\"name\":\"common_entrance_id\",\"type\":\"long\"},{\"name\":\"common_device_id\",\"type\":\"string\"},{\"name\":\"common_egress_link_id\",\"type\":\"long\"},{\"name\":\"common_ingress_link_id\",\"type\":\"long\"},{\"name\":\"common_isp\",\"type\":\"string\"},{\"name\":\"common_device_tag\",\"type\":\"string\"},{\"name\":\"common_data_center\",\"type\":\"string\"},{\"name\":\"common_encapsulation\",\"type\":\"long\"},{\"name\":\"common_tunnels\",\"type\":\"string\"},{\"name\":\"common_sled_ip\",\"type\":\"string\"},{\"name\":\"common_device_group\",\"type\":\"string\"},{\"name\":\"common_app_behavior\",\"type\":\"string\"},{\"name\":\"common_client_location\",\"type\":\"string\"},{\"name\":\"common_client_asn\",\"type\":\"string\"},{\"name\":\"common_subscriber_id\",\"type\":\"string\"},{\"name\":\"common_imei\",\"type\":\"string\"},{\"name\":\"common_imsi\",\"type\":\"string\"},{\"name\":\"common_phone_number\",\"type\":\"string\"},{\"name\":\"common_server_ip\",\"type\":\"string\"},{\"name\":\"common_server_port\",\"type\":\"long\"},{\"name\":\"common_external_ip\",\"type\":\"string\"},{\"name\":\"common_server_location\",\"type\":\"string\"},{\"name\":\"common_server_asn\",\"type\":\"string\"},{\"name\":\"common_protocol_label\",\"type\":\"string\"},{\"name\":\"common_service_category\",\"type\":{\"type\":\"array\",\"items\":\"long\"}},{\"name\":\"common_app_label\",\"type\":\"string\"},{\"name\":\"common_app_id\",\"type\":\"string\"},{\"name\":\"common_userdefine_app_name\",\"type\":\"string\"},{\"name\":\"common_app_surrogate_id\",\"type\":\"string\"},{\"name\":\"common_l7_protocol\",\"type\":\"string\"},{\"name\":\"common_sessions\",\"type\":\"long\"},{\"name\":\"common_c2s_pkt_num\",\"type\":\"long\"},{\"name\":\"common_s2c_pkt_num\",\"type\":\"long\"},{\"name\":\"common_c2s_pkt_diff\",\"type\":\"long\"},{\"name\":\"common_s2c_pkt_diff\",\"type\":\"long\"},{\"name\":\"common_c2s_byte_diff\",\"type\":\"long\"},{\"name\":\"common_s2c_byte_diff\",\"type\":\"long\"},{\"name\":\"common_c2s_byte_num\",\"type\":\"long\"},{\"name\":\"common_s2c_byte_num\",\"type\":\"long\"},{\"name\":\"common_start_time\",\"type\":\"long\"},{\"name\":\"common_end_time\",\"type\":\"long\"},{\"name\":\"common_establish_latency_ms\",\"type\":\"long\"},{\"name\":\"common_con_duration_ms\",\"type\":\"long\"},{\"name\":\"common_stream_dir\",\"type\":\"long\"},{\"name\":\"common_address_list\",\"type\":\"string\"},{\"name\":\"common_has_dup_traffic\",\"type\":\"long\"},{\"name\":\"common_stream_error\",\"type\":\"string\"},{\"name\":\"common_stream_trace_id\",\"type\":\"long\"},{\"name\":\"common_link_info_c2s\",\"type\":\"string\"},{\"name\":\"common_link_info_s2c\",\"type\":\"string\"},{\"name\":\"common_packet_capture_file\",\"type\":\"string\"},{\"name\":\"common_c2s_ipfrag_num\",\"type\":\"long\"},{\"name\":\"common_s2c_ipfrag_num\",\"type\":\"long\"},{\"name\":\"common_c2s_tcp_lostlen\",\"type\":\"long\"},{\"name\":\"common_s2c_tcp_lostlen\",\"type\":\"long\"},{\"name\":\"common_c2s_tcp_unorder_num\",\"type\":\"long\"},{\"name\":\"common_s2c_tcp_unorder_num\",\"type\":\"long\"},{\"name\":\"common_c2s_pkt_retrans\",\"type\":\"long\"},{\"name\":\"common_s2c_pkt_retrans\",\"type\":\"long\"},{\"name\":\"common_c2s_byte_retrans\",\"type\":\"long\"},{\"name\":\"common_s2c_byte_retrans\",\"type\":\"long\"},{\"name\":\"common_tcp_client_isn\",\"type\":\"long\"},{\"name\":\"common_tcp_server_isn\",\"type\":\"long\"},{\"name\":\"common_mirrored_pkts\",\"type\":\"long\"},{\"name\":\"common_mirrored_bytes\",\"type\":\"long\"},{\"name\":\"common_first_ttl\",\"type\":\"long\"},{\"name\":\"common_processing_time\",\"type\":\"long\"},{\"name\":\"http_url\",\"type\":\"string\"},{\"name\":\"http_host\",\"type\":\"string\"},{\"name\":\"http_domain\",\"type\":\"string\"},{\"name\":\"http_request_line\",\"type\":\"string\"},{\"name\":\"http_response_line\",\"type\":\"string\"},{\"name\":\"http_request_header\",\"type\":\"string\"},{\"name\":\"http_response_header\",\"type\":\"string\"},{\"name\":\"http_request_content\",\"type\":\"string\"},{\"name\":\"http_response_content\",\"type\":\"string\"},{\"name\":\"http_request_body\",\"type\":\"string\"},{\"name\":\"http_response_body\",\"type\":\"string\"},{\"name\":\"http_request_body_key\",\"type\":\"string\"},{\"name\":\"http_response_body_key\",\"type\":\"string\"},{\"name\":\"http_proxy_flag\",\"type\":\"long\"},{\"name\":\"http_sequence\",\"type\":\"long\"},{\"name\":\"http_snapshot\",\"type\":\"string\"},{\"name\":\"http_cookie\",\"type\":\"string\"},{\"name\":\"http_referer\",\"type\":\"string\"},{\"name\":\"http_user_agent\",\"type\":\"string\"},{\"name\":\"http_request_content_length\",\"type\":\"string\"},{\"name\":\"http_request_content_type\",\"type\":\"string\"},{\"name\":\"http_response_content_length\",\"type\":\"string\"},{\"name\":\"http_response_content_type\",\"type\":\"string\"},{\"name\":\"http_content_length\",\"type\":\"string\"},{\"name\":\"http_content_type\",\"type\":\"string\"},{\"name\":\"http_set_cookie\",\"type\":\"string\"},{\"name\":\"http_version\",\"type\":\"string\"},{\"name\":\"http_response_latency_ms\",\"type\":\"long\"},{\"name\":\"http_session_duration_ms\",\"type\":\"long\"},{\"name\":\"http_action_file_size\",\"type\":\"long\"},{\"name\":\"mail_protocol_type\",\"type\":\"string\"},{\"name\":\"mail_account\",\"type\":\"string\"},{\"name\":\"mail_to_cmd\",\"type\":\"string\"},{\"name\":\"mail_from_cmd\",\"type\":\"string\"},{\"name\":\"mail_from\",\"type\":\"string\"},{\"name\":\"mail_to\",\"type\":\"string\"},{\"name\":\"mail_cc\",\"type\":\"string\"},{\"name\":\"mail_bcc\",\"type\":\"string\"},{\"name\":\"mail_subject\",\"type\":\"string\"},{\"name\":\"mail_subject_charset\",\"type\":\"string\"},{\"name\":\"mail_content\",\"type\":\"string\"},{\"name\":\"mail_content_charset\",\"type\":\"string\"},{\"name\":\"mail_attachment_name\",\"type\":\"string\"},{\"name\":\"mail_attachment_name_charset\",\"type\":\"string\"},{\"name\":\"mail_attachment_content\",\"type\":\"string\"},{\"name\":\"mail_eml_file\",\"type\":\"string\"},{\"name\":\"mail_snapshot\",\"type\":\"string\"},{\"name\":\"dns_message_id\",\"type\":\"long\"},{\"name\":\"dns_qr\",\"type\":\"long\"},{\"name\":\"dns_opcode\",\"type\":\"long\"},{\"name\":\"dns_aa\",\"type\":\"long\"},{\"name\":\"dns_tc\",\"type\":\"long\"},{\"name\":\"dns_rd\",\"type\":\"long\"},{\"name\":\"dns_ra\",\"type\":\"long\"},{\"name\":\"dns_rcode\",\"type\":\"long\"},{\"name\":\"dns_qdcount\",\"type\":\"long\"},{\"name\":\"dns_ancount\",\"type\":\"long\"},{\"name\":\"dns_nscount\",\"type\":\"long\"},{\"name\":\"dns_arcount\",\"type\":\"long\"},{\"name\":\"dns_qname\",\"type\":\"string\"},{\"name\":\"dns_qtype\",\"type\":\"long\"},{\"name\":\"dns_qclass\",\"type\":\"long\"},{\"name\":\"dns_cname\",\"type\":\"string\"},{\"name\":\"dns_sub\",\"type\":\"long\"},{\"name\":\"dns_rr\",\"type\":\"string\"},{\"name\":\"ssl_version\",\"type\":\"string\"},{\"name\":\"ssl_sni\",\"type\":\"string\"},{\"name\":\"ssl_san\",\"type\":\"string\"},{\"name\":\"ssl_cn\",\"type\":\"string\"},{\"name\":\"ssl_pinningst\",\"type\":\"long\"},{\"name\":\"ssl_intercept_state\",\"type\":\"long\"},{\"name\":\"ssl_passthrough_reason\",\"type\":\"string\"},{\"name\":\"ssl_server_side_latency\",\"type\":\"long\"},{\"name\":\"ssl_client_side_latency\",\"type\":\"long\"},{\"name\":\"ssl_server_side_version\",\"type\":\"string\"},{\"name\":\"ssl_client_side_version\",\"type\":\"string\"},{\"name\":\"ssl_cert_verify\",\"type\":\"long\"},{\"name\":\"ssl_error\",\"type\":\"string\"},{\"name\":\"ssl_con_latency_ms\",\"type\":\"long\"},{\"name\":\"ssl_ja3_fingerprint\",\"type\":\"string\"},{\"name\":\"ssl_ja3_hash\",\"type\":\"string\"},{\"name\":\"ssl_cert_issuer\",\"type\":\"string\"},{\"name\":\"ssl_cert_subject\",\"type\":\"string\"},{\"name\":\"quic_version\",\"type\":\"string\"},{\"name\":\"quic_sni\",\"type\":\"string\"},{\"name\":\"quic_user_agent\",\"type\":\"string\"},{\"name\":\"ftp_account\",\"type\":\"string\"},{\"name\":\"ftp_url\",\"type\":\"string\"},{\"name\":\"ftp_content\",\"type\":\"string\"},{\"name\":\"ftp_link_type\",\"type\":\"string\"},{\"name\":\"bgp_type\",\"type\":\"long\"},{\"name\":\"bgp_as_num\",\"type\":\"string\"},{\"name\":\"bgp_route\",\"type\":\"string\"},{\"name\":\"voip_calling_account\",\"type\":\"string\"},{\"name\":\"voip_called_account\",\"type\":\"string\"},{\"name\":\"voip_calling_number\",\"type\":\"string\"},{\"name\":\"voip_called_number\",\"type\":\"string\"},{\"name\":\"sip_call_id\",\"type\":\"string\"},{\"name\":\"sip_originator_description\",\"type\":\"string\"},{\"name\":\"sip_responder_description\",\"type\":\"string\"},{\"name\":\"sip_user_agent\",\"type\":\"string\"},{\"name\":\"sip_server\",\"type\":\"string\"},{\"name\":\"sip_originator_sdp_connect_ip\",\"type\":\"string\"},{\"name\":\"sip_originator_sdp_media_port\",\"type\":\"long\"},{\"name\":\"sip_originator_sdp_media_type\",\"type\":\"string\"},{\"name\":\"sip_originator_sdp_content\",\"type\":\"string\"},{\"name\":\"sip_responder_sdp_connect_ip\",\"type\":\"string\"},{\"name\":\"sip_responder_sdp_media_port\",\"type\":\"long\"},{\"name\":\"sip_responder_sdp_media_type\",\"type\":\"string\"},{\"name\":\"sip_responder_sdp_content\",\"type\":\"string\"},{\"name\":\"sip_duration_s\",\"type\":\"long\"},{\"name\":\"sip_bye\",\"type\":\"string\"},{\"name\":\"rtp_payload_type_c2s\",\"type\":\"long\"},{\"name\":\"rtp_payload_type_s2c\",\"type\":\"long\"},{\"name\":\"rtp_pcap_path\",\"type\":\"string\"},{\"name\":\"rtp_originator_dir\",\"type\":\"long\"},{\"name\":\"ssh_version\",\"type\":\"string\"},{\"name\":\"ssh_auth_success\",\"type\":\"string\"},{\"name\":\"ssh_client_version\",\"type\":\"string\"},{\"name\":\"ssh_server_version\",\"type\":\"string\"},{\"name\":\"ssh_cipher_alg\",\"type\":\"string\"},{\"name\":\"ssh_mac_alg\",\"type\":\"string\"},{\"name\":\"ssh_compression_alg\",\"type\":\"string\"},{\"name\":\"ssh_kex_alg\",\"type\":\"string\"},{\"name\":\"ssh_host_key_alg\",\"type\":\"string\"},{\"name\":\"ssh_host_key\",\"type\":\"string\"},{\"name\":\"ssh_hassh\",\"type\":\"string\"},{\"name\":\"stratum_cryptocurrency\",\"type\":\"string\"},{\"name\":\"stratum_mining_pools\",\"type\":\"string\"},{\"name\":\"stratum_mining_program\",\"type\":\"string\"},{\"name\":\"streaming_media_url\",\"type\":\"string\"},{\"name\":\"streaming_media_protocol\",\"type\":\"string\"},{\"name\":\"app_extra_info\",\"type\":\"string\"}]}"; + Object parse = JSONObject.parse(json); + + System.err.println(parse); + } +} +*/ diff --git a/src/main/java/com/mesasoft/cn/util/BeanUtils.java b/src/main/java/com/mesasoft/cn/util/BeanUtils.java new file mode 100644 index 0000000..152c490 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/util/BeanUtils.java @@ -0,0 +1,77 @@ +package com.mesasoft.cn.util; + +import com.alibaba.fastjson.JSONObject; +import com.zhazhapan.modules.constant.ValueConsts; +import com.zhazhapan.util.Checker; +import com.zhazhapan.util.Formatter; +import com.zhazhapan.util.enums.FieldModifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.InvocationTargetException; + +/** + * @author pantao + * @since 2018/1/18 + */ +public class BeanUtils { + + private static final String ERROR_JSON = "{\"error\":\"internal error, please try again later\"}"; + + private static Logger logger = LoggerFactory.getLogger(BeanUtils.class); + + private BeanUtils() {} + + /** + * 将权限字符串装换成权限数组 + * + * @param auth 权限字符串 + * + * @return 权限数组 + */ + public static int[] getAuth(String auth) { + int[] a = new int[5]; + if (Checker.isNotEmpty(auth)) { + String[] u = auth.split(ValueConsts.COMMA_SIGN); + int len = Math.min(a.length, u.length); + for (int i = 0; i < len; i++) { + a[i] = Formatter.stringToInt(u[i]); + } + } + return a; + } + + /** + * 将Bean转换成JSON + * + * @param object Bean对象 + * + * @return {@link String} + */ + public static String toPrettyJson(Object object) { + String result; + try { + result = com.zhazhapan.util.BeanUtils.toPrettyJson(object, FieldModifier.PRIVATE); + } catch (IllegalAccessException e) { + result = Formatter.formatJson(ERROR_JSON); + logger.error(e.getMessage()); + } + return result; + } + + /** + * 将类属性装换成JSON(只能转换有get方法的) + * + * @param object 转换的对象 + * + * @return {@link JSONObject} + */ + public static JSONObject beanToJson(Object object) { + try { + return com.zhazhapan.util.BeanUtils.beanToJson(object); + } catch (IllegalAccessException | InvocationTargetException e) { + logger.error(e.getMessage()); + return null; + } + } +} diff --git a/src/main/java/com/mesasoft/cn/util/CommonUtils.java b/src/main/java/com/mesasoft/cn/util/CommonUtils.java new file mode 100644 index 0000000..eebb49b --- /dev/null +++ b/src/main/java/com/mesasoft/cn/util/CommonUtils.java @@ -0,0 +1,26 @@ +package com.mesasoft.cn.util; + +import com.mesasoft.cn.modules.constant.DefaultValues; +import com.zhazhapan.modules.constant.ValueConsts; + +/** + * @author pantao + * @since 2018/1/29 + */ +public class CommonUtils { + + private CommonUtils() {} + + /** + * 将相对路径转换成绝对路径 + * + * @param path 文件路径 + * + * @return {@link String} + */ + public static String checkPath(String path) { + String prefix = DefaultValues.COLON + ValueConsts.SEPARATOR; + return path.startsWith(ValueConsts.SEPARATOR) || path.startsWith(prefix, ValueConsts.ONE_INT) ? path : + DefaultValues.STORAGE_PATH + path; + } +} diff --git a/src/main/java/com/mesasoft/cn/util/ConfigUtils.java b/src/main/java/com/mesasoft/cn/util/ConfigUtils.java new file mode 100644 index 0000000..8aedad0 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/util/ConfigUtils.java @@ -0,0 +1,46 @@ +package com.mesasoft.cn.util; + + +import org.apache.log4j.Logger; + +import java.util.Properties; + +public class ConfigUtils { + private static final Logger LOG = Logger.getLogger(ConfigUtils.class); + private static Properties propCommon = new Properties(); + + public static String getStringProperty(String key) { + return propCommon.getProperty(key); + } + + public static Integer getIntProperty(String key) { + return Integer.parseInt(propCommon.getProperty(key)); + } + + public static Long getLongProperty(String key) { + return Long.parseLong(propCommon.getProperty(key)); + } + + public static Boolean getBooleanProperty(String key) { + return "true".equals(propCommon.getProperty(key).toLowerCase().trim()); + } + + public static String getEffectiveString(String s) { + if (!(s == null)) { + return s.length() == 0 ? null : s; + } else { + return null; + } + } + + static { + try { + propCommon.load(ConfigUtils.class.getClassLoader().getResourceAsStream("sketch.properties")); + + } catch (Exception e) { + propCommon = null; + LOG.error("配置加载失败"); + } + } + +} diff --git a/src/main/java/com/mesasoft/cn/util/Contants.java b/src/main/java/com/mesasoft/cn/util/Contants.java new file mode 100644 index 0000000..2989133 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/util/Contants.java @@ -0,0 +1,54 @@ +package com.mesasoft.cn.util; + +import com.google.common.collect.ImmutableMap; + +import java.util.Map; + +/** + * @description, + * @author, zhq + * @create, 2022-03-18 + **/ +public class Contants { + + public static final Map CONTENT_TYPES = ImmutableMap.builder() + .put("doc", "application/msword") + .put("bin", "application/octet-stream") + .put("exe", "application/octet-stream") + .put("so", "application/octet-stream") + .put("dll", "application/octet-stream") + .put("pdf", "application/pdf") + .put("ai", "application/postscript") + .put("xls", "application/vnd.ms-excel") + .put("ppt", "application/vnd.ms-powerpoint") + .put("dir", "application/x-director") + .put("js", "application/x-javascript") + .put("swf", "application/x-shockwave-flash") + .put("xhtml", "application/xhtml+xml") + .put("xht", "application/xhtml+xml") + .put("zip", "application/zip") + .put("mid", "audio/midi") + .put("midi", "audio/midi") + .put("mp3", "audio/mpeg") + .put("rm", "audio/x-pn-realaudio") + .put("rpm", "audio/x-pn-realaudio-plugin") + .put("wav", "audio/x-wav") + .put("bmp", "image/bmp") + .put("gif", "image/gif") + .put("jpeg", "image/jpeg") + .put("jpg", "image/jpeg") + .put("png", "image/png") + .put("css", "text/css") + .put("html", "text/html") + .put("htm", "text/html") + .put("txt", "text/plain") + .put("xsl", "text/xml") + .put("xml", "text/xml") + .put("mpeg", "video/mpeg") + .put("mpg", "video/mpeg") + .put("avi", "video/x-msvideo") + .put("movie", "video/x-sgi-movie") + .put(".csv", "text/csv" + ).build(); + +} diff --git a/src/main/java/com/mesasoft/cn/util/ControllerUtils.java b/src/main/java/com/mesasoft/cn/util/ControllerUtils.java new file mode 100644 index 0000000..e551434 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/util/ControllerUtils.java @@ -0,0 +1,152 @@ +package com.mesasoft.cn.util; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.text.UnicodeUtil; +import com.alibaba.fastjson.JSONObject; +import com.mesasoft.cn.modules.constant.DefaultValues; +import com.zhazhapan.util.Checker; +import org.apache.commons.lang3.StringUtils; + +import javax.activation.MimetypesFileTypeMap; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; + +/** + * @author pantao + * @since 2018/1/30 + */ +public class ControllerUtils { + + private ControllerUtils() { + } + + /** + * 获取一个简单的响应状态 + * + * @param isSuccess 是否操作成功 + * @return 响应JSON字符串 + */ + public static String getResponse(boolean isSuccess) { + JSONObject jsonObject = new JSONObject(); + if (isSuccess) { + jsonObject.put("status", "success"); + } else { + jsonObject.put("status", "error"); + } + return jsonObject.toString(); + } + + /** + * 加载本地资源 + * + * @param response 返回的Response + * @param path 资源路径 + * @param download 直接下载 + */ + public static void loadResource2(HttpServletResponse response, String path, boolean download) throws IOException { + if (Checker.isNotEmpty(path)) { + File file = new File(path); + if (download) { + response.setContentType(getContentType(file)+";charset=UTF-8"); + setResponseFileName2(response, file.getName()); + } + FileInputStream in = new FileInputStream(file); + ServletOutputStream os = response.getOutputStream(); + byte[] b; + while (in.available() > 0) { + b = in.available() > 1024 ? new byte[1024] : new byte[in.available()]; + in.read(b, 0, b.length); + os.write(b, 0, b.length); + } + in.close(); + os.flush(); + os.close(); + } else { + response.sendRedirect(DefaultValues.NOT_FOUND_PAGE); + } + } + public static void loadResource(HttpServletResponse response, String path, boolean download) throws IOException { + if (Checker.isNotEmpty(path)) { + File file = new File(path); + if (download) { + response.setContentType(getContentType(file)); + setResponseFileName( response, file.getName()); + response.setCharacterEncoding("UTF-8"); + } + FileInputStream in = new FileInputStream(file); + ServletOutputStream os = response.getOutputStream(); + byte[] b; + while (in.available() > 0) { + b = in.available() > 1024 ? new byte[1024] : new byte[in.available()]; + in.read(b, 0, b.length); + os.write(b, 0, b.length); + } + in.close(); + os.flush(); + os.close(); + } else { + response.sendRedirect(DefaultValues.NOT_FOUND_PAGE); + } + } + public static String getContentType(File file) { + String defContentType = "application/octet-stream"; + String fileName = file.getName(); + String fileTyle=fileName.substring(fileName.lastIndexOf(".")+1,fileName.length()); + if (StringUtils.isNotBlank(fileTyle)) { + String type2 = Contants.CONTENT_TYPES.get(fileTyle); + if (StringUtils.isNotBlank(type2)) { + return type2; + } + } else { + String type1 = new MimetypesFileTypeMap().getContentType(file); + if (StringUtils.isNotBlank(type1)) { + return type1; + } + } + return defContentType; + } + + /** + * 设置响应头的文件名 + * + * @param response {@link HttpServletResponse} + * @param fileName 文件名 + */ + public static void setResponseFileName2(HttpServletResponse response, String fileName) { + response.setHeader("Content-Disposition", "attachment;filename=" + UnicodeUtil.toUnicode(fileName)); + } + public static void setResponseFileName( HttpServletResponse response, String fileName) throws + UnsupportedEncodingException { + response.setHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes("UTF-8"), + "ISO-8859-1")); + } + + public static void loadFile(HttpServletRequest request, HttpServletResponse response,String filePath,boolean download) throws IOException { + request.setCharacterEncoding("utf-8"); + // 文件存储路径 + // 从请求中获取文件名 + File file=new File(filePath); + String fileName=file.getName(); + // 创建输出流对象 + ServletOutputStream outputStream = response.getOutputStream(); + //以字节数组的形式读取文件 + byte[] bytes = FileUtil.readBytes(filePath); + // 设置返回内容格式 + response.setContentType("application/octet-stream;charset=UTF-8"); + // 把文件名按UTF-8取出并按ISO8859-1编码,保证弹出窗口中的文件名中文不乱码 + // 中文不要太多,最多支持17个中文,因为header有150个字节限制。 + response.setHeader("filename", UnicodeUtil.toUnicode(fileName)); + // 这一步一定要在读取文件之后进行,否则文件名会乱码,找不到文件 + fileName = new String(fileName.getBytes("UTF-8"),"ISO-8859-1"); + // 设置下载弹窗的文件名和格式(文件名要包括名字和文件格式) + response.setHeader("Content-Disposition", "attachment;filename=" + fileName); + // 返回数据到输出流对象中 + outputStream.write(bytes); + // 关闭流对象 + IoUtil.close(outputStream); + + } +} diff --git a/src/main/java/com/mesasoft/cn/util/FileUtils.java b/src/main/java/com/mesasoft/cn/util/FileUtils.java new file mode 100644 index 0000000..773843d --- /dev/null +++ b/src/main/java/com/mesasoft/cn/util/FileUtils.java @@ -0,0 +1,197 @@ +package com.mesasoft.cn.util; + +import org.apache.log4j.Logger; + +import java.io.*; +import java.util.ArrayList; +import java.util.List; + +/** + * @author yjy + * @version 1.0 + * @date 2021/2/25 6:11 下午 + */ +public class FileUtils { + private static final Logger LOG = Logger.getLogger(FileUtils.class); + + public static List readTxtFileIntoStringArrList(String filePath) + { + List list = new ArrayList<>(); + try + { + String encoding = "GBK"; + File file = new File(filePath); + if (file.isFile() && file.exists()) + { // 判断文件是否存在 + InputStreamReader read = new InputStreamReader( + new FileInputStream(file), encoding); + BufferedReader bufferedReader = new BufferedReader(read); + String lineTxt = null; + + while ((lineTxt = bufferedReader.readLine()) != null) + { + if (!lineTxt.equals("")) { + list.add(lineTxt.trim()); + } + } + bufferedReader.close(); + read.close(); + } + else + { + System.out.println("Can not find file: " + filePath); + } + } + catch (Exception e) + { + System.out.println("Error occurred in Function 'readTxtFileIntoStringArrList'"); + e.printStackTrace(); + } + + return list; + } + + public static List getBatchLineReadIn(BufferedReader bufferedReader, int batchSize){ + List list = new ArrayList<>(); + String lineTxt; + try{ + while ((lineTxt = bufferedReader.readLine()) != null && list.size() lengthDomain) { + lengthDomain = split.length; + } + } + read.close(); + } else { + LOG.error("FilePath is wrong--->{" + filePath + "}<---"); + } + } catch (Exception e) { + LOG.error("Get filePathData error--->{" + e + "}<---"); + e.printStackTrace(); + } + return lengthDomain; + } +} diff --git a/src/main/java/com/mesasoft/cn/util/MariaDBUtils.java b/src/main/java/com/mesasoft/cn/util/MariaDBUtils.java new file mode 100644 index 0000000..a86b363 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/util/MariaDBUtils.java @@ -0,0 +1,80 @@ +package com.mesasoft.cn.util; + +import com.alibaba.druid.pool.DruidDataSourceFactory; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Properties; + +/** + * Druid连接池的工具类 + */ +public class MariaDBUtils { + private static DataSource ds ; + + static{ + try { + Properties pro = new Properties(); +// pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties")); + ds = DruidDataSourceFactory.createDataSource(pro); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 获取连接 + */ + public static Connection getConnection() throws SQLException { + return ds.getConnection(); + } + + /** + * 释放资源 + */ + public static void close(Statement stmt,Connection conn){ + + close(null,stmt,conn); + } + + + public static void close(ResultSet rs , Statement stmt, Connection conn){ + + + if(rs != null){ + try { + rs.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + + if(stmt != null){ + try { + stmt.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + if(conn != null){ + try { + conn.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + } + + /** + * 获取连接池方法 + */ + public static DataSource getDataSource(){ + return ds; + } + +} \ No newline at end of file diff --git a/src/main/java/com/mesasoft/cn/util/MariaDbBase.java b/src/main/java/com/mesasoft/cn/util/MariaDbBase.java new file mode 100644 index 0000000..777ec44 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/util/MariaDbBase.java @@ -0,0 +1,84 @@ +package com.mesasoft.cn.util; + +import com.mesasoft.cn.sketch.config.ApplicationConfig; +import org.apache.log4j.Logger; + +import java.sql.*; +import java.util.Date; +import java.util.Properties; + +/** + * Created with IntelliJ IDEA. + * User: joy + * Date: 2021/12/28 + * Time: 2:56 PM + * Description: No Description + */ +public class MariaDbBase { + + private static final Logger LOG = Logger.getLogger(MariaDbBase.class); + private static final Properties props = new Properties(); + + private final Statement statement; + + public MariaDbBase(Connection conn, Statement stat) { + statement = stat; + } + + /** + * 执行写入sql + */ + public void writeSqlExecute(String sql){ + try { + statement.executeUpdate(sql); + } catch (SQLIntegrityConstraintViolationException e){ + LOG.error("Duplicated entry for key 'PRIMARY'"); + } catch (SQLException exception) { + LOG.error("Sql : " + sql); + exception.printStackTrace(); + } + } + + /** + * 执行查询sql + */ + public ResultSet querySqlExecute(String sql){ + ResultSet set = null; + try { + set = statement.executeQuery(sql); + } catch (SQLException exception) { + exception.printStackTrace(); + } + return set; + } + + + /** + * 获得指定表格、按指定时间字段的过期记录 + * @param tableName 库表名称 + * @param timeColumnName 时间列名 + * @return 查询结果 + */ + public ResultSet getExpiredRecord(String tableName, String timeColumnName){ + Date lastUpdateTime = new Timestamp(getExpiredTime(ApplicationConfig.UPDATE_EXPIRED_DAY).getTime()); + + String resSql = "SELECT *" + + " FROM " + ApplicationConfig.DATABASE + "." + tableName + + " WHERE " + timeColumnName + " < '" + lastUpdateTime + '\''; + + LOG.debug("Update task: expired query sql" + resSql); + + return querySqlExecute(resSql); + } + + /** + * TODO: getUnlabeledRecord() 考虑多个来源的情况 + */ + + /** + * 获得过期时间, 当前时间的expiredRangeDays天之前的日期为过期日期 + */ + public static Date getExpiredTime(int expiredRangeDays){ + return new Timestamp(TimeUtils.getStartOfDay(-expiredRangeDays).getTime()); + } +} diff --git a/src/main/java/com/mesasoft/cn/util/ServiceUtils.java b/src/main/java/com/mesasoft/cn/util/ServiceUtils.java new file mode 100644 index 0000000..2e3b542 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/util/ServiceUtils.java @@ -0,0 +1,59 @@ +package com.mesasoft.cn.util; + +import com.mesasoft.cn.service.ICategoryService; +import com.mesasoft.cn.service.IFileService; +import com.mesasoft.cn.service.IUserService; +import com.zhazhapan.modules.constant.ValueConsts; +import com.zhazhapan.util.Checker; +import com.zhazhapan.util.ReflectUtils; +import org.apache.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.lang.reflect.InvocationTargetException; + +/** + * @author pantao + * @since 2018/2/28 + */ +@Component +public class ServiceUtils { + + private static IUserService userService; + + private static IFileService fileService; + + private static ICategoryService categoryService; + + private static Logger logger = Logger.getLogger(ServiceUtils.class); + + @Autowired + public ServiceUtils(IUserService userService, IFileService fileService, ICategoryService categoryService) { + ServiceUtils.userService = userService; + ServiceUtils.fileService = fileService; + ServiceUtils.categoryService = categoryService; + } + + public static int getUserId(String usernameOrEmail) { + return Checker.isEmpty(usernameOrEmail) ? ValueConsts.ZERO_INT : userService.getUserId(usernameOrEmail); + } + + public static long getFileId(String fileName) { + return Checker.isEmpty(fileName) ? ValueConsts.ZERO_INT : fileService.getFileId(fileName); + } + + public static int getCategoryId(String categoryName) { + return Checker.isEmpty(categoryName) ? ValueConsts.ZERO_INT : categoryService.getIdByName(categoryName); + } + + public static Object invokeFileFilter(Object object, String methodName, String user, String file, String + category, int offset) { + try { + return ReflectUtils.invokeMethodUseBasicType(object, methodName, new Object[]{getUserId(user), getFileId + (file), file, getCategoryId(category), offset}); + } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + logger.error(e.getMessage()); + return null; + } + } +} diff --git a/src/main/java/com/mesasoft/cn/util/TimeUtils.java b/src/main/java/com/mesasoft/cn/util/TimeUtils.java new file mode 100644 index 0000000..5e128dd --- /dev/null +++ b/src/main/java/com/mesasoft/cn/util/TimeUtils.java @@ -0,0 +1,59 @@ +package com.mesasoft.cn.util; + +import java.util.Calendar; +import java.util.Date; + +/** + * @author yjy + * @version 1.0 + * @date 2021/2/25 11:26 上午 + */ +public class TimeUtils { + public static final Long HOUR_TO_MILLISECONDS = 3600000L; + public static final Long DAY_TO_MILLSEDONDS = 86400000L; + public static final Integer SECOND_TO_MILLSEDONDS = 1000; + + + /** + * 获得当前时间小时的起始(0分钟)时间 + */ + public static Date getStartOfHour() { + return getStartOfHour(0); + } + public static Date getStartOfHour(Integer offset) { + Calendar ca = Calendar.getInstance(); + ca.add(Calendar.HOUR, offset); + ca.set(Calendar.MINUTE, 0); + ca.set(Calendar.SECOND, 0); + ca.set(Calendar.MILLISECOND, 0); + return ca.getTime(); + } + + /** + * 获得当前日期的起始(0时)时间 + */ + public static Date getStartOfDay() { + return getStartOfDay(0); + } + public static Date getStartOfDay(Integer bias) { + Calendar ca = Calendar.getInstance(); + ca.add(Calendar.DATE, bias); + ca.set(Calendar.HOUR, -12); + ca.set(Calendar.MINUTE, 0); + ca.set(Calendar.SECOND, 0); + ca.set(Calendar.MILLISECOND, 0); + return ca.getTime(); + } + + public static Date getStartOfMonth() { + Calendar ca = Calendar.getInstance(); + ca.set(Calendar.DATE, 1); + ca.set(Calendar.HOUR, -12); + ca.set(Calendar.MINUTE, 0); + ca.set(Calendar.SECOND, 0); + ca.set(Calendar.MILLISECOND, 0); + return ca.getTime(); + } + +} + diff --git a/src/main/java/com/mesasoft/cn/util/ValidationUtils.java b/src/main/java/com/mesasoft/cn/util/ValidationUtils.java new file mode 100644 index 0000000..013008e --- /dev/null +++ b/src/main/java/com/mesasoft/cn/util/ValidationUtils.java @@ -0,0 +1,202 @@ +package com.mesasoft.cn.util; + +import com.mesasoft.cn.sketch.api.BrightCloud; +import com.mesasoft.cn.sketch.config.ApplicationConfig; +import org.apache.commons.lang3.StringUtils; +import org.apache.log4j.Logger; +import sun.net.util.IPAddressUtil; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ValidationUtils { + private static final Logger LOG = Logger.getLogger(ValidationUtils.class); + + /** + * 获取二级域名 + **/ + public static String getSecDomain(String fqdnOrUrl){ + String filePath = Objects.requireNonNull(BrightCloud.class.getClassLoader() + .getResource(ApplicationConfig.TLD_FILE)).getFile(); + HashMap> maps = readTopDomainFile(filePath); + try { + String[] split = fqdnOrUrl.split("\\."); + String secDomain = null; + for (int i = split.length - 1; i >= 0; i--) { + int maps_index = split.length - (i + 1); + HashMap innerMap = maps.get("map_id_" + maps_index); + HashMap fullTop = maps.get("full"); + if (!(innerMap.containsKey(split[i]))) { + String strSec = ""; + for (int j = i; j < split.length; j++) { + strSec += (split[j] + "."); + } + secDomain = strSec.substring(0, strSec.length() - 1); + if (fullTop.containsKey(getTopFromSecDomain(secDomain))) { + break; + } else { + while (!fullTop.containsKey(getTopFromSecDomain(secDomain)) && getTopFromSecDomain(secDomain).contains(".")) { + secDomain = getTopFromSecDomain(secDomain); + } + break; + } + } + } + // 右匹配为顶级域名 + if (secDomain == null){ + secDomain = fqdnOrUrl; + } + return secDomain; + } catch (Exception e) { + LOG.error("urlDomain:" + fqdnOrUrl); + e.printStackTrace(); + return "---no---return---"; + } + } + + public static List getSecDomain(List fqdnOrUrls) { + HashMap> maps = readTopDomainFile(ApplicationConfig.TLD_FILE); + List secDomainList = new ArrayList<>(); + for (String oriDomain : fqdnOrUrls) { + String secDomain = getSecDomain(oriDomain); + if (StringUtils.isNotBlank(secDomain) && !("---no---return---".equals(secDomain))) { + secDomainList.add(secDomain); + } else { + System.out.println(oriDomain); + } + } + return secDomainList; + } + + public static String getTopFromSecDomain(String secDomain) { + String quFirstDian = secDomain; + if (secDomain.contains(".")) { + quFirstDian = secDomain.substring(secDomain.indexOf(".")).substring(1); + } + return quFirstDian; + } + + public static HashMap> readTopDomainFile(String filePath) { + HashMap> maps = makeHashMap(filePath); + try { + String encoding = "UTF-8"; + File file = new File(filePath); + if (file.isFile() && file.exists()) { + InputStreamReader read = new InputStreamReader( + new FileInputStream(file), encoding); + BufferedReader bufferedReader = new BufferedReader(read); + String lineTxt = null; + while ((lineTxt = bufferedReader.readLine()) != null) { + HashMap fullTop = maps.get("full"); + fullTop.put(lineTxt, lineTxt); + maps.put("full", fullTop); + String[] split = lineTxt.split("\\."); + for (int i = split.length - 1; i >= 0; i--) { + int maps_index = split.length - (i + 1); + HashMap innerMap = maps.get("map_id_" + maps_index); + innerMap.put(split[i], split[i]); + maps.put("map_id_" + maps_index, innerMap); + } + } + read.close(); + } else { + LOG.error("TopDomainUtils>=>readTopDomainFile filePath is wrong--->{" + filePath + "}<---"); + } + } catch (Exception e) { + LOG.error("TopDomainUtils>=>readTopDomainFile get filePathData error--->{" + e + "}<---"); + e.printStackTrace(); + } + return maps; + } + + public static HashMap> makeHashMap(String filePath) { + int maxLength = FileUtils.getMaxLength(filePath); + HashMap> maps = new HashMap>(); + for (int i = 0; i < maxLength; i++) { + maps.put("map_id_" + i, new HashMap()); + } + maps.put("full", new HashMap()); + return maps; + } + + public static List getChecked(List objectList, String type){ + if (type.equals("ip")){ + return getCheckedIps(objectList); + } + if (type.equals("domain")){ + return getCheckedFqdns(objectList); + } + LOG.error("Wrong type to be checked: " + type); + return objectList; + } + + public static List getCheckedFqdns(List fqdns){ + List res = new ArrayList<>(); + for (String fqdn:fqdns){ + //去端口号 + fqdn = fqdn.split(":")[0]; + // 去重 & 校验 + if (isValidDomain(fqdn) && !res.contains(fqdn)){ + res.add(fqdn.toLowerCase()); + } else { + LOG.debug("Bad or duplicated fqdn:" + fqdn); + } + } + return res; + } + + public static List getCheckedIps(List ipList){ + List res = new ArrayList<>(); + for (String ip:ipList){ + //去端口号 + ip = ip.split(":")[0]; + // 去重 & 校验 + if (isValidIp(ip) && !res.contains(ip)){ + res.add(ip.toLowerCase()); + } else { + LOG.debug("Bad or duplicated fqdn:" + ip); + } + } + return res; + } + + public static boolean isValidIp(String ip){ + boolean iPv4LiteralAddress = IPAddressUtil.isIPv4LiteralAddress(ip); + boolean iPv6LiteralAddress = IPAddressUtil.isIPv6LiteralAddress(ip); + return iPv4LiteralAddress || iPv6LiteralAddress; + } + + private static boolean isValidDomain(String str) + { + String regex = "^((?!-)[A-Za-z0-9-_]" + + "{1,63}(? 0) { + request.getSession().setAttribute(DefaultValues.CODE_STRING, code); + logger.info("verify code: " + code); + jsonObject.put("status", "success"); + } else { + jsonObject.put("status", "error"); + } + return jsonObject.toString(); + } + + @ApiOperation(value = "验证验证码是否正确") + @AuthInterceptor(InterceptorLevel.NONE) + @RequestMapping(value = "/{code}/verification", method = RequestMethod.PUT) + public String verifyCode(@PathVariable("code") String code) { + boolean isSuccess = Checker.checkNull(code).equals(String.valueOf(request.getSession().getAttribute + (DefaultValues.CODE_STRING))); + return ControllerUtils.getResponse(isSuccess); + } +} diff --git a/src/main/java/com/mesasoft/cn/web/controller/ConfigController.java b/src/main/java/com/mesasoft/cn/web/controller/ConfigController.java new file mode 100644 index 0000000..e13bf35 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/web/controller/ConfigController.java @@ -0,0 +1,92 @@ +package com.mesasoft.cn.web.controller; + +import com.mesasoft.cn.SketchApplication; +import com.mesasoft.cn.modules.constant.DefaultValues; +import com.mesasoft.cn.annotation.AuthInterceptor; +import com.mesasoft.cn.entity.User; +import com.mesasoft.cn.enums.InterceptorLevel; +import com.mesasoft.cn.service.IConfigService; +import com.zhazhapan.modules.constant.ValueConsts; +import com.zhazhapan.util.FileExecutor; +import com.zhazhapan.util.NetUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.apache.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +/** + * @author pantao + * @since 2018/1/22 + */ +@RestController +@RequestMapping("/config") +@Api(value = "/config", description = "配置文件的相关操作") +public class ConfigController { + + private static Logger logger = Logger.getLogger(ConfigController.class); + + private final IConfigService configService; + + private final HttpServletRequest request; + + @Autowired + public ConfigController(IConfigService configService, HttpServletRequest request) { + this.configService = configService; + this.request = request; + } + + @ApiOperation(value = "更新配置文件") + @ApiImplicitParam(name = "config", value = "配置文件内容", required = true) + @AuthInterceptor(InterceptorLevel.ADMIN) + @RequestMapping(value = "", method = RequestMethod.PUT) + public String updateConfig(String config) { + User user = (User) request.getSession().getAttribute(ValueConsts.USER_STRING); + if (user.getPermission() > ValueConsts.TWO_INT) { + SketchApplication.settings.setJsonObject(config); + //打包成jar之后无法修改config.json文件 + try { + FileExecutor.saveFile(NetUtils.urlToString(SketchApplication.class.getResource(DefaultValues + .SETTING_PATH)), SketchApplication.settings.toString()); + } catch (IOException e) { + logger.error(e.getMessage()); + return "{\"message\":\"internal error, cannot save\"}"; + } + return "{\"message\":\"saved successfully\"}"; + } else { + return "{\"message\":\"permission denied\"}"; + } + } + + @ApiOperation(value = "获取配置文件内容") + @AuthInterceptor(InterceptorLevel.ADMIN) + @RequestMapping(value = "/all", method = RequestMethod.GET) + public String getAll() { + User user = (User) request.getSession().getAttribute(ValueConsts.USER_STRING); + if (user.getPermission() > ValueConsts.TWO_INT) { + return SketchApplication.settings.toString(); + } else { + return "{\"message\":\"permission denied\"}"; + } + } + + @ApiOperation(value = "获取配置文件中的全局相关配置内容") + @AuthInterceptor(InterceptorLevel.NONE) + @RequestMapping(value = "/global", method = RequestMethod.GET) + public String getGlobalConfig() { + return configService.getGlobalConfig(); + } + + @ApiOperation(value = "获取配置文件中的用户相关配置内容") + @AuthInterceptor(InterceptorLevel.NONE) + @RequestMapping(value = "/user", method = RequestMethod.GET) + public String getUserConfig() { + return configService.getUserConfig(); + } +} diff --git a/src/main/java/com/mesasoft/cn/web/controller/CustomErrorController.java b/src/main/java/com/mesasoft/cn/web/controller/CustomErrorController.java new file mode 100644 index 0000000..bc22a1d --- /dev/null +++ b/src/main/java/com/mesasoft/cn/web/controller/CustomErrorController.java @@ -0,0 +1,42 @@ +package com.mesasoft.cn.web.controller; + +import com.mesasoft.cn.annotation.AuthInterceptor; +import com.mesasoft.cn.enums.InterceptorLevel; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.boot.web.servlet.error.ErrorController; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import springfox.documentation.annotations.ApiIgnore; + +/** + * @author pantao + * @since 2018/1/22 + */ +@Controller +@Api(description = "错误页面映射") +public class CustomErrorController implements ErrorController { + + @ApiOperation(value = "异常页面") + @AuthInterceptor(InterceptorLevel.NONE) + @RequestMapping("/exception") + public String handleError() { + return "error"; + } + + @ApiOperation(value = "404、错误页面") + @AuthInterceptor(InterceptorLevel.NONE) + @RequestMapping("/error") + @ResponseStatus(HttpStatus.NOT_FOUND) + public String handleNotFound() { + return "/404"; + } + + @ApiIgnore + @Override + public String getErrorPath() { + return "/error"; + } +} \ No newline at end of file diff --git a/src/main/java/com/mesasoft/cn/web/controller/DownloadedController.java b/src/main/java/com/mesasoft/cn/web/controller/DownloadedController.java new file mode 100644 index 0000000..324aeaa --- /dev/null +++ b/src/main/java/com/mesasoft/cn/web/controller/DownloadedController.java @@ -0,0 +1,41 @@ +package com.mesasoft.cn.web.controller; + +import com.mesasoft.cn.annotation.AuthInterceptor; +import com.mesasoft.cn.enums.InterceptorLevel; +import com.mesasoft.cn.service.IDownloadedService; +import com.zhazhapan.util.Formatter; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author pantao + * @since 2018/2/9 + */ +@RestController +@RequestMapping(value = "/downloaded") +@Api(value = "/downloaded", description = "下载记录相关操作") +public class DownloadedController { + + private final IDownloadedService downloadService; + + @Autowired + public DownloadedController(IDownloadedService downloadService) { + this.downloadService = downloadService; + } + + @ApiOperation(value = "获取文件下载记录") + @ApiImplicitParams({@ApiImplicitParam(name = "user", value = "指定用户(默认所有用户)"), @ApiImplicitParam(name = + "指定文件(默认所有文件)"), @ApiImplicitParam(name = "category", value = "指定分类(默认所有分类)"), @ApiImplicitParam(name = + "offset", value = "偏移量", required = true)}) + @AuthInterceptor(InterceptorLevel.ADMIN) + @RequestMapping(value = "all", method = RequestMethod.GET) + public String getAll(String user, String file, String category, int offset) { + return Formatter.listToJson(downloadService.list(user, file, category, offset)); + } +} diff --git a/src/main/java/com/mesasoft/cn/web/controller/FileController.java b/src/main/java/com/mesasoft/cn/web/controller/FileController.java new file mode 100644 index 0000000..6ae6d57 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/web/controller/FileController.java @@ -0,0 +1,220 @@ +package com.mesasoft.cn.web.controller; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.mesasoft.cn.SketchApplication; +import com.mesasoft.cn.modules.constant.ConfigConsts; +import com.mesasoft.cn.util.BeanUtils; +import com.mesasoft.cn.annotation.AuthInterceptor; +import com.mesasoft.cn.entity.User; +import com.mesasoft.cn.enums.InterceptorLevel; +import com.mesasoft.cn.service.IFileService; +import com.mesasoft.cn.util.ControllerUtils; +import com.zhazhapan.modules.constant.ValueConsts; +import com.zhazhapan.util.Checker; +import com.zhazhapan.util.FileExecutor; +import com.zhazhapan.util.Formatter; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.IOException; + +/** + * @author pantao + * @since 2018/1/29 + */ +@RestController +@RequestMapping("/file") +@Api(value = "/file", description = "文件相关操作") +public class FileController { + + private final IFileService fileService; + + private final HttpServletRequest request; + + private final JSONObject jsonObject; + + @Autowired + public FileController(IFileService fileService, HttpServletRequest request, JSONObject jsonObject) { + this.fileService = fileService; + this.request = request; + this.jsonObject = jsonObject; + } + + @ApiOperation(value = "获取我的下载记录") + @ApiImplicitParams({@ApiImplicitParam(name = "offset", value = "偏移量", required = true), @ApiImplicitParam(name = + "search", value = "记录匹配(允许为空)")}) + @AuthInterceptor(InterceptorLevel.USER) + @RequestMapping(value = "/user/downloaded", method = RequestMethod.GET) + public String getUserDownloaded(int offset, String search) { + User user = (User) request.getSession().getAttribute(ValueConsts.USER_STRING); + return Formatter.listToJson(fileService.listUserDownloaded(user.getId(), offset, search)); + } + + @ApiOperation(value = "获取我的上传记录") + @ApiImplicitParams({@ApiImplicitParam(name = "offset", value = "偏移量", required = true), @ApiImplicitParam(name = + "search", value = "记录匹配(允许为空)")}) + @AuthInterceptor(InterceptorLevel.USER) + @RequestMapping(value = "/user/uploaded", method = RequestMethod.GET) + public String getUserUploaded(int offset, String search) { + User user = (User) request.getSession().getAttribute(ValueConsts.USER_STRING); + return Formatter.listToJson(fileService.listUserUploaded(user.getId(), offset, search)); + } + + @ApiOperation(value = "文件上传") + @ApiImplicitParams({@ApiImplicitParam(name = "categoryId", value = "分类ID", required = true), @ApiImplicitParam + (name = "tag", value = "文件标签"), @ApiImplicitParam(name = "description", value = "文件描述"), + @ApiImplicitParam(name = "prefix", value = "文件前缀(仅适用于管理员上传文件,普通用户无效)")}) + @AuthInterceptor(InterceptorLevel.USER) + @RequestMapping(value = "", method = RequestMethod.POST) + public String upload(int categoryId, String tag, String description, String prefix, @RequestParam("file") + MultipartFile multipartFile) { + User user = (User) request.getSession().getAttribute(ValueConsts.USER_STRING); + return ControllerUtils.getResponse(fileService.upload(categoryId, tag, description, prefix, multipartFile, + user)); + } + + @ApiOperation(value = "获取文件记录") + @ApiImplicitParams({@ApiImplicitParam(name = "offset", value = "偏移量", required = true), @ApiImplicitParam(name = + "categoryId", value = "分类ID", required = true), @ApiImplicitParam(name = "orderBy", value = "排序方式", + required = true, example = "id desc"), @ApiImplicitParam(name = "search", value = "记录匹配(允许为空)")}) + @AuthInterceptor(InterceptorLevel.NONE) + @RequestMapping(value = "/all", method = RequestMethod.GET) + public String getAll(int offset, int categoryId, String orderBy, String search) { + User user = (User) request.getSession().getAttribute(ValueConsts.USER_STRING); + boolean canGet = SketchApplication.settings.getBooleanUseEval(ConfigConsts.ANONYMOUS_VISIBLE_OF_SETTING) || + (Checker.isNotNull(user) && user.getIsVisible() == 1); + if (canGet) { + int userId = Checker.isNull(user) ? 0 : user.getId(); + return Formatter.listToJson(fileService.listAll(userId, offset, categoryId, orderBy, search)); + } else { + jsonObject.put("error", "权限被限制,无法获取资源,请联系管理员"); + return jsonObject.toString(); + } + } + + @ApiOperation(value = "删除指定文件") + @AuthInterceptor(InterceptorLevel.USER) + @RequestMapping(value = "/{id}", method = RequestMethod.DELETE) + public String removeFile(@PathVariable("id") long id) { + User user = (User) request.getSession().getAttribute(ValueConsts.USER_STRING); + jsonObject.put("status", "error"); + if (Checker.isNull(user)) { + jsonObject.put("message", "请先登录"); + } else if (id < 1) { + jsonObject.put("message", "格式不合法"); + } else if (fileService.removeFile(user, id)) { + jsonObject.put("status", "success"); + } else { + jsonObject.put("message", "删除失败,权限不够,请联系管理员"); + } + return jsonObject.toString(); + } + + @ApiOperation(value = "更新文件属性") + @ApiImplicitParams({@ApiImplicitParam(name = "name", value = "文件名", required = true), @ApiImplicitParam(name = + "category", value = "分类名称", required = true), @ApiImplicitParam(name = "tag", value = "文件标签", required = + true), @ApiImplicitParam(name = "description", value = "文件描述", required = true)}) + @AuthInterceptor(InterceptorLevel.USER) + @RequestMapping(value = "/{id}", method = RequestMethod.PUT) + public String updateFileInfo(@PathVariable("id") long id, String name, String category, String tag, String + description) { + User user = (User) request.getSession().getAttribute(ValueConsts.USER_STRING); + jsonObject.put("status", "error"); + if (fileService.updateFileInfo(id, user, name, category, tag, description)) { + jsonObject.put("status", "success"); + } else { + jsonObject.put("message", "格式不正确或权限不够,更新失败,请联系管理员"); + } + return jsonObject.toString(); + } + + @ApiOperation(value = "获取所有文件的基本信息") + @ApiImplicitParams({@ApiImplicitParam(name = "user", value = "指定用户(默认所有用户)"), @ApiImplicitParam(name = "file", + value = "指定文件(默认所有文件)"), @ApiImplicitParam(name = "category", value = "指定分类(默认所有分类)"), @ApiImplicitParam + (name = "offset", value = "偏移量", required = true)}) + @AuthInterceptor(InterceptorLevel.ADMIN) + @RequestMapping(value = "/basic/all", method = RequestMethod.GET) + public String getBasicAll(String user, String file, String category, int offset) { + return Formatter.listToJson(fileService.listBasicAll(user, file, category, offset)); + } + + @ApiOperation(value = "通过文件路径获取服务器端的文件") + @ApiImplicitParam(name = "path", value = "文件路径(默认根目录)") + @AuthInterceptor(InterceptorLevel.ADMIN) + @RequestMapping(value = "/server", method = RequestMethod.GET) + public String getServerFilesByPath(String path) { + File[] files = FileExecutor.listFile(Checker.isEmpty(path) ? (Checker.isWindows() ? "C:\\" : "/") : path); + JSONArray array = new JSONArray(); + if (Checker.isNotNull(files)) { + for (File file : files) { + array.add(BeanUtils.beanToJson(file)); + } + } + return array.toJSONString(); + } + + @ApiOperation("分享服务器端文件") + @ApiImplicitParams({@ApiImplicitParam(name = "prefix", value = "自定义前缀(可空)"), @ApiImplicitParam(name = "files", + value = "文件", required = true, example = "file1,file2,file3")}) + @AuthInterceptor(InterceptorLevel.ADMIN) + @RequestMapping(value = "/server/share", method = RequestMethod.POST) + public String shareFile(String prefix, String files) { + User user = (User) request.getSession().getAttribute(ValueConsts.USER_STRING); + return ControllerUtils.getResponse(fileService.shareFiles(Checker.checkNull(prefix), files, user)); + } + + @ApiOperation(value = "更新文件路径(包括本地路径,访问路径,如果新的本地路径和访问路径均为空,这什么也不会做)") + @ApiImplicitParams({@ApiImplicitParam(name = "oldLocalUrl", value = "文件本地路径", required = true), @ApiImplicitParam + (name = "localUrl", value = "新的本地路径(可空)"), @ApiImplicitParam(name = "visitUrl", value = "新的访问路径(可空)")}) + @AuthInterceptor(InterceptorLevel.ADMIN) + @RequestMapping(value = "/{id}/url", method = RequestMethod.PUT) + public String uploadFileUrl(@PathVariable("id") int id, String oldLocalUrl, String localUrl, String visitUrl) { + boolean[] b = fileService.updateUrl(id, oldLocalUrl, localUrl, visitUrl); + String responseJson = "{status:{localUrl:" + b[0] + ",visitUrl:" + b[1] + "}}"; + return Formatter.formatJson(responseJson); + } + + @ApiOperation(value = "批量删除文件") + @AuthInterceptor(InterceptorLevel.ADMIN) + @RequestMapping(value = "/batch/{ids}", method = RequestMethod.DELETE) + public String deleteFiles(@PathVariable("ids") String ids) { + return ControllerUtils.getResponse(fileService.deleteFiles(ids)); + } + + @ApiOperation(value = "获取指定文件的权限记录") + @AuthInterceptor(InterceptorLevel.ADMIN) + @RequestMapping(value = "/{id}/auth", method = RequestMethod.GET) + public String getAuth(@PathVariable("id") long id) { + return BeanUtils.toPrettyJson(fileService.getAuth(id)); + } + + @ApiOperation(value = "更新指定文件的权限") + @ApiImplicitParam(name = "auth", value = "权限", required = true, example = "1,1,1,1") + @AuthInterceptor(InterceptorLevel.ADMIN) + @RequestMapping(value = "/{id}/auth", method = RequestMethod.PUT) + public String updateAuth(@PathVariable("id") long id, String auth) { + return ControllerUtils.getResponse(fileService.updateAuth(id, auth)); + } + + /** + * 资源下载 + * + * @param response {@link HttpServletResponse} + */ + @ApiOperation(value = "通过访问路径获取文件资源") + @AuthInterceptor(InterceptorLevel.NONE) + @RequestMapping(value = "/**", method = RequestMethod.GET) + public void getResource(HttpServletResponse response) throws IOException { + ControllerUtils.loadResource(response, fileService.getResource(request.getServletPath(), request), + ValueConsts.FALSE); + } +} diff --git a/src/main/java/com/mesasoft/cn/web/controller/FileMangerController.java b/src/main/java/com/mesasoft/cn/web/controller/FileMangerController.java new file mode 100644 index 0000000..ca7dbff --- /dev/null +++ b/src/main/java/com/mesasoft/cn/web/controller/FileMangerController.java @@ -0,0 +1,188 @@ +package com.mesasoft.cn.web.controller; + +import com.alibaba.fastjson.JSONObject; +import com.mesasoft.cn.annotation.AuthInterceptor; +import com.mesasoft.cn.enums.InterceptorLevel; +import com.mesasoft.cn.service.IFileManagerService; +import com.mesasoft.cn.util.ControllerUtils; +import com.zhazhapan.modules.constant.ValueConsts; +import com.zhazhapan.util.ArrayUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.MultipartHttpServletRequest; +import springfox.documentation.annotations.ApiIgnore; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Map; + +/** + * see api doc + * + * @author pantao + * @since 2018/1/29 + */ +@ApiIgnore +@RestController +@RequestMapping("/filemanager") +@AuthInterceptor(InterceptorLevel.SYSTEM) +public class FileMangerController { + + private final IFileManagerService fileManagerService; + + private final JSONObject jsonObject; + + @Autowired + public FileMangerController(IFileManagerService fileManagerService, JSONObject jsonObject) { + this.fileManagerService = fileManagerService; + this.jsonObject = jsonObject; + } + + @AuthInterceptor(InterceptorLevel.SYSTEM) + @RequestMapping(value = "/multidownload", method = RequestMethod.GET) + public void multiDownload(HttpServletResponse response, String[] items, String toFilename) throws IOException { + ControllerUtils.setResponseFileName(response, toFilename); + fileManagerService.multiDownload(response, items, toFilename); + } + + @AuthInterceptor(InterceptorLevel.SYSTEM) + @RequestMapping(value = "/download", method = RequestMethod.GET) + public void download(HttpServletResponse response, String path) throws IOException { + ControllerUtils.loadResource(response, path, ValueConsts.TRUE); + } + public static String getEncoding(String str) { + String encode = "GB2312"; + String finecode = ""; + try { + if (str.equals(new String(str.getBytes(encode), encode))) { + finecode = encode; + } + } catch (Exception exception) { + } + encode = "ISO-8859-1"; + try { + if (str.equals(new String(str.getBytes(encode), encode))) { + finecode = encode; + } + } catch (Exception exception1) { + } + encode = "GBK"; + try { + if (str.equals(new String(str.getBytes(encode), encode))) { + finecode = encode; + } + } catch (Exception exception1) { + } + encode = "UTF-8"; + try { + if (str.equals(new String(str.getBytes(encode), encode))) { + finecode = encode; + } + } catch (Exception exception2) { + } + encode = "GBK"; + try { + if (str.equals(new String(str.getBytes(encode), encode))) { + finecode = encode; + } + } catch (Exception exception3) { + } + return finecode; + } + /** + * 暂时没有找到更好的解决方案 + * + * @param destination 目的 + * + * @return 响应结果 + */ + @AuthInterceptor(InterceptorLevel.SYSTEM) + @RequestMapping(value = "/upload", method = RequestMethod.POST) + public String upload(String destination, MultipartHttpServletRequest request) { + Map fileMap = request.getFileMap(); + MultipartFile[] files = ArrayUtils.mapToArray(fileMap, MultipartFile.class); + jsonObject.put("result", fileManagerService.upload(destination, files)); + return jsonObject.toJSONString(); + } + + @AuthInterceptor(InterceptorLevel.SYSTEM) + @RequestMapping(value = "/extract", method = RequestMethod.POST) + public String extract(@RequestBody JSONObject json) { + jsonObject.put("result", fileManagerService.extract(json)); + return jsonObject.toJSONString(); + } + + @AuthInterceptor(InterceptorLevel.SYSTEM) + @RequestMapping(value = "/compress", method = RequestMethod.POST) + public String compress(@RequestBody JSONObject json) { + jsonObject.put("result", fileManagerService.compress(json)); + return jsonObject.toJSONString(); + } + + @AuthInterceptor(InterceptorLevel.SYSTEM) + @RequestMapping(value = "/permission", method = RequestMethod.POST) + public String setPermission(@RequestBody JSONObject json) { + jsonObject.put("result", fileManagerService.setPermission(json)); + return jsonObject.toJSONString(); + } + + @AuthInterceptor(InterceptorLevel.SYSTEM) + @RequestMapping(value = "/folder", method = RequestMethod.POST) + public String createFolder(@RequestBody JSONObject json) { + jsonObject.put("result", fileManagerService.createFolder(json)); + return jsonObject.toJSONString(); + } + + @AuthInterceptor(InterceptorLevel.SYSTEM) + @RequestMapping(value = "/content", method = RequestMethod.POST) + public String getContent(@RequestBody JSONObject json) { + jsonObject.put("result", fileManagerService.getContent(json)); + return jsonObject.toJSONString(); + } + + @AuthInterceptor(InterceptorLevel.SYSTEM) + @RequestMapping(value = "/edit", method = RequestMethod.POST) + public String edit(@RequestBody JSONObject json) { + jsonObject.put("result", fileManagerService.edit(json)); + return jsonObject.toJSONString(); + } + + @AuthInterceptor(InterceptorLevel.SYSTEM) + @RequestMapping(value = "/remove", method = RequestMethod.POST) + public String remove(@RequestBody JSONObject json) { + jsonObject.put("result", fileManagerService.remove(json)); + return jsonObject.toJSONString(); + } + + @AuthInterceptor(InterceptorLevel.SYSTEM) + @RequestMapping(value = "/copy", method = RequestMethod.POST) + public String copy(@RequestBody JSONObject json) { + jsonObject.put("result", fileManagerService.copy(json)); + return jsonObject.toJSONString(); + } + + @AuthInterceptor(InterceptorLevel.SYSTEM) + @RequestMapping(value = "/move", method = RequestMethod.POST) + public String move(@RequestBody JSONObject json) { + jsonObject.put("result", fileManagerService.move(json)); + return jsonObject.toJSONString(); + } + + @AuthInterceptor(InterceptorLevel.SYSTEM) + @RequestMapping(value = "/rename", method = RequestMethod.POST) + public String rename(@RequestBody JSONObject json) { + jsonObject.put("result", fileManagerService.rename(json)); + return jsonObject.toJSONString(); + } + + @AuthInterceptor(InterceptorLevel.SYSTEM) + @RequestMapping(value = "/list", method = RequestMethod.POST) + public String list(@RequestBody JSONObject json) { + jsonObject.put("result", fileManagerService.list(json)); + return jsonObject.toJSONString(); + } +} diff --git a/src/main/java/com/mesasoft/cn/web/controller/GlobalExceptionHandler.java b/src/main/java/com/mesasoft/cn/web/controller/GlobalExceptionHandler.java new file mode 100644 index 0000000..e096243 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/web/controller/GlobalExceptionHandler.java @@ -0,0 +1,49 @@ +package com.mesasoft.cn.web.controller; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.log.Log; +import cn.hutool.log.LogFactory; +import com.mesasoft.cn.entity.Result; +import com.mesasoft.cn.entity.ResultEntity; +import com.mesasoft.cn.enums.StatusEnum; +import com.mesasoft.cn.exception.BusinessException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.context.request.async.AsyncRequestTimeoutException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@RestControllerAdvice +public class GlobalExceptionHandler { + private static final Log log = LogFactory.get(); + + + @ExceptionHandler(AsyncRequestTimeoutException.class) //捕获特定异常 + public void handleAsyncRequestTimeoutException(AsyncRequestTimeoutException e, HttpServletRequest request) { + log.info("Handle Async Request Timeout Exception"); + } + + + @ExceptionHandler(Exception.class) + public ResultEntity handleException(Exception e, HttpServletRequest request, HttpServletResponse response) { + response.setStatus(StatusEnum.FAIL.getStatus()); + String message = e.getMessage() + (e.getCause() != null ? e.getCause().getMessage() : ""); + log.error("message:{}, stackTrace:{}", message, getStackTrace(e)); + return Result.fail(e.getMessage()); + } + + @ExceptionHandler({BusinessException.class}) + public ResultEntity handleBusinessException(BusinessException e, HttpServletRequest request, HttpServletResponse response) { + response.setStatus(e.getStatus()); + String message = (e.getMessage() != null ? e.getMessage() : e.getMessage()) + " " + (e.getCause() != null ? e.getCause().getMessage() : ""); + log.error("message:{}.stackTrace:{}", message, getStackTrace(e)); + return Result.fail(e.getStatus(), e.getCode(), message); + } + + private String getStackTrace(Exception e) { + return ObjectUtil.isNotNull(e.getStackTrace()) ? e.getStackTrace()[0].toString() : ""; + } + + +} diff --git a/src/main/java/com/mesasoft/cn/web/controller/UploadedController.java b/src/main/java/com/mesasoft/cn/web/controller/UploadedController.java new file mode 100644 index 0000000..a99827f --- /dev/null +++ b/src/main/java/com/mesasoft/cn/web/controller/UploadedController.java @@ -0,0 +1,39 @@ +package com.mesasoft.cn.web.controller; + +import com.mesasoft.cn.annotation.AuthInterceptor; +import com.mesasoft.cn.enums.InterceptorLevel; +import com.mesasoft.cn.service.IUploadedService; +import com.zhazhapan.util.Formatter; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author pantao + * @since 2018/2/28 + */ +@RestController +@RequestMapping(value = "/uploaded") +@Api(value = "/uploaded", description = "上传记录相关操作") +public class UploadedController { + + private final IUploadedService uploadedService; + + @Autowired + public UploadedController(IUploadedService uploadedService) {this.uploadedService = uploadedService;} + + @ApiOperation(value = "获取文件上传记录") + @ApiImplicitParams({@ApiImplicitParam(name = "user", value = "指定用户(默认所有用户)"), @ApiImplicitParam(name = + "指定文件(默认所有文件)"), @ApiImplicitParam(name = "category", value = "指定分类(默认所有分类)"), @ApiImplicitParam(name = + "offset", value = "偏移量", required = true)}) + @AuthInterceptor(InterceptorLevel.ADMIN) + @RequestMapping(value = "all", method = RequestMethod.GET) + public String getAll(String user, String file, String category, int offset) { + return Formatter.listToJson(uploadedService.list(user, file, category, offset)); + } +} diff --git a/src/main/java/com/mesasoft/cn/web/controller/UserController.java b/src/main/java/com/mesasoft/cn/web/controller/UserController.java new file mode 100644 index 0000000..a76aad9 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/web/controller/UserController.java @@ -0,0 +1,281 @@ +package com.mesasoft.cn.web.controller; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.mesasoft.cn.SketchApplication; +import com.mesasoft.cn.modules.constant.ConfigConsts; +import com.mesasoft.cn.modules.constant.DefaultValues; +import com.mesasoft.cn.annotation.AuthInterceptor; +import com.mesasoft.cn.config.TokenConfig; +import com.mesasoft.cn.entity.Result; +import com.mesasoft.cn.entity.ResultEntity; +import com.mesasoft.cn.entity.User; +import com.mesasoft.cn.enums.InterceptorLevel; +import com.mesasoft.cn.service.IUserService; +import com.mesasoft.cn.util.ControllerUtils; +import com.zhazhapan.modules.constant.ValueConsts; +import com.zhazhapan.util.Checker; +import com.zhazhapan.util.Formatter; +import com.zhazhapan.util.encryption.JavaEncrypt; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import java.io.UnsupportedEncodingException; +import java.security.NoSuchAlgorithmException; + +/** + * @author pantao + * @since 2018/1/22 + */ +@RestController +@RequestMapping("/user") +@Api(value = "/user", description = "用户相关操作") +public class UserController { + + private final IUserService userService; + + private final HttpServletRequest request; + + private final JSONObject jsonObject; + + @Autowired + public UserController(IUserService userService, HttpServletRequest request, JSONObject jsonObject) { + this.userService = userService; + this.request = request; + this.jsonObject = jsonObject; + } + + @ApiOperation(value = "更新用户权限(注:不是文件权限)") + @AuthInterceptor(InterceptorLevel.ADMIN) + @RequestMapping(value = "/{id}/{permission}", method = RequestMethod.PUT) + public String updatePermission(@PathVariable("id") int id, @PathVariable("permission") int permission) { + User user = (User) request.getSession().getAttribute(ValueConsts.USER_STRING); + if (user.getPermission() < ValueConsts.THREE_INT && permission > 1) { + jsonObject.put("message", "权限不够,设置失败"); + } else if (userService.updatePermission(id, permission)) { + jsonObject.put("message", "更新成功"); + } else { + jsonObject.put("message", "更新失败,请稍后重新尝试"); + } + return jsonObject.toJSONString(); + } + + @ApiOperation("重置用户密码(管理员接口)") + @AuthInterceptor(InterceptorLevel.ADMIN) + @RequestMapping(value = "/reset/{id}/{password}", method = RequestMethod.PUT) + public String resetPassword(@PathVariable("id") int id, @PathVariable("password") String password) { + return ControllerUtils.getResponse(userService.resetPassword(id, password)); + } + + @ApiOperation(value = "更新用户的默认文件权限") + @ApiImplicitParam(name = "auth", value = "权限", example = "1,1,1,1", required = true) + @AuthInterceptor(InterceptorLevel.ADMIN) + @RequestMapping(value = "/{id}/auth", method = RequestMethod.PUT) + public String updateFileAuth(@PathVariable("id") int id, String auth) { + return ControllerUtils.getResponse(userService.updateFileAuth(id, auth)); + } + + @ApiOperation(value = "获取所有用户") + @ApiImplicitParams({@ApiImplicitParam(name = "user", value = "指定用户(默认所有用户)"), @ApiImplicitParam(name = "offset", + value = "偏移量", required = true)}) + @AuthInterceptor(InterceptorLevel.ADMIN) + @RequestMapping(value = "/all", method = RequestMethod.GET) + public String getUser(String user, int offset) { + User u = (User) request.getSession().getAttribute(ValueConsts.USER_STRING); + return Formatter.listToJson(userService.listUser(u.getPermission(), user, offset)); + } + + @ApiOperation(value = "更新我的基本信息") + @ApiImplicitParams({@ApiImplicitParam(name = "avatar", value = "头像(可空)"), @ApiImplicitParam(name = "realName", + value = "真实姓名(可空)"), @ApiImplicitParam(name = "email", value = "邮箱(可空)"), @ApiImplicitParam(name = + "code", value = "验证码(可空)")}) + @AuthInterceptor(InterceptorLevel.USER) + @RequestMapping(value = "/info", method = RequestMethod.PUT) + public String updateBasicInfo(String avatar, String realName, String email, String code) { + User user = (User) request.getSession().getAttribute(ValueConsts.USER_STRING); + jsonObject.put("message", "保存成功"); + boolean emilVerify = SketchApplication.settings.getBooleanUseEval(ConfigConsts.EMAIL_VERIFY_OF_SETTINGS); + if (Checker.isNotEmpty(email) && !email.equals(user.getEmail())) { + if (!emilVerify || isCodeValidate(code)) { + if (userService.emailExists(email)) { + jsonObject.put("message", "邮箱更新失败,该邮箱已经存在"); + } else { + user.setEmail(email); + } + } else { + jsonObject.put("message", "邮箱更新失败,验证码校验失败"); + } + } + if (userService.updateBasicInfoById(user.getId(), avatar, realName, user.getEmail())) { + user.setAvatar(avatar); + user.setRealName(realName); + jsonObject.put("status", "success"); + } else { + jsonObject.put("message", "服务器发生错误,请稍后重新尝试"); + } + jsonObject.put("email", user.getEmail()); + return jsonObject.toString(); + } + + @ApiOperation(value = "更新我的密码") + @ApiImplicitParams({@ApiImplicitParam(name = "oldPassword", value = "原密码", required = true), @ApiImplicitParam + (name = "newPassword", value = "新密码", required = true)}) + @AuthInterceptor(InterceptorLevel.USER) + @RequestMapping(value = "/password", method = RequestMethod.PUT) + public String updatePassword(String oldPassword, String newPassword) { + User user = (User) request.getSession().getAttribute(ValueConsts.USER_STRING); + jsonObject.put("status", "error"); + try { + if (user.getPassword().equals(JavaEncrypt.sha256(oldPassword))) { + if (userService.updatePasswordById(newPassword, user.getId())) { + jsonObject.put("status", "success"); + TokenConfig.removeTokenByValue(user.getId()); + } else { + jsonObject.put("message", "新密码格式不正确"); + } + } else { + jsonObject.put("message", "原密码不正确"); + } + } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { + jsonObject.put("message", "服务器内部错误,请稍后重新尝试"); + } + return jsonObject.toString(); + } + + @ApiOperation(value = "获取我的基本信息") + @AuthInterceptor(InterceptorLevel.USER) + @RequestMapping(value = "/info", method = RequestMethod.GET) + public String getInfo() { + User user = (User) request.getSession().getAttribute(ValueConsts.USER_STRING); + JSONObject object = JSON.parseObject(user.toString()); + object.remove(ValueConsts.ID_STRING); + object.remove(ValueConsts.PASSWORD_STRING); + return object.toString(); + } + + @ApiOperation(value = "登录(用户名密码和token必须有一个输入)") + @ApiImplicitParams({@ApiImplicitParam(name = "username", value = "用户名"), @ApiImplicitParam(name + = "password", value = "密码"), @ApiImplicitParam(name = "auto", value = "是否自动登录", dataType = "Boolean"), + @ApiImplicitParam(name = "token", value = "用于自动登录")}) + @AuthInterceptor(InterceptorLevel.NONE) + @RequestMapping(value = "/login", method = RequestMethod.PUT) + public String login(String username, String password, boolean auto, String token) { + //使用密码登录 + User user = userService.login(username, password, ValueConsts.NULL_STRING, ValueConsts.NULL_RESPONSE); + if (Checker.isNull(user) || user.getPermission() < 1) { + jsonObject.put("status", "failed"); + } else { + request.getSession().setAttribute(ValueConsts.USER_STRING, user); + jsonObject.put("status", "success"); + if (auto) { + jsonObject.put("token", TokenConfig.generateToken(token, user.getId())); + } else { + jsonObject.put("token", ""); + TokenConfig.removeTokenByValue(user.getId()); + } + } + return jsonObject.toString(); + } + + @ApiOperation(value = "用户注册(当不需要验证邮箱时,邮箱和验证码可空)") + @ApiImplicitParams({@ApiImplicitParam(name = "username", value = "用户名", required = true), @ApiImplicitParam(name + = "email", value = "邮箱"), @ApiImplicitParam(name = "password", value = "密码", required = true), + @ApiImplicitParam(name = "code", value = "验证码")}) + @AuthInterceptor(InterceptorLevel.NONE) + @RequestMapping(value = "/register", method = RequestMethod.POST) + public String register(String username, String email, String password, String code) { + boolean emilVerify = SketchApplication.settings.getBooleanUseEval(ConfigConsts.EMAIL_VERIFY_OF_SETTINGS); + jsonObject.put("status", "error"); + if (!emilVerify || isCodeValidate(code)) { + if (userService.usernameExists(username)) { + jsonObject.put("message", "用户名已经存在"); + } else if (userService.emailExists(email)) { + jsonObject.put("message", "该邮箱已经被注册啦"); + } else if (userService.register(username, email, password)) { + jsonObject.put("status", "success"); + } else { + jsonObject.put("message", "数据格式不合法"); + } + } else { + jsonObject.put("message", "验证码校验失败"); + } + return jsonObject.toString(); + } + + @ApiOperation(value = "重置我的密码") + @ApiImplicitParams({@ApiImplicitParam(name = "email", value = "邮箱", required = true), @ApiImplicitParam(name = + "code", value = "验证码", required = true), @ApiImplicitParam(name = "password", value = "密码", required = + true)}) + @AuthInterceptor(InterceptorLevel.NONE) + @RequestMapping(value = "/password/reset", method = RequestMethod.PUT) + public String resetPassword(String email, String code, String password) { + jsonObject.put("status", "error"); + if (isCodeValidate(code)) { + if (userService.resetPasswordByEmail(email, password)) { + jsonObject.put("status", "success"); + } else { + jsonObject.put("message", "格式不合法"); + } + } else { + jsonObject.put("message", "验证码校验失败"); + } + return jsonObject.toString(); + } + + @ApiOperation(value = "检测用户名是否已经注册") + @ApiImplicitParam(name = "username", value = "用户名", required = true) + @AuthInterceptor(InterceptorLevel.NONE) + @RequestMapping(value = "/username/exists", method = RequestMethod.GET) + public String usernameExists(String username) { + jsonObject.put("exists", userService.usernameExists(username)); + return jsonObject.toString(); + } + + @ApiOperation(value = "检测邮箱是否已经注册") + @ApiImplicitParam(name = "email", value = "邮箱", required = true) + @AuthInterceptor(InterceptorLevel.NONE) + @RequestMapping(value = "/email/exists", method = RequestMethod.GET) + public String emailExists(String email) { + jsonObject.put("exists", userService.emailExists(email)); + return jsonObject.toString(); + } + + private boolean isCodeValidate(String code) { + return Checker.checkNull(code).equals(String.valueOf(request.getSession().getAttribute(DefaultValues + .CODE_STRING))); + } + + + @ApiOperation(value = "登录(用户名密码和token必须有一个输入)") + @ApiImplicitParams({@ApiImplicitParam(name = "username", value = "用户名"), @ApiImplicitParam(name + = "password", value = "密码"), @ApiImplicitParam(name = "auto", value = "是否自动登录", dataType = "Boolean"), + @ApiImplicitParam(name = "token", value = "用于自动登录")}) + @AuthInterceptor(InterceptorLevel.NONE) + @RequestMapping(value = "/login2", method = RequestMethod.PUT) + public ResultEntity login2(String username, String password, boolean auto, String token) { + + JSONObject resultObject = null; + //使用密码登录 + User user = userService.login(username, password, ValueConsts.NULL_STRING, ValueConsts.NULL_RESPONSE); + if (Checker.isNull(user) || user.getPermission() < 1) { + return Result.fail(); + } else { + request.getSession().setAttribute(ValueConsts.USER_STRING, user); + if (auto) { + resultObject.put("token", TokenConfig.generateToken(token, user.getId())); + } else { + resultObject.put("token", ""); + TokenConfig.removeTokenByValue(user.getId()); + } + } + return Result.success(resultObject); + } +} diff --git a/src/main/java/com/mesasoft/cn/web/controller/ViewController.java b/src/main/java/com/mesasoft/cn/web/controller/ViewController.java new file mode 100644 index 0000000..33ad670 --- /dev/null +++ b/src/main/java/com/mesasoft/cn/web/controller/ViewController.java @@ -0,0 +1,63 @@ +package com.mesasoft.cn.web.controller; + +import com.mesasoft.cn.annotation.AuthInterceptor; +import com.mesasoft.cn.enums.InterceptorLevel; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import springfox.documentation.annotations.ApiIgnore; + +/** + * @author pantao + * @since 2018/1/25 + */ +@Controller +@Api(description = "视图页面映射") +public class ViewController { + + @ApiOperation(value = "远程文件管理页面") + @AuthInterceptor(InterceptorLevel.SYSTEM) + @RequestMapping(value = "/filemanager", method = RequestMethod.GET) + public String fileManager() { + return "/filemanager"; + } + + @ApiOperation(value = "上传页面") + @AuthInterceptor + @RequestMapping(value = "/upload", method = RequestMethod.GET) + public String upload() { + return "/upload"; + } + + @ApiOperation(value = "首页") + @AuthInterceptor(InterceptorLevel.NONE) + @RequestMapping(value = "/index", method = RequestMethod.GET) + public String index() { + return "/index"; + } + + @ApiOperation(value = "登录、注册、忘记密码页面") + @AuthInterceptor(InterceptorLevel.NONE) + @RequestMapping(value = "/signin", method = RequestMethod.GET) + public String signin() { + return "/signin"; + } + + @ApiOperation(value = "管理员页面") + @AuthInterceptor(InterceptorLevel.ADMIN) + @RequestMapping(value = "/admin", method = RequestMethod.GET) + public String admin() { + return "/admin"; + } + + @ApiIgnore + @AuthInterceptor(InterceptorLevel.NONE) + @RequestMapping(value = "/test", method = RequestMethod.GET) + @ResponseBody + public String test() { + return "test"; + } +} diff --git a/src/main/resources/application-sketch.properties b/src/main/resources/application-sketch.properties new file mode 100644 index 0000000..36f070c --- /dev/null +++ b/src/main/resources/application-sketch.properties @@ -0,0 +1,85 @@ +driverClassName=org.mariadb.jdbc.Driver +url=jdbc:mariadb://192.168.44.12:3306/web_sketch_v2 +username=root +password=galaxy2019 +initialSize=5 +maxActive=10 +maxWait=1000 + +validationQuery=SELECT 1 + +#################################################### +################### application #################### +#################################################### +query.types.domain = domain_category, domain_whois +query.types.ip = dns_server +query.output.dir = /home/bigdata/domain/output_file + +# 数据库更新定时任务时间间隔1d 1000 * 3600 * 24 = 86400000 +update.expired.day = 7 +# offline读取批处理量 +query.readin.batch = 10000 + +# 打印进度日志的查询条数 +query.log.file.line.interval = 10000 + +#################################################### +################### mariadb ######################## +#################################################### +database = web_sketch_v2 +tablename.domain.category = domain_category_reputation +tablename.domain.whois = domain_whois +tablename.dns.server = +db.query.batch.size = 10000 + +#################################################### +###################### api ######################### +#################################################### + +##### bright cloud ####### +bc.oemid = GeedgeNet +bc.deviceid = TSG-Dev +bc.uid = GN0001 +bc.url = https://api.bcti.brightcloud.com/1.0/url/getinfo +bc.method = POST + +bc.queryType = getinfo +bc.isa1cat = 1 +bc.isReputation = 1 +# 返回json格式 +bc.isxml = 0 +# bc api单次查询url长度限制 API最高限制 +bc.maximum.query.num = 100 +bc.cateinfo.filepath = categoryinfo.json +bc.usereport.filepath = bright_cloud_query_count.csv + +######### chinaz ######### +chinaz.url.single = https://apidatav2.chinaz.com/single/whois +chinaz.url.batch = https://apidatav2.chinaz.com/batch/whois +chinaz.key = ffc9be4141bd49a093ed0185a54dc6a2 +chinaz.maximum.query.num = 50 +chinaz.usereport.filepath = chinaz_query_count.csv + +#################################################### +###################### 其他 ######################### +#################################################### +tld.file = public_suffix_list_only.dat + + +sketch.datasource.druid.filters=stat +sketch.datasource.druid.driverClassName=com.mysql.jdbc.Driver +sketch.datasource.druid.url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&autoReconnect=true&useSSL=false +sketch.datasource.druid.username=root +sketch.datasource.druid.password=12311 +sketch.datasource.druid.initialSize=1 +sketch.datasource.druid.minIdle=1 +sketch.datasource.druid.maxActive=10 +sketch.datasource.druid.maxWait=60000 +sketch.datasource.druid.timeBetweenEvictionRunsMillis=60000 +sketch.datasource.druid.minEvictableIdleTimeMillis=300000 +sketch.datasource.druid.validationQuery=SELECT 'x' +sketch.datasource.druid.testWhileIdle=true +sketch.datasource.druid.testOnBorrow=false +sketch.datasource.druid.testOnReturn=false +sketch.datasource.druid.poolPreparedStatements=false +sketch.datasource.druid.maxPoolPreparedStatementPerConnectionSize=10 \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..1172ca8 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,42 @@ +server.port=8196 +mybatis.type-aliases-package=com.mesasoft.cn.entity +spring.datasource.driverClassName=com.mysql.jdbc.Driver +spring.datasource.url=jdbc:mysql://192.168.44.12:3306/efo?useUnicode=true&characterEncoding=utf-8&useSSL=true +spring.datasource.username=root +spring.datasource.password=galaxy2019 +spring.jpa.show-sql=true +spring.servlet.multipart.max-file-size=1099511627776 +spring.servlet.multipart.max-request-size=1099511627776 +spring.devtools.restart.enabled=false +spring.devtools.restart.additional-paths=src/main + +#spring.profiles.active=prod +#spring.mvc.favicon.enabled=false +swagger.enabled=true + +swagger.title=\u7EBF\u4E0A\u6587\u4EF6\u7BA1\u7406\u7CFB\u7EDF +swagger.description=\u4E0A\u4F20\u3001\u4E0B\u8F7D\u3001\u5206\u4EAB\u3001\u8FDC\u7A0B\u6587\u4EF6\u7BA1\u7406 +swagger.version=1.2 +swagger.license=MIT +swagger.licenseUrl=https://opensource.org/licenses/MIT +swagger.termsOfServiceUrl=https://github.com/code4everything/efo +swagger.contact.name=zhazhapan +swagger.contact.url=https://github.zhazhapan.com +swagger.contact.email=zhazhapan.com +swagger.base-package=com.mesasoft.cn +swagger.base-path=/** +swagger.exclude-path=/error, /ops/** + +swagger.apply-default-response-messages=false +swagger.global-response-message.get[0].code=401 +swagger.global-response-message.get[0].message=401get +swagger.global-response-message.get[1].code=500 +swagger.global-response-message.get[1].message=500get +swagger.global-response-message.get[1].modelRef=ERROR +swagger.global-response-message.post[0].code=500 +swagger.global-response-message.post[0].message=500post +swagger.global-response-message.post[0].modelRef=ERROR + + +sketch.path.admin=C:\\test +sketch.path.user=C:\\test\\1\\ \ No newline at end of file diff --git a/src/main/resources/assets/css/angular-filemanager.min.css b/src/main/resources/assets/css/angular-filemanager.min.css new file mode 100644 index 0000000..1d59c7b --- /dev/null +++ b/src/main/resources/assets/css/angular-filemanager.min.css @@ -0,0 +1,3 @@ +@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}@keyframes fadeIn{0%{opacity:0}100%{opacity:1}}@-webkit-keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes rotate{100%{transform:rotate(360deg)}}@-webkit-keyframes rotate{100%{-webkit-transform:rotate(360deg)}}@keyframes colors{0%{stroke:#4285f4}25%{stroke:#de3e35}50%{stroke:#f7c223}75%{stroke:#1b9a59}100%{stroke:#4285f4}}@keyframes dash{0%{stroke-dasharray:1,150;stroke-dashoffset:0;stroke:red}50%{stroke-dasharray:90,150;stroke-dashoffset:-35;stroke:#ff0}100%{stroke-dasharray:90,150;stroke-dashoffset:-124;stroke:green}}@-webkit-keyframes dash{0%{stroke-dasharray:1,150;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-35}100%{stroke-dasharray:90,150;stroke-dashoffset:-124}}.animated{-webkit-animation-duration:.7s;animation-duration:.7s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.animated.fast,.modal.animated{-webkit-animation-duration:.2s;animation-duration:.2s}.animated.slow{-webkit-animation-duration:1.1s;animation-duration:1.1s}.animated.fadeInDown{-webkit-animation-name:fadeInDown;animation-name:fadeInDown}.animated.fadeIn{-webkit-animation-name:fadeIn;animation-name:fadeIn}.spinner-container{-webkit-animation:rotate 2s linear infinite;animation:rotate 2s linear infinite;z-index:2;width:65px;height:65px}.spinner-container .path{stroke-dasharray:1,150;stroke-dashoffset:0;stroke:#2196f3;stroke-linecap:round;-webkit-animation:dash 1.5s ease-in-out infinite,colors 5.6s ease-in-out infinite;animation:dash 1.5s ease-in-out infinite,colors 5.6s ease-in-out infinite} +.modal{word-wrap:break-word}.modal .label.error-msg{display:block;font-size:12px;margin-top:5px;padding:0;padding:5px;margin-top:10px;text-align:left}.modal .label.error-msg>span{white-space:pre-wrap}.modal .breadcrumb{margin:0;background:#00bcd4;font-size:16px;max-height:inherit;padding:0 10px;margin-bottom:5px}.modal-fullscreen .modal-content,.modal-fullscreen .modal-dialog{bottom:0;left:0;position:absolute;right:0;top:0}.modal-fullscreen .modal-dialog{margin:0;width:100%}.modal-fullscreen .modal-content{border:none;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:inherit;-moz-box-shadow:inherit;-o-box-shadow:inherit;box-shadow:inherit}.modal-fullscreen textarea.code{min-height:450px}.modal img.preview{max-width:100%;max-height:640px;border-radius:3px}.modal img.preview.loading{width:100%;height:1px;opacity:0}.modal .modal-content{border-radius:10px 10px 4px 4px}.modal .modal-header{border-radius:4px 4px 0 0;background:#2196f3;padding:1.3em}.modal .modal-header .modal-title{font-size:20px;line-height:100%;color:#d4e5f5;margin:0}.modal .modal-header .close{opacity:1;color:#d4e5f5}.modal .modal-header .close.fullscreen{font-size:14px;position:relative;top:4px;margin-right:.8em} +body{font-size:14px;height:100vh}*,:focus{outline:0!important}.navbar{min-height:32px;margin-bottom:0;border:0;border-radius:0;color:#fff}.navbar .navbar-collapse{overflow:visible;padding:0}.navbar .navbar-toggle{padding:5px 10px}.navbar .navbar-brand{font-size:inherit;height:55px;line-height:100%}.btn.btn-default{color:#444;background-color:#fafafa}.btn{box-shadow:0 2px 5px 0 rgba(0,0,0,.26);font-weight:500;letter-spacing:.01em;border:none}textarea.code{font-family:Menlo,Monaco,Consolas,"Courier New",monospace;font-size:13px;min-height:250px;resize:vertical;color:#000}.sub-header{padding-bottom:10px;border-bottom:1px solid #eee}.sidebar{display:none;background:#fafafa;margin-top:2px;padding:0;overflow-x:hidden;overflow-y:auto;border-right:1px solid #eee}.btn-go-back{margin-top:-5px}.nav-sidebar{margin-right:-21px;margin-bottom:20px;margin-left:-20px}.nav-sidebar>li>a{color:#7a7a7a;padding:7px 0 7px 16px}.nav-sidebar>li>a:focus,.nav-sidebar>li>a:hover{background:0 0;color:#1378b9}.nav-sidebar>li.active>a{color:#2196f3}.main{padding:0}.main .page-header{margin-top:0}.file-tree ul.nav.nav-sidebar{margin:0;padding:0;padding-left:12px}.file-tree ul.nav.nav-sidebar:first-child{padding-left:0}.file-tree ul.nav.nav-sidebar.file-tree-root>li{border-left:none;padding-left:0}.table td{vertical-align:middle}#context-menu{position:absolute;display:none;z-index:9999}.iconset{padding:10px}.col-120{width:100px;max-height:100px;float:left;margin-bottom:9px;margin-right:9px}.col-120:last-child{margin-right:0}.noselect{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.iconset .thumbnail{border-radius:0;overflow:hidden;margin:0;padding:0;padding:10px 0;border:none;background:0 0}.iconset .thumbnail.selected,.table-files .selected{background:#2196f3}.iconset .thumbnail.selected,.table-files .selected td,.table-files .selected td a{color:#fff}.iconset .thumbnail .item-icon{font-size:32px}.detail-sources{text-overflow:ellipsis;overflow:hidden;word-wrap:break-word}::-webkit-scrollbar{width:10px;height:10px;background-color:#fff;box-shadow:inset 1px 1px 0 rgba(0,0,0,.1),inset -1px -1px 0 rgba(0,0,0,.07)}::-webkit-scrollbar:hover{background-color:#eee}::-webkit-scrollbar-thumb{min-height:.8em;min-width:.8em;background-color:rgba(0,0,0,.2);box-shadow:inset 1px 1px 0 rgba(0,0,0,.1),inset -1px -1px 0 rgba(0,0,0,.07)}::-webkit-scrollbar-thumb:hover{background-color:#bbb}::-webkit-scrollbar-thumb:active{background-color:#888}.dropdown-menu{font-size:14px}.dropdown-menu>li>a{padding:6px 20px}.dropdown-menu>li>a>i{margin-right:4px}.dropdown-menu.dropdown-right-click{display:block;position:static;margin-bottom:5px}.dropdown-menu.dropdown-right-click .divider{margin:3px 0}.upload-dragover .main{opacity:.4}.upload-dragover:before{content:"\e198";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);z-index:100;color:#2196f3;font-size:8em;font-family:'Glyphicons Halflings'}.upload-list{margin-top:20px}.spinner-wrapper{margin:0 auto;text-align:center;margin-top:8%}a:active,a:focus,a:hover,table th>a:active,table th>a:focus,table th>a:hover{text-decoration:none}.sortorder:after{color:#2196f3;content:'\25bc'}.sortorder.reverse:after{color:#2196f3;content:'\25b2'}.ng-cloak,.x-ng-cloak,[data-ng-cloak],[ng-cloak],[ng\:cloak],[x-ng-cloak]{display:none!important}.mr2{margin-right:2px}.mr5{margin-right:5px}.mt10{margin-top:10px}.mb0{margin-bottom:0}.pointer{cursor:pointer}.block{display:block}.ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.bold{font-weight:700}.main{overflow-y:auto}@media (min-width:768px){.main{padding-right:0;padding-left:0}.main,.row,.sidebar,angular-filemanager>div{height:100%}.container-fluid{height:-webkit-calc(100% - 58px);height:-moz-calc(100% - 58px);height:calc(100% - 58px)}.sidebar{display:block}}.selected-file-details{padding-left:20px}.item-extension::after{font-family:Roboto,"Helvetica Neue",Helvetica,Arial,sans-serif;content:attr(data-ext);left:4px;position:absolute;color:#fff;font-size:9px;text-transform:uppercase;top:21px}.selected .item-extension::after{color:#2196f3}.form-control.search-input{max-width:20em;display:inline}.like-code{display:inline}.point{margin-right:8px;font-size:10px}.navbar .btn.btn-flat{padding:2px;width:32px;height:30px;margin-left:5px}.navbar-inverse .navbar-toggle .icon-bar{background:#fff}.navbar-inverse .navbar-form input[type=text]{color:#7a7a7a;box-shadow:none;margin:0 10px}.navbar .navbar-form{border-bottom:none;border-top:none;box-shadow:none;padding:0;margin:12px 0}.breadcrumb{background:0 0;padding:0;font-size:17px;margin:12px 0;overflow:hidden;max-height:30px}.breadcrumb a,.breadcrumb>.active{color:#fff}.breadcrumb>li+li:before{font-family:'Glyphicons Halflings';content:"\e080";font-size:12px;color:#fff}.scrollable-menu{height:auto;max-height:200px;overflow-x:hidden}.btn.btn-flat{background:0 0;color:#fff}.btn-group.open>.btn-flat,.btn.btn-flat,.btn.btn-flat:active{box-shadow:none}.btn.btn-flat>i{font-size:18px;width:18px;height:18px;line-height:100%} \ No newline at end of file diff --git a/src/main/resources/assets/css/bootstrap.min.css b/src/main/resources/assets/css/bootstrap.min.css new file mode 100644 index 0000000..cac117e --- /dev/null +++ b/src/main/resources/assets/css/bootstrap.min.css @@ -0,0 +1,6 @@ +/*! + * Bootstrap v4.0.0-beta.3 (https://getbootstrap.com) + * Copyright 2011-2017 The Bootstrap Authors + * Copyright 2011-2017 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#868e96;--gray-dark:#343a40;--primary:#007bff;--secondary:#868e96;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}@-ms-viewport{width:device-width}article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent;-webkit-text-decoration-skip:objects}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg:not(:root){overflow:hidden}[role=button],a,area,button,input:not([type=range]),label,select,summary,textarea{-ms-touch-action:manipulation;touch-action:manipulation}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#868e96;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-family:inherit;font-weight:500;line-height:1.2;color:inherit}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#868e96}.blockquote-footer::before{content:"\2014 \00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#868e96}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}code{font-size:87.5%;color:#e83e8c;word-break:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-1{-webkit-box-flex:0;-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-webkit-box-flex:0;-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-webkit-box-flex:0;-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-webkit-box-flex:0;-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-webkit-box-flex:0;-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-webkit-box-flex:0;-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-webkit-box-flex:0;-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-webkit-box-flex:0;-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-sm-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-sm-1{-webkit-box-flex:0;-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-webkit-box-flex:0;-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-webkit-box-flex:0;-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-webkit-box-flex:0;-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-webkit-box-flex:0;-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-webkit-box-flex:0;-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-webkit-box-flex:0;-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-webkit-box-flex:0;-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-sm-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-sm-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-sm-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-sm-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-sm-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-sm-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-sm-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-sm-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-sm-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-sm-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-sm-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-sm-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-md-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-md-1{-webkit-box-flex:0;-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-webkit-box-flex:0;-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-webkit-box-flex:0;-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-webkit-box-flex:0;-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-webkit-box-flex:0;-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-webkit-box-flex:0;-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-webkit-box-flex:0;-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-webkit-box-flex:0;-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-md-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-md-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-md-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-md-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-md-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-md-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-md-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-md-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-md-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-md-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-md-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-md-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-lg-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-lg-1{-webkit-box-flex:0;-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-webkit-box-flex:0;-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-webkit-box-flex:0;-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-webkit-box-flex:0;-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-webkit-box-flex:0;-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-webkit-box-flex:0;-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-webkit-box-flex:0;-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-webkit-box-flex:0;-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-lg-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-lg-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-lg-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-lg-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-lg-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-lg-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-lg-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-lg-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-lg-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-lg-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-lg-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-lg-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-xl-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-xl-1{-webkit-box-flex:0;-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-webkit-box-flex:0;-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-webkit-box-flex:0;-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-webkit-box-flex:0;-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-webkit-box-flex:0;-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-webkit-box-flex:0;-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-webkit-box-flex:0;-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-webkit-box-flex:0;-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-xl-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-xl-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-xl-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-xl-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-xl-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-xl-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-xl-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-xl-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-xl-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-xl-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-xl-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-xl-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;max-width:100%;margin-bottom:1rem;background-color:transparent}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table .table{background-color:#fff}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#dddfe2}.table-hover .table-secondary:hover{background-color:#cfd2d6}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#cfd2d6}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#212529;border-color:#32383e}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#212529}.table-dark td,.table-dark th,.table-dark thead th{border-color:#32383e}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{background-color:rgba(255,255,255,.075)}@media (max-width:575.99px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.99px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.99px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.99px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#868e96;opacity:1}.form-control::-moz-placeholder{color:#868e96;opacity:1}.form-control:-ms-input-placeholder{color:#868e96;opacity:1}.form-control::-ms-input-placeholder{color:#868e96;opacity:1}.form-control::placeholder{color:#868e96;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}select.form-control:not([size]):not([multiple]){height:calc(2.25rem + 2px)}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding-top:.375rem;padding-bottom:.375rem;margin-bottom:0;line-height:1.5;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm,.input-group-lg>.form-control-plaintext.form-control,.input-group-lg>.input-group-append>.form-control-plaintext.btn,.input-group-lg>.input-group-append>.form-control-plaintext.input-group-text,.input-group-lg>.input-group-prepend>.form-control-plaintext.btn,.input-group-lg>.input-group-prepend>.form-control-plaintext.input-group-text,.input-group-sm>.form-control-plaintext.form-control,.input-group-sm>.input-group-append>.form-control-plaintext.btn,.input-group-sm>.input-group-append>.form-control-plaintext.input-group-text,.input-group-sm>.input-group-prepend>.form-control-plaintext.btn,.input-group-sm>.input-group-prepend>.form-control-plaintext.input-group-text{padding-right:0;padding-left:0}.form-control-sm,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-sm>.input-group-append>select.btn:not([size]):not([multiple]),.input-group-sm>.input-group-append>select.input-group-text:not([size]):not([multiple]),.input-group-sm>.input-group-prepend>select.btn:not([size]):not([multiple]),.input-group-sm>.input-group-prepend>select.input-group-text:not([size]):not([multiple]),.input-group-sm>select.form-control:not([size]):not([multiple]),select.form-control-sm:not([size]):not([multiple]){height:calc(1.8125rem + 2px)}.form-control-lg,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-lg>.input-group-append>select.btn:not([size]):not([multiple]),.input-group-lg>.input-group-append>select.input-group-text:not([size]):not([multiple]),.input-group-lg>.input-group-prepend>select.btn:not([size]):not([multiple]),.input-group-lg>.input-group-prepend>select.input-group-text:not([size]):not([multiple]),.input-group-lg>select.form-control:not([size]):not([multiple]),select.form-control-lg:not([size]):not([multiple]){height:calc(2.875rem + 2px)}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label{color:#868e96}.form-check-label{margin-bottom:0}.form-check-inline{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;width:250px;padding:.5rem;margin-top:.1rem;font-size:.875rem;line-height:1;color:#fff;background-color:rgba(40,167,69,.8);border-radius:.2rem}.custom-select.is-valid,.form-control.is-valid,.was-validated .custom-select:valid,.was-validated .form-control:valid{border-color:#28a745}.custom-select.is-valid:focus,.form-control.is-valid:focus,.was-validated .custom-select:valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-select.is-valid~.valid-feedback,.custom-select.is-valid~.valid-tooltip,.form-control.is-valid~.valid-feedback,.form-control.is-valid~.valid-tooltip,.was-validated .custom-select:valid~.valid-feedback,.was-validated .custom-select:valid~.valid-tooltip,.was-validated .form-control:valid~.valid-feedback,.was-validated .form-control:valid~.valid-tooltip{display:block}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{background-color:#71dd8a}.custom-control-input.is-valid~.valid-feedback,.custom-control-input.is-valid~.valid-tooltip,.was-validated .custom-control-input:valid~.valid-feedback,.was-validated .custom-control-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(40,167,69,.25)}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label::before,.was-validated .custom-file-input:valid~.custom-file-label::before{border-color:inherit}.custom-file-input.is-valid~.valid-feedback,.custom-file-input.is-valid~.valid-tooltip,.was-validated .custom-file-input:valid~.valid-feedback,.was-validated .custom-file-input:valid~.valid-tooltip{display:block}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;width:250px;padding:.5rem;margin-top:.1rem;font-size:.875rem;line-height:1;color:#fff;background-color:rgba(220,53,69,.8);border-radius:.2rem}.custom-select.is-invalid,.form-control.is-invalid,.was-validated .custom-select:invalid,.was-validated .form-control:invalid{border-color:#dc3545}.custom-select.is-invalid:focus,.form-control.is-invalid:focus,.was-validated .custom-select:invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-select.is-invalid~.invalid-feedback,.custom-select.is-invalid~.invalid-tooltip,.form-control.is-invalid~.invalid-feedback,.form-control.is-invalid~.invalid-tooltip,.was-validated .custom-select:invalid~.invalid-feedback,.was-validated .custom-select:invalid~.invalid-tooltip,.was-validated .form-control:invalid~.invalid-feedback,.was-validated .form-control:invalid~.invalid-tooltip{display:block}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{background-color:#efa2a9}.custom-control-input.is-invalid~.invalid-feedback,.custom-control-input.is-invalid~.invalid-tooltip,.was-validated .custom-control-input:invalid~.invalid-feedback,.was-validated .custom-control-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(220,53,69,.25)}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label::before,.was-validated .custom-file-input:invalid~.custom-file-label::before{border-color:inherit}.custom-file-input.is-invalid~.invalid-feedback,.custom-file-input.is-invalid~.invalid-tooltip,.was-validated .custom-file-input:invalid~.invalid-feedback,.was-validated .custom-file-input:invalid~.invalid-tooltip{display:block}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .input-group{width:auto}.form-inline .form-check{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;text-align:center;white-space:nowrap;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.btn:focus,.btn:hover{text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not([disabled]):not(.disabled){cursor:pointer}.btn:not([disabled]):not(.disabled).active,.btn:not([disabled]):not(.disabled):active{background-image:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-primary.disabled,.btn-primary:disabled{background-color:#007bff;border-color:#007bff}.btn-primary:not([disabled]):not(.disabled).active,.btn-primary:not([disabled]):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not([disabled]):not(.disabled).active:focus,.btn-primary:not([disabled]):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-secondary{color:#fff;background-color:#868e96;border-color:#868e96}.btn-secondary:hover{color:#fff;background-color:#727b84;border-color:#6c757d}.btn-secondary.focus,.btn-secondary:focus{box-shadow:0 0 0 .2rem rgba(134,142,150,.5)}.btn-secondary.disabled,.btn-secondary:disabled{background-color:#868e96;border-color:#868e96}.btn-secondary:not([disabled]):not(.disabled).active,.btn-secondary:not([disabled]):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#666e76}.btn-secondary:not([disabled]):not(.disabled).active:focus,.btn-secondary:not([disabled]):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(134,142,150,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-success.disabled,.btn-success:disabled{background-color:#28a745;border-color:#28a745}.btn-success:not([disabled]):not(.disabled).active,.btn-success:not([disabled]):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not([disabled]):not(.disabled).active:focus,.btn-success:not([disabled]):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-info.disabled,.btn-info:disabled{background-color:#17a2b8;border-color:#17a2b8}.btn-info:not([disabled]):not(.disabled).active,.btn-info:not([disabled]):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not([disabled]):not(.disabled).active:focus,.btn-info:not([disabled]):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-warning.disabled,.btn-warning:disabled{background-color:#ffc107;border-color:#ffc107}.btn-warning:not([disabled]):not(.disabled).active,.btn-warning:not([disabled]):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not([disabled]):not(.disabled).active:focus,.btn-warning:not([disabled]):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-danger.disabled,.btn-danger:disabled{background-color:#dc3545;border-color:#dc3545}.btn-danger:not([disabled]):not(.disabled).active,.btn-danger:not([disabled]):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not([disabled]):not(.disabled).active:focus,.btn-danger:not([disabled]):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-light.disabled,.btn-light:disabled{background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not([disabled]):not(.disabled).active,.btn-light:not([disabled]):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not([disabled]):not(.disabled).active:focus,.btn-light:not([disabled]):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-dark.disabled,.btn-dark:disabled{background-color:#343a40;border-color:#343a40}.btn-dark:not([disabled]):not(.disabled).active,.btn-dark:not([disabled]):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not([disabled]):not(.disabled).active:focus,.btn-dark:not([disabled]):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-primary{color:#007bff;background-color:transparent;background-image:none;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not([disabled]):not(.disabled).active,.btn-outline-primary:not([disabled]):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#212529;background-color:#007bff;border-color:#007bff;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#868e96;background-color:transparent;background-image:none;border-color:#868e96}.btn-outline-secondary:hover{color:#fff;background-color:#868e96;border-color:#868e96}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(134,142,150,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#868e96;background-color:transparent}.btn-outline-secondary:not([disabled]):not(.disabled).active,.btn-outline-secondary:not([disabled]):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#212529;background-color:#868e96;border-color:#868e96;box-shadow:0 0 0 .2rem rgba(134,142,150,.5)}.btn-outline-success{color:#28a745;background-color:transparent;background-image:none;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not([disabled]):not(.disabled).active,.btn-outline-success:not([disabled]):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#212529;background-color:#28a745;border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;background-color:transparent;background-image:none;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not([disabled]):not(.disabled).active,.btn-outline-info:not([disabled]):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#212529;background-color:#17a2b8;border-color:#17a2b8;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;background-color:transparent;background-image:none;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not([disabled]):not(.disabled).active,.btn-outline-warning:not([disabled]):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;background-color:transparent;background-image:none;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not([disabled]):not(.disabled).active,.btn-outline-danger:not([disabled]):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#212529;background-color:#dc3545;border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;background-color:transparent;background-image:none;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not([disabled]):not(.disabled).active,.btn-outline-light:not([disabled]):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#fff;background-color:#f8f9fa;border-color:#f8f9fa;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;background-color:transparent;background-image:none;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not([disabled]):not(.disabled).active,.btn-outline-dark:not([disabled]):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#212529;background-color:#343a40;border-color:#343a40;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;background-color:transparent}.btn-link:hover{color:#0056b3;text-decoration:underline;background-color:transparent;border-color:transparent}.btn-link.focus,.btn-link:focus{text-decoration:underline;border-color:transparent;box-shadow:none}.btn-link.disabled,.btn-link:disabled{color:#868e96}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;transition:opacity .15s linear}.fade.show{opacity:1}.collapse{display:none}.collapse.show{display:block}tr.collapse.show{display:table-row}tbody.collapse.show{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}.dropdown,.dropup{position:relative}.dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropup .dropdown-menu{margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;width:0;height:0;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#868e96;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#868e96;white-space:nowrap}.btn-group,.btn-group-vertical{position:relative;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-webkit-box-flex:0;-ms-flex:0 1 auto;flex:0 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group,.btn-group-vertical .btn+.btn,.btn-group-vertical .btn+.btn-group,.btn-group-vertical .btn-group+.btn,.btn-group-vertical .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after{margin-left:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.btn-group-vertical .btn,.btn-group-vertical .btn-group{width:100%}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group .custom-file,.input-group .custom-select,.input-group .form-control{position:relative;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;margin-bottom:0}.input-group .custom-file:focus,.input-group .custom-select:focus,.input-group .form-control:focus{z-index:3}.input-group .custom-file+.form-control,.input-group .custom-select+.form-control,.input-group .form-control+.form-control{margin-left:-1px}.input-group .custom-select:not(:last-child),.input-group .form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group .custom-select:not(:first-child),.input-group .form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group .custom-file{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.input-group .custom-file:not(:last-child) .custom-file-control,.input-group .custom-file:not(:last-child) .custom-file-control::before{border-top-right-radius:0;border-bottom-right-radius:0}.input-group .custom-file:not(:first-child) .custom-file-control,.input-group .custom-file:not(:first-child) .custom-file-control::before{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:active~.custom-control-label::before{color:#fff;background-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label{color:#868e96}.custom-control-input:disabled~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{margin-bottom:0}.custom-control-label::before{position:absolute;top:.25rem;left:0;display:block;width:1rem;height:1rem;pointer-events:none;content:"";-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:#dee2e6}.custom-control-label::after{position:absolute;top:.25rem;left:0;display:block;width:1rem;height:1rem;content:"";background-repeat:no-repeat;background-position:center center;background-size:50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::before{background-color:#007bff}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E")}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::before{background-color:#007bff}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E")}.custom-select{display:inline-block;width:100%;height:calc(2.25rem + 2px);padding:.375rem 1.75rem .375rem .75rem;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right .75rem center;background-size:8px 10px;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:inset 0 1px 2px rgba(0,0,0,.075),0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#868e96;background-color:#e9ecef}.custom-select::-ms-expand{opacity:0}.custom-select-sm{height:calc(1.8125rem + 2px);padding-top:.375rem;padding-bottom:.375rem;font-size:75%}.custom-select-lg{height:calc(2.875rem + 2px);padding-top:.375rem;padding-bottom:.375rem;font-size:125%}.custom-file{position:relative;display:inline-block;width:100%;height:calc(2.25rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(2.25rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-control{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:focus~.custom-file-control::before{border-color:#80bdff}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(2.25rem + 2px);padding:.375rem .75rem;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(calc(2.25rem + 2px) - 1px * 2);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:1px solid #ced4da;border-radius:0 .25rem .25rem 0}.nav{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#868e96}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#868e96;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar>.container,.navbar>.container-fluid{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler:not([disabled]):not(.disabled){cursor:pointer}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.99px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .dropdown-menu-right{right:0;left:auto}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .dropup .dropdown-menu{top:auto;bottom:100%}}@media (max-width:767.99px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .dropdown-menu-right{right:0;left:auto}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .dropup .dropdown-menu{top:auto;bottom:100%}}@media (max-width:991.99px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .dropdown-menu-right{right:0;left:auto}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .dropup .dropdown-menu{top:auto;bottom:100%}}@media (max-width:1199.99px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .dropdown-menu-right{right:0;left:auto}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .dropup .dropdown-menu{top:auto;bottom:100%}}.navbar-expand{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .dropdown-menu-right{right:0;left:auto}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-expand .dropup .dropdown-menu{top:auto;bottom:100%}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-body{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img{width:100%;border-radius:calc(.25rem - 1px)}.card-img-top{width:100%;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img-bottom{width:100%;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-ms-flex:1 0 0%;flex:1 0 0%;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-webkit-box-flex:1;-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:first-child .card-header,.card-group>.card:first-child .card-img-top{border-top-right-radius:0}.card-group>.card:first-child .card-footer,.card-group>.card:first-child .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:last-child .card-header,.card-group>.card:last-child .card-img-top{border-top-left-radius:0}.card-group>.card:last-child .card-footer,.card-group>.card:last-child .card-img-bottom{border-bottom-left-radius:0}.card-group>.card:only-child{border-radius:.25rem}.card-group>.card:only-child .card-header,.card-group>.card:only-child .card-img-top{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card-group>.card:only-child .card-footer,.card-group>.card:only-child .card-img-bottom{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-group>.card:not(:first-child):not(:last-child):not(:only-child){border-radius:0}.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-footer,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-header,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-top{border-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem}.card-columns .card{display:inline-block;width:100%}}.breadcrumb{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;padding-left:.5rem;color:#868e96;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#868e96}.pagination{display:-webkit-box;display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:focus,.page-link:hover{color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:not([disabled]):not(.disabled){cursor:pointer}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:1;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#868e96;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}.badge-primary[href]:focus,.badge-primary[href]:hover{color:#fff;text-decoration:none;background-color:#0062cc}.badge-secondary{color:#fff;background-color:#868e96}.badge-secondary[href]:focus,.badge-secondary[href]:hover{color:#fff;text-decoration:none;background-color:#6c757d}.badge-success{color:#fff;background-color:#28a745}.badge-success[href]:focus,.badge-success[href]:hover{color:#fff;text-decoration:none;background-color:#1e7e34}.badge-info{color:#fff;background-color:#17a2b8}.badge-info[href]:focus,.badge-info[href]:hover{color:#fff;text-decoration:none;background-color:#117a8b}.badge-warning{color:#212529;background-color:#ffc107}.badge-warning[href]:focus,.badge-warning[href]:hover{color:#212529;text-decoration:none;background-color:#d39e00}.badge-danger{color:#fff;background-color:#dc3545}.badge-danger[href]:focus,.badge-danger[href]:hover{color:#fff;text-decoration:none;background-color:#bd2130}.badge-light{color:#212529;background-color:#f8f9fa}.badge-light[href]:focus,.badge-light[href]:hover{color:#212529;text-decoration:none;background-color:#dae0e5}.badge-dark{color:#fff;background-color:#343a40}.badge-dark[href]:focus,.badge-dark[href]:hover{color:#fff;text-decoration:none;background-color:#1d2124}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#464a4e;background-color:#e7e8ea;border-color:#dddfe2}.alert-secondary hr{border-top-color:#cfd2d6}.alert-secondary .alert-link{color:#2e3133}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-webkit-box;display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;color:#fff;text-align:center;background-color:#007bff;transition:width .6s ease}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}.media{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.media-body{-webkit-box-flex:1;-ms-flex:1;flex:1}.list-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;margin-bottom:-1px;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item:focus,.list-group-item:hover{z-index:1;text-decoration:none}.list-group-item.disabled,.list-group-item:disabled{color:#868e96;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-flush .list-group-item{border-right:0;border-left:0;border-radius:0}.list-group-flush:first-child .list-group-item:first-child{border-top:0}.list-group-flush:last-child .list-group-item:last-child{border-bottom:0}.list-group-item-primary{color:#004085;background-color:#b8daff}a.list-group-item-primary,button.list-group-item-primary{color:#004085}a.list-group-item-primary:focus,a.list-group-item-primary:hover,button.list-group-item-primary:focus,button.list-group-item-primary:hover{color:#004085;background-color:#9fcdff}a.list-group-item-primary.active,button.list-group-item-primary.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#464a4e;background-color:#dddfe2}a.list-group-item-secondary,button.list-group-item-secondary{color:#464a4e}a.list-group-item-secondary:focus,a.list-group-item-secondary:hover,button.list-group-item-secondary:focus,button.list-group-item-secondary:hover{color:#464a4e;background-color:#cfd2d6}a.list-group-item-secondary.active,button.list-group-item-secondary.active{color:#fff;background-color:#464a4e;border-color:#464a4e}.list-group-item-success{color:#155724;background-color:#c3e6cb}a.list-group-item-success,button.list-group-item-success{color:#155724}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#155724;background-color:#b1dfbb}a.list-group-item-success.active,button.list-group-item-success.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}a.list-group-item-info,button.list-group-item-info{color:#0c5460}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#0c5460;background-color:#abdde5}a.list-group-item-info.active,button.list-group-item-info.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}a.list-group-item-warning,button.list-group-item-warning{color:#856404}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#856404;background-color:#ffe8a1}a.list-group-item-warning.active,button.list-group-item-warning.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}a.list-group-item-danger,button.list-group-item-danger{color:#721c24}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#721c24;background-color:#f1b0b7}a.list-group-item-danger.active,button.list-group-item-danger.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}a.list-group-item-light,button.list-group-item-light{color:#818182}a.list-group-item-light:focus,a.list-group-item-light:hover,button.list-group-item-light:focus,button.list-group-item-light:hover{color:#818182;background-color:#ececf6}a.list-group-item-light.active,button.list-group-item-light.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}a.list-group-item-dark,button.list-group-item-dark{color:#1b1e21}a.list-group-item-dark:focus,a.list-group-item-dark:hover,button.list-group-item-dark:focus,button.list-group-item-dark:hover{color:#1b1e21;background-color:#b9bbbe}a.list-group-item-dark.active,button.list-group-item-dark.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:focus,.close:hover{color:#000;text-decoration:none;opacity:.75}.close:not([disabled]):not(.disabled){cursor:pointer}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;outline:0}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.show .modal-dialog{-webkit-transform:translate(0,0);transform:translate(0,0)}.modal-dialog-centered{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;min-height:calc(100% - (.5rem * 2))}.modal-content{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:1rem;border-bottom:1px solid #e9ecef;border-top-left-radius:.3rem;border-top-right-radius:.3rem}.modal-header .close{padding:1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;padding:1rem;border-top:1px solid #e9ecef}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-centered{min-height:calc(100% - (1.75rem * 2))}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg{max-width:800px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top] .arrow,.bs-popover-top .arrow{bottom:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=top] .arrow::after,.bs-popover-auto[x-placement^=top] .arrow::before,.bs-popover-top .arrow::after,.bs-popover-top .arrow::before{border-width:.5rem .5rem 0}.bs-popover-auto[x-placement^=top] .arrow::before,.bs-popover-top .arrow::before{bottom:0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top] .arrow::after,.bs-popover-top .arrow::after{bottom:1px;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right] .arrow,.bs-popover-right .arrow{left:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right] .arrow::after,.bs-popover-auto[x-placement^=right] .arrow::before,.bs-popover-right .arrow::after,.bs-popover-right .arrow::before{border-width:.5rem .5rem .5rem 0}.bs-popover-auto[x-placement^=right] .arrow::before,.bs-popover-right .arrow::before{left:0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right] .arrow::after,.bs-popover-right .arrow::after{left:1px;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom] .arrow,.bs-popover-bottom .arrow{top:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=bottom] .arrow::after,.bs-popover-auto[x-placement^=bottom] .arrow::before,.bs-popover-bottom .arrow::after,.bs-popover-bottom .arrow::before{border-width:0 .5rem .5rem .5rem}.bs-popover-auto[x-placement^=bottom] .arrow::before,.bs-popover-bottom .arrow::before{top:0;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom] .arrow::after,.bs-popover-bottom .arrow::after{top:1px;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left] .arrow,.bs-popover-left .arrow{right:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left] .arrow::after,.bs-popover-auto[x-placement^=left] .arrow::before,.bs-popover-left .arrow::after,.bs-popover-left .arrow::before{border-width:.5rem 0 .5rem .5rem}.bs-popover-auto[x-placement^=left] .arrow::before,.bs-popover-left .arrow::before{right:0;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left] .arrow::after,.bs-popover-left .arrow::after{right:1px;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;color:inherit;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-item{position:relative;display:none;-webkit-box-align:center;-ms-flex-align:center;align-items:center;width:100%;transition:-webkit-transform .6s ease;transition:transform .6s ease;transition:transform .6s ease,-webkit-transform .6s ease;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.carousel-item-next,.carousel-item-prev{position:absolute;top:0}.carousel-item-next.carousel-item-left,.carousel-item-prev.carousel-item-right{-webkit-transform:translateX(0);transform:translateX(0)}@supports ((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d)){.carousel-item-next.carousel-item-left,.carousel-item-prev.carousel-item-right{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.active.carousel-item-right,.carousel-item-next{-webkit-transform:translateX(100%);transform:translateX(100%)}@supports ((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d)){.active.carousel-item-right,.carousel-item-next{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}.active.carousel-item-left,.carousel-item-prev{-webkit-transform:translateX(-100%);transform:translateX(-100%)}@supports ((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d)){.active.carousel-item-left,.carousel-item-prev{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:transparent no-repeat center center;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E")}.carousel-control-next-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E")}.carousel-indicators{position:absolute;right:0;bottom:10px;left:0;z-index:15;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{position:relative;-webkit-box-flex:0;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;background-color:rgba(255,255,255,.5)}.carousel-indicators li::before{position:absolute;top:-10px;left:0;display:inline-block;width:100%;height:10px;content:""}.carousel-indicators li::after{position:absolute;bottom:-10px;left:0;display:inline-block;width:100%;height:10px;content:""}.carousel-indicators .active{background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#868e96!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#6c757d!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #e9ecef!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#868e96!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-circle{border-radius:50%!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}.d-print-block{display:none!important}@media print{.d-print-block{display:block!important}}.d-print-inline{display:none!important}@media print{.d-print-inline{display:inline!important}}.d-print-inline-block{display:none!important}@media print{.d-print-inline-block{display:inline-block!important}}@media print{.d-print-none{display:none!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-webkit-box-orient:horizontal!important;-webkit-box-direction:normal!important;-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-webkit-box-orient:vertical!important;-webkit-box-direction:normal!important;-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-webkit-box-orient:horizontal!important;-webkit-box-direction:reverse!important;-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-webkit-box-orient:vertical!important;-webkit-box-direction:reverse!important;-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.justify-content-start{-webkit-box-pack:start!important;-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-webkit-box-pack:end!important;-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-webkit-box-pack:center!important;-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-webkit-box-pack:justify!important;-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-webkit-box-align:start!important;-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-webkit-box-align:end!important;-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-webkit-box-align:center!important;-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-webkit-box-align:baseline!important;-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-webkit-box-align:stretch!important;-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-webkit-box-orient:horizontal!important;-webkit-box-direction:normal!important;-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-webkit-box-orient:vertical!important;-webkit-box-direction:normal!important;-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-webkit-box-orient:horizontal!important;-webkit-box-direction:reverse!important;-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-webkit-box-orient:vertical!important;-webkit-box-direction:reverse!important;-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.justify-content-sm-start{-webkit-box-pack:start!important;-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-webkit-box-pack:end!important;-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-webkit-box-pack:center!important;-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-webkit-box-pack:justify!important;-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-webkit-box-align:start!important;-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-webkit-box-align:end!important;-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-webkit-box-align:center!important;-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-webkit-box-align:baseline!important;-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-webkit-box-align:stretch!important;-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-webkit-box-orient:horizontal!important;-webkit-box-direction:normal!important;-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-webkit-box-orient:vertical!important;-webkit-box-direction:normal!important;-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-webkit-box-orient:horizontal!important;-webkit-box-direction:reverse!important;-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-webkit-box-orient:vertical!important;-webkit-box-direction:reverse!important;-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.justify-content-md-start{-webkit-box-pack:start!important;-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-webkit-box-pack:end!important;-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-webkit-box-pack:center!important;-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-webkit-box-pack:justify!important;-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-webkit-box-align:start!important;-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-webkit-box-align:end!important;-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-webkit-box-align:center!important;-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-webkit-box-align:baseline!important;-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-webkit-box-align:stretch!important;-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-webkit-box-orient:horizontal!important;-webkit-box-direction:normal!important;-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-webkit-box-orient:vertical!important;-webkit-box-direction:normal!important;-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-webkit-box-orient:horizontal!important;-webkit-box-direction:reverse!important;-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-webkit-box-orient:vertical!important;-webkit-box-direction:reverse!important;-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.justify-content-lg-start{-webkit-box-pack:start!important;-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-webkit-box-pack:end!important;-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-webkit-box-pack:center!important;-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-webkit-box-pack:justify!important;-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-webkit-box-align:start!important;-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-webkit-box-align:end!important;-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-webkit-box-align:center!important;-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-webkit-box-align:baseline!important;-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-webkit-box-align:stretch!important;-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-webkit-box-orient:horizontal!important;-webkit-box-direction:normal!important;-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-webkit-box-orient:vertical!important;-webkit-box-direction:normal!important;-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-webkit-box-orient:horizontal!important;-webkit-box-direction:reverse!important;-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-webkit-box-orient:vertical!important;-webkit-box-direction:reverse!important;-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.justify-content-xl-start{-webkit-box-pack:start!important;-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-webkit-box-pack:end!important;-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-webkit-box-pack:center!important;-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-webkit-box-pack:justify!important;-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-webkit-box-align:start!important;-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-webkit-box-align:end!important;-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-webkit-box-align:center!important;-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-webkit-box-align:baseline!important;-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-webkit-box-align:stretch!important;-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;-webkit-clip-path:inset(50%);clip-path:inset(50%);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal;-webkit-clip-path:none;clip-path:none}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-justify{text-align:justify!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0062cc!important}.text-secondary{color:#868e96!important}a.text-secondary:focus,a.text-secondary:hover{color:#6c757d!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#1e7e34!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#117a8b!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#d39e00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#bd2130!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#dae0e5!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#1d2124!important}.text-muted{color:#868e96!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}} \ No newline at end of file diff --git a/src/main/resources/assets/css/fileinput.min.css b/src/main/resources/assets/css/fileinput.min.css new file mode 100644 index 0000000..165e01a --- /dev/null +++ b/src/main/resources/assets/css/fileinput.min.css @@ -0,0 +1,542 @@ +/*! + * bootstrap-fileinput v4.4.7 + * http://plugins.krajee.com/file-input + * + * Krajee default styling for bootstrap-fileinput. + * + * Author: Kartik Visweswaran + * Copyright: 2014 - 2018, Kartik Visweswaran, Krajee.com + * + * Licensed under the BSD 3-Clause + * https://github.com/kartik-v/bootstrap-fileinput/blob/master/LICENSE.md + */ +.file-loading input[type=file], input[type=file].file-loading { + width: 0; + height: 0 +} + +.file-caption-icon, .file-input-ajax-new .fileinput-remove-button, .file-input-ajax-new .fileinput-upload-button, .file-input-ajax-new .no-browse .input-group-btn, .file-input-new .close, .file-input-new .file-preview, .file-input-new .fileinput-remove-button, .file-input-new .fileinput-upload-button, .file-input-new .glyphicon-file, .file-input-new .no-browse .input-group-btn, .file-zoom-dialog .modal-header:after, .file-zoom-dialog .modal-header:before, .hide-content .kv-file-content, .kv-hidden { + display: none +} + +.btn-file input[type=file], .file-caption-icon, .file-preview .fileinput-remove, .file-zoom-dialog .btn-navigate, .file-zoom-dialog .floating-buttons, .krajee-default .file-thumb-progress { + position: absolute +} + +.btn-file, .file-caption, .file-loading:before, .file-preview, .file-zoom-dialog .modal-dialog, .krajee-default .file-thumbnail-footer, .krajee-default.file-preview-frame { + position: relative +} + +.file-error-message pre, .file-error-message ul, .krajee-default .file-actions, .krajee-default .file-other-error { + text-align: left +} + +.file-error-message pre, .file-error-message ul { + margin: 0 +} + +.krajee-default .file-drag-handle, .krajee-default .file-upload-indicator { + float: left; + margin: 5px 0 -5px; + width: 16px; + height: 16px +} + +.krajee-default .file-thumb-progress .progress, .krajee-default .file-thumb-progress .progress-bar { + height: 11px; + font-size: 9px; + line-height: 10px +} + +.krajee-default .file-caption-info, .krajee-default .file-size-info { + display: block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + width: 160px; + height: 15px; + margin: auto +} + +.file-zoom-content > .file-object.type-flash, .file-zoom-content > .file-object.type-image, .file-zoom-content > .file-object.type-video { + max-width: 100%; + max-height: 100%; + width: auto +} + +.file-zoom-content > .file-object.type-flash, .file-zoom-content > .file-object.type-video { + height: 100% +} + +.file-zoom-content > .file-object.type-default, .file-zoom-content > .file-object.type-html, .file-zoom-content > .file-object.type-pdf, .file-zoom-content > .file-object.type-text { + width: 100% +} + +.rotate-2 { + transform: rotateY(180deg) +} + +.rotate-3 { + transform: rotate(180deg) +} + +.rotate-4 { + transform: rotate(180deg) rotateY(180deg) +} + +.rotate-5 { + transform: rotate(270deg) rotateY(180deg) +} + +.rotate-6 { + transform: rotate(90deg) +} + +.rotate-7 { + transform: rotate(90deg) rotateY(180deg) +} + +.rotate-8 { + transform: rotate(270deg) +} + +.file-loading:before { + content: " Loading..."; + display: inline-block; + padding-left: 20px; + line-height: 16px; + font-size: 13px; + font-variant: small-caps; + color: #999; + background: url(../img/loading.gif) top left no-repeat +} + +.file-object { + margin: 0 0 -5px; + padding: 0 +} + +.btn-file { + overflow: hidden +} + +.btn-file input[type=file] { + top: 0; + right: 0; + min-width: 100%; + min-height: 100%; + text-align: right; + opacity: 0; + background: none; + cursor: inherit; + display: block +} + +.btn-file ::-ms-browse { + font-size: 10000px; + width: 100%; + height: 100% +} + +.file-caption .file-caption-name { + width: 100%; + margin: 0; + padding: 0; + box-shadow: none; + border: none; + background: 0 0; + outline: 0 +} + +.file-caption.icon-visible .file-caption-icon { + display: inline-block +} + +.file-caption.icon-visible .file-caption-name { + padding-left: 15px +} + +.file-caption-icon { + line-height: 1; + left: 8px +} + +.file-error-message { + color: #a94442; + background-color: #f2dede; + margin: 5px; + border: 1px solid #ebccd1; + border-radius: 4px; + padding: 15px +} + +.file-error-message pre { + margin: 5px 0 +} + +.file-caption-disabled { + background-color: #eee; + cursor: not-allowed; + opacity: 1 +} + +.file-preview { + border-radius: 5px; + border: 1px solid #ddd; + padding: 8px; + width: 100%; + margin-bottom: 5px +} + +.file-preview .btn-xs { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px +} + +.file-preview .fileinput-remove { + top: 1px; + right: 1px; + line-height: 10px +} + +.file-preview .clickable { + cursor: pointer +} + +.file-preview-image { + font: 40px Impact, Charcoal, sans-serif; + color: green +} + +.krajee-default.file-preview-frame { + margin: 8px; + border: 1px solid #ddd; + box-shadow: 1px 1px 5px 0 #a2958a; + padding: 6px; + float: left; + text-align: center +} + +.krajee-default.file-preview-frame .kv-file-content { + width: 213px; + height: 160px +} + +.krajee-default.file-preview-frame .file-thumbnail-footer { + height: 70px +} + +.krajee-default.file-preview-frame:not(.file-preview-error):hover { + box-shadow: 3px 3px 5px 0 #333 +} + +.krajee-default .file-preview-text { + display: block; + color: #428bca; + border: 1px solid #ddd; + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; + outline: 0; + padding: 8px; + resize: none +} + +.krajee-default .file-preview-html { + border: 1px solid #ddd; + padding: 8px; + overflow: auto +} + +.krajee-default .file-other-icon { + font-size: 6em +} + +.krajee-default .file-footer-buttons { + float: right +} + +.krajee-default .file-footer-caption { + display: block; + text-align: center; + padding-top: 4px; + font-size: 11px; + color: #777; + margin-bottom: 15px +} + +.krajee-default .file-preview-error { + opacity: .65; + box-shadow: none +} + +.krajee-default .file-thumb-progress { + height: 11px; + top: 37px; + left: 0; + right: 0 +} + +.krajee-default.kvsortable-ghost { + background: #e1edf7; + border: 2px solid #a1abff +} + +.krajee-default .file-preview-other:hover { + opacity: .8 +} + +.krajee-default .file-preview-frame:not(.file-preview-error) .file-footer-caption:hover { + color: #000 +} + +.kv-upload-progress .progress { + height: 20px; + line-height: 20px; + margin: 10px 0; + overflow: hidden +} + +.kv-upload-progress .progress-bar { + height: 20px; + line-height: 20px +} + +.file-zoom-dialog .file-other-icon { + font-size: 22em; + font-size: 50vmin +} + +.file-zoom-dialog .modal-dialog { + width: auto +} + +.file-zoom-dialog .modal-header { + display: flex; + align-items: center; + justify-content: space-between +} + +.file-zoom-dialog .btn-navigate { + padding: 0; + margin: 0; + background: 0 0; + text-decoration: none; + outline: 0; + opacity: .7; + top: 45%; + font-size: 4em; + color: #1c94c4 +} + +.file-zoom-dialog .btn-navigate:not([disabled]):hover { + outline: 0; + box-shadow: none; + opacity: .6 +} + +.file-zoom-dialog .floating-buttons { + top: 5px; + right: 10px +} + +.file-zoom-dialog .btn-navigate[disabled] { + opacity: .3 +} + +.file-zoom-dialog .btn-prev { + left: 1px +} + +.file-zoom-dialog .btn-next { + right: 1px +} + +.file-zoom-dialog .kv-zoom-title { + font-weight: 300; + color: #999; + max-width: 50%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis +} + +.file-input-ajax-new .no-browse .form-control, .file-input-new .no-browse .form-control { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px +} + +.file-caption-main { + width: 100% +} + +.file-thumb-loading { + background: url(../img/loading.gif) center center no-repeat content-box !important +} + +.file-drop-zone { + border: 1px dashed #aaa; + border-radius: 4px; + height: 100%; + text-align: center; + vertical-align: middle; + margin: 12px 15px 12px 12px; + padding: 5px +} + +.file-drop-zone.clickable:hover { + border: 2px dashed #999 +} + +.file-drop-zone.clickable:focus { + border: 2px solid #5acde2 +} + +.file-drop-zone .file-preview-thumbnails { + cursor: default +} + +.file-drop-zone-title { + color: #aaa; + font-size: 1.6em; + padding: 85px 10px; + cursor: default +} + +.file-highlighted { + border: 2px dashed #999 !important; + background-color: #eee +} + +.file-uploading { + background: url(../img/loading-sm.gif) center bottom 10px no-repeat; + opacity: .65 +} + +@media (min-width: 576px) { + .file-zoom-dialog .modal-dialog { + max-width: 500px + } +} + +@media (min-width: 992px) { + .file-zoom-dialog .modal-lg { + max-width: 800px + } +} + +.file-zoom-fullscreen.modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0 +} + +.file-zoom-fullscreen .modal-dialog { + position: fixed; + margin: 0; + padding: 0; + width: 100%; + height: 100%; + max-width: 100%; + max-height: 100% +} + +.file-zoom-fullscreen .modal-content { + border-radius: 0; + box-shadow: none +} + +.file-zoom-fullscreen .modal-body { + overflow-y: auto +} + +.btn-kv { + display: inline-block; + text-align: center; + width: 30px; + height: 30px; + line-height: 30px; + padding: 0; + font-size: 90%; + border-radius: .2rem +} + +.floating-buttons { + z-index: 3000 +} + +.floating-buttons .btn-kv { + margin-left: 3px; + z-index: 3000 +} + +.file-zoom-content { + height: 480px; + text-align: center +} + +.file-zoom-content .file-preview-image, .file-zoom-content .file-preview-video { + max-height: 100% +} + +.file-zoom-content .is-portrait-gt4 { + margin-top: 60px +} + +.file-zoom-content > .file-object.type-image { + height: auto; + min-height: inherit +} + +.file-zoom-content > .file-object.type-audio { + width: auto; + height: 30px +} + +@media screen and (max-width: 767px) { + .file-preview-thumbnails { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column + } + + .file-zoom-dialog .modal-header { + flex-direction: column + } +} + +@media screen and (max-width: 350px) { + .krajee-default.file-preview-frame .kv-file-content { + width: 160px + } +} + +.file-loading[dir=rtl]:before { + background: url(../img/loading.gif) top right no-repeat; + padding-left: 0; + padding-right: 20px +} + +.file-sortable .file-drag-handle { + cursor: move; + opacity: 1 +} + +.file-sortable .file-drag-handle:hover { + opacity: .7 +} + +.clickable .file-drop-zone-title { + cursor: pointer +} + +.kv-zoom-actions .btn-kv { + margin-left: 3px +} + +.file-preview-initial.sortable-chosen { + background-color: #d9edf7 +} \ No newline at end of file diff --git a/src/main/resources/assets/css/fontawesome-all.css b/src/main/resources/assets/css/fontawesome-all.css new file mode 100644 index 0000000..5a930b6 --- /dev/null +++ b/src/main/resources/assets/css/fontawesome-all.css @@ -0,0 +1,3543 @@ +/*! + * Font Awesome Free 5.0.6 by @fontawesome - http://fontawesome.com + * License - http://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + */ +.fa, +.fas, +.far, +.fal, +.fab { + -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: 0.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: solid 0.08em #eee; + border-radius: .1em; + padding: .2em .25em .15em; +} + +.fa-pull-left { + float: left; +} + +.fa-pull-right { + float: right; +} + +.fa.fa-pull-left, +.fas.fa-pull-left, +.far.fa-pull-left, +.fal.fa-pull-left, +.fab.fa-pull-left { + margin-right: .3em; +} + +.fa.fa-pull-right, +.fas.fa-pull-right, +.far.fa-pull-right, +.fal.fa-pull-right, +.fab.fa-pull-right { + margin-left: .3em; +} + +.fa-spin { + -webkit-animation: fa-spin 2s infinite linear; + animation: fa-spin 2s infinite linear; +} + +.fa-pulse { + -webkit-animation: fa-spin 1s infinite steps(8); + animation: fa-spin 1s infinite steps(8); +} + +@-webkit-keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} + +@keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} + +.fa-rotate-90 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)"; + -webkit-transform: rotate(90deg); + transform: rotate(90deg); +} + +.fa-rotate-180 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)"; + -webkit-transform: rotate(180deg); + transform: rotate(180deg); +} + +.fa-rotate-270 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)"; + -webkit-transform: rotate(270deg); + transform: rotate(270deg); +} + +.fa-flip-horizontal { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)"; + -webkit-transform: scale(-1, 1); + transform: scale(-1, 1); +} + +.fa-flip-vertical { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"; + -webkit-transform: scale(1, -1); + transform: scale(1, -1); +} + +.fa-flip-horizontal.fa-flip-vertical { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"; + -webkit-transform: scale(-1, -1); + transform: scale(-1, -1); +} + +:root .fa-rotate-90, +:root .fa-rotate-180, +:root .fa-rotate-270, +:root .fa-flip-horizontal, +:root .fa-flip-vertical { + -webkit-filter: none; + filter: none; +} + +.fa-stack { + display: inline-block; + height: 2em; + line-height: 2em; + position: relative; + vertical-align: middle; + width: 2em; +} + +.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; +} + +/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen +readers do not read off random characters that represent icons */ +.fa-500px:before { + content: "\f26e"; +} + +.fa-accessible-icon:before { + content: "\f368"; +} + +.fa-accusoft:before { + content: "\f369"; +} + +.fa-address-book:before { + content: "\f2b9"; +} + +.fa-address-card:before { + content: "\f2bb"; +} + +.fa-adjust:before { + content: "\f042"; +} + +.fa-adn:before { + content: "\f170"; +} + +.fa-adversal:before { + content: "\f36a"; +} + +.fa-affiliatetheme:before { + content: "\f36b"; +} + +.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-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-angrycreative:before { + content: "\f36e"; +} + +.fa-angular:before { + content: "\f420"; +} + +.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-pay:before { + content: "\f415"; +} + +.fa-archive:before { + content: "\f187"; +} + +.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-assistive-listening-systems:before { + content: "\f2a2"; +} + +.fa-asterisk:before { + content: "\f069"; +} + +.fa-asymmetrik:before { + content: "\f372"; +} + +.fa-at:before { + content: "\f1fa"; +} + +.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-aws:before { + content: "\f375"; +} + +.fa-backward:before { + content: "\f04a"; +} + +.fa-balance-scale:before { + content: "\f24e"; +} + +.fa-ban:before { + content: "\f05e"; +} + +.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-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-bicycle:before { + content: "\f206"; +} + +.fa-bimobject:before { + content: "\f378"; +} + +.fa-binoculars:before { + content: "\f1e5"; +} + +.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-blind:before { + content: "\f29d"; +} + +.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-book:before { + content: "\f02d"; +} + +.fa-bookmark:before { + content: "\f02e"; +} + +.fa-bowling-ball:before { + content: "\f436"; +} + +.fa-braille:before { + content: "\f2a1"; +} + +.fa-briefcase:before { + content: "\f0b1"; +} + +.fa-btc:before { + content: "\f15a"; +} + +.fa-bug:before { + content: "\f188"; +} + +.fa-building:before { + content: "\f1ad"; +} + +.fa-bullhorn:before { + content: "\f0a1"; +} + +.fa-bullseye:before { + content: "\f140"; +} + +.fa-buromobelexperte:before { + content: "\f37f"; +} + +.fa-bus:before { + content: "\f207"; +} + +.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-minus:before { + content: "\f272"; +} + +.fa-calendar-plus:before { + content: "\f271"; +} + +.fa-calendar-times:before { + content: "\f273"; +} + +.fa-camera:before { + content: "\f030"; +} + +.fa-camera-retro:before { + content: "\f083"; +} + +.fa-car:before { + content: "\f1b9"; +} + +.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-cart-arrow-down:before { + content: "\f218"; +} + +.fa-cart-plus:before { + content: "\f217"; +} + +.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-certificate:before { + content: "\f0a3"; +} + +.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-square:before { + content: "\f14a"; +} + +.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-circle:before { + content: "\f111"; +} + +.fa-circle-notch:before { + content: "\f1ce"; +} + +.fa-clipboard:before { + content: "\f328"; +} + +.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-upload-alt:before { + content: "\f382"; +} + +.fa-cloudscale:before { + content: "\f383"; +} + +.fa-cloudsmith:before { + content: "\f384"; +} + +.fa-cloudversify:before { + content: "\f385"; +} + +.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-columns:before { + content: "\f0db"; +} + +.fa-comment:before { + content: "\f075"; +} + +.fa-comment-alt:before { + content: "\f27a"; +} + +.fa-comments:before { + content: "\f086"; +} + +.fa-compass:before { + content: "\f14e"; +} + +.fa-compress:before { + content: "\f066"; +} + +.fa-connectdevelop:before { + content: "\f20e"; +} + +.fa-contao:before { + content: "\f26d"; +} + +.fa-copy:before { + content: "\f0c5"; +} + +.fa-copyright:before { + content: "\f1f9"; +} + +.fa-cpanel:before { + content: "\f388"; +} + +.fa-creative-commons:before { + content: "\f25e"; +} + +.fa-credit-card:before { + content: "\f09d"; +} + +.fa-crop:before { + content: "\f125"; +} + +.fa-crosshairs:before { + content: "\f05b"; +} + +.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-dashcube:before { + content: "\f210"; +} + +.fa-database:before { + content: "\f1c0"; +} + +.fa-deaf:before { + content: "\f2a4"; +} + +.fa-delicious:before { + content: "\f1a5"; +} + +.fa-deploydog:before { + content: "\f38e"; +} + +.fa-deskpro:before { + content: "\f38f"; +} + +.fa-desktop:before { + content: "\f108"; +} + +.fa-deviantart:before { + content: "\f1bd"; +} + +.fa-digg:before { + content: "\f1a6"; +} + +.fa-digital-ocean:before { + content: "\f391"; +} + +.fa-discord:before { + content: "\f392"; +} + +.fa-discourse:before { + content: "\f393"; +} + +.fa-dochub:before { + content: "\f394"; +} + +.fa-docker:before { + content: "\f395"; +} + +.fa-dollar-sign:before { + content: "\f155"; +} + +.fa-dot-circle:before { + content: "\f192"; +} + +.fa-download:before { + content: "\f019"; +} + +.fa-draft2digital:before { + content: "\f396"; +} + +.fa-dribbble:before { + content: "\f17d"; +} + +.fa-dribbble-square:before { + content: "\f397"; +} + +.fa-dropbox:before { + content: "\f16b"; +} + +.fa-drupal:before { + content: "\f1a9"; +} + +.fa-dyalog:before { + content: "\f399"; +} + +.fa-earlybirds:before { + content: "\f39a"; +} + +.fa-edge:before { + content: "\f282"; +} + +.fa-edit:before { + content: "\f044"; +} + +.fa-eject:before { + content: "\f052"; +} + +.fa-elementor:before { + content: "\f430"; +} + +.fa-ellipsis-h:before { + content: "\f141"; +} + +.fa-ellipsis-v:before { + content: "\f142"; +} + +.fa-ember:before { + content: "\f423"; +} + +.fa-empire:before { + content: "\f1d1"; +} + +.fa-envelope:before { + content: "\f0e0"; +} + +.fa-envelope-open:before { + content: "\f2b6"; +} + +.fa-envelope-square:before { + content: "\f199"; +} + +.fa-envira:before { + content: "\f299"; +} + +.fa-eraser:before { + content: "\f12d"; +} + +.fa-erlang:before { + content: "\f39d"; +} + +.fa-ethereum:before { + content: "\f42e"; +} + +.fa-etsy:before { + content: "\f2d7"; +} + +.fa-euro-sign:before { + content: "\f153"; +} + +.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-fast-backward:before { + content: "\f049"; +} + +.fa-fast-forward:before { + content: "\f050"; +} + +.fa-fax:before { + content: "\f1ac"; +} + +.fa-female:before { + content: "\f182"; +} + +.fa-fighter-jet:before { + content: "\f0fb"; +} + +.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-excel:before { + content: "\f1c3"; +} + +.fa-file-image:before { + content: "\f1c5"; +} + +.fa-file-pdf:before { + content: "\f1c1"; +} + +.fa-file-powerpoint:before { + content: "\f1c4"; +} + +.fa-file-video:before { + content: "\f1c8"; +} + +.fa-file-word:before { + content: "\f1c2"; +} + +.fa-film:before { + content: "\f008"; +} + +.fa-filter:before { + content: "\f0b0"; +} + +.fa-fire:before { + content: "\f06d"; +} + +.fa-fire-extinguisher:before { + content: "\f134"; +} + +.fa-firefox:before { + content: "\f269"; +} + +.fa-first-order:before { + content: "\f2b0"; +} + +.fa-firstdraft:before { + content: "\f3a1"; +} + +.fa-flag:before { + content: "\f024"; +} + +.fa-flag-checkered:before { + content: "\f11e"; +} + +.fa-flask:before { + content: "\f0c3"; +} + +.fa-flickr:before { + content: "\f16e"; +} + +.fa-flipboard:before { + content: "\f44d"; +} + +.fa-fly:before { + content: "\f417"; +} + +.fa-folder:before { + content: "\f07b"; +} + +.fa-folder-open:before { + content: "\f07c"; +} + +.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-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-frown:before { + content: "\f119"; +} + +.fa-futbol:before { + content: "\f1e3"; +} + +.fa-gamepad:before { + content: "\f11b"; +} + +.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-gift:before { + content: "\f06b"; +} + +.fa-git:before { + content: "\f1d3"; +} + +.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-martini:before { + content: "\f000"; +} + +.fa-glide:before { + content: "\f2a5"; +} + +.fa-glide-g:before { + content: "\f2a6"; +} + +.fa-globe:before { + content: "\f0ac"; +} + +.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-graduation-cap:before { + content: "\f19d"; +} + +.fa-gratipay:before { + content: "\f184"; +} + +.fa-grav:before { + content: "\f2d6"; +} + +.fa-gripfire:before { + content: "\f3ac"; +} + +.fa-grunt:before { + content: "\f3ad"; +} + +.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-hand-lizard:before { + content: "\f258"; +} + +.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-handshake:before { + content: "\f2b5"; +} + +.fa-hashtag:before { + content: "\f292"; +} + +.fa-hdd:before { + content: "\f0a0"; +} + +.fa-heading:before { + content: "\f1dc"; +} + +.fa-headphones:before { + content: "\f025"; +} + +.fa-heart:before { + content: "\f004"; +} + +.fa-heartbeat:before { + content: "\f21e"; +} + +.fa-hips:before { + content: "\f452"; +} + +.fa-hire-a-helper:before { + content: "\f3b0"; +} + +.fa-history:before { + content: "\f1da"; +} + +.fa-hockey-puck:before { + content: "\f453"; +} + +.fa-home:before { + content: "\f015"; +} + +.fa-hooli:before { + content: "\f427"; +} + +.fa-hospital:before { + content: "\f0f8"; +} + +.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-houzz:before { + content: "\f27c"; +} + +.fa-html5:before { + content: "\f13b"; +} + +.fa-hubspot:before { + content: "\f3b2"; +} + +.fa-i-cursor:before { + content: "\f246"; +} + +.fa-id-badge:before { + content: "\f2c1"; +} + +.fa-id-card:before { + content: "\f2c2"; +} + +.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-info:before { + content: "\f129"; +} + +.fa-info-circle:before { + content: "\f05a"; +} + +.fa-instagram:before { + content: "\f16d"; +} + +.fa-internet-explorer:before { + content: "\f26b"; +} + +.fa-ioxhost:before { + content: "\f208"; +} + +.fa-italic:before { + content: "\f033"; +} + +.fa-itunes:before { + content: "\f3b4"; +} + +.fa-itunes-note:before { + content: "\f3b5"; +} + +.fa-jenkins:before { + content: "\f3b6"; +} + +.fa-joget:before { + content: "\f3b7"; +} + +.fa-joomla:before { + content: "\f1aa"; +} + +.fa-js:before { + content: "\f3b8"; +} + +.fa-js-square:before { + content: "\f3b9"; +} + +.fa-jsfiddle:before { + content: "\f1cc"; +} + +.fa-key:before { + content: "\f084"; +} + +.fa-keyboard:before { + content: "\f11c"; +} + +.fa-keycdn:before { + content: "\f3ba"; +} + +.fa-kickstarter:before { + content: "\f3bb"; +} + +.fa-kickstarter-k:before { + content: "\f3bc"; +} + +.fa-korvue:before { + content: "\f42f"; +} + +.fa-language:before { + content: "\f1ab"; +} + +.fa-laptop:before { + content: "\f109"; +} + +.fa-laravel:before { + content: "\f3bd"; +} + +.fa-lastfm:before { + content: "\f202"; +} + +.fa-lastfm-square:before { + content: "\f203"; +} + +.fa-leaf:before { + content: "\f06c"; +} + +.fa-leanpub:before { + content: "\f212"; +} + +.fa-lemon:before { + content: "\f094"; +} + +.fa-less:before { + content: "\f41d"; +} + +.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-lyft:before { + content: "\f3c3"; +} + +.fa-magento:before { + content: "\f3c4"; +} + +.fa-magic:before { + content: "\f0d0"; +} + +.fa-magnet:before { + content: "\f076"; +} + +.fa-male:before { + content: "\f183"; +} + +.fa-map:before { + content: "\f279"; +} + +.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-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-maxcdn:before { + content: "\f136"; +} + +.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-meh:before { + content: "\f11a"; +} + +.fa-mercury:before { + content: "\f223"; +} + +.fa-microchip:before { + content: "\f2db"; +} + +.fa-microphone:before { + content: "\f130"; +} + +.fa-microphone-slash:before { + content: "\f131"; +} + +.fa-microsoft:before { + content: "\f3ca"; +} + +.fa-minus:before { + content: "\f068"; +} + +.fa-minus-circle:before { + content: "\f056"; +} + +.fa-minus-square:before { + content: "\f146"; +} + +.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-alt:before { + content: "\f3d1"; +} + +.fa-moon:before { + content: "\f186"; +} + +.fa-motorcycle:before { + content: "\f21c"; +} + +.fa-mouse-pointer:before { + content: "\f245"; +} + +.fa-music:before { + content: "\f001"; +} + +.fa-napster:before { + content: "\f3d2"; +} + +.fa-neuter:before { + content: "\f22c"; +} + +.fa-newspaper:before { + content: "\f1ea"; +} + +.fa-nintendo-switch:before { + content: "\f418"; +} + +.fa-node:before { + content: "\f419"; +} + +.fa-node-js:before { + content: "\f3d3"; +} + +.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-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-outdent:before { + content: "\f03b"; +} + +.fa-page4:before { + content: "\f3d7"; +} + +.fa-pagelines:before { + content: "\f18c"; +} + +.fa-paint-brush:before { + content: "\f1fc"; +} + +.fa-palfed:before { + content: "\f3d8"; +} + +.fa-paper-plane:before { + content: "\f1d8"; +} + +.fa-paperclip:before { + content: "\f0c6"; +} + +.fa-paragraph:before { + content: "\f1dd"; +} + +.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-pen-square:before { + content: "\f14b"; +} + +.fa-pencil-alt:before { + content: "\f303"; +} + +.fa-percent:before { + content: "\f295"; +} + +.fa-periscope:before { + content: "\f3da"; +} + +.fa-phabricator:before { + content: "\f3db"; +} + +.fa-phoenix-framework:before { + content: "\f3dc"; +} + +.fa-phone:before { + content: "\f095"; +} + +.fa-phone-square:before { + content: "\f098"; +} + +.fa-phone-volume:before { + content: "\f2a0"; +} + +.fa-php:before { + content: "\f457"; +} + +.fa-pied-piper:before { + content: "\f2ae"; +} + +.fa-pied-piper-alt:before { + content: "\f1a8"; +} + +.fa-pied-piper-pp:before { + content: "\f1a7"; +} + +.fa-pinterest:before { + content: "\f0d2"; +} + +.fa-pinterest-p:before { + content: "\f231"; +} + +.fa-pinterest-square:before { + content: "\f0d3"; +} + +.fa-plane:before { + content: "\f072"; +} + +.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-pound-sign:before { + content: "\f154"; +} + +.fa-power-off:before { + content: "\f011"; +} + +.fa-print:before { + content: "\f02f"; +} + +.fa-product-hunt:before { + content: "\f288"; +} + +.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-random:before { + content: "\f074"; +} + +.fa-ravelry:before { + content: "\f2d9"; +} + +.fa-react:before { + content: "\f41b"; +} + +.fa-rebel:before { + content: "\f1d0"; +} + +.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-redo:before { + content: "\f01e"; +} + +.fa-redo-alt:before { + content: "\f2f9"; +} + +.fa-registered:before { + content: "\f25d"; +} + +.fa-rendact:before { + content: "\f3e4"; +} + +.fa-renren:before { + content: "\f18b"; +} + +.fa-reply:before { + content: "\f3e5"; +} + +.fa-reply-all:before { + content: "\f122"; +} + +.fa-replyd:before { + content: "\f3e6"; +} + +.fa-resolving:before { + content: "\f3e7"; +} + +.fa-retweet:before { + content: "\f079"; +} + +.fa-road:before { + content: "\f018"; +} + +.fa-rocket:before { + content: "\f135"; +} + +.fa-rocketchat:before { + content: "\f3e8"; +} + +.fa-rockrms:before { + content: "\f3e9"; +} + +.fa-rss:before { + content: "\f09e"; +} + +.fa-rss-square:before { + content: "\f143"; +} + +.fa-ruble-sign:before { + content: "\f158"; +} + +.fa-rupee-sign:before { + content: "\f156"; +} + +.fa-safari:before { + content: "\f267"; +} + +.fa-sass:before { + content: "\f41e"; +} + +.fa-save:before { + content: "\f0c7"; +} + +.fa-schlix:before { + content: "\f3ea"; +} + +.fa-scribd:before { + content: "\f28a"; +} + +.fa-search:before { + content: "\f002"; +} + +.fa-search-minus:before { + content: "\f010"; +} + +.fa-search-plus:before { + content: "\f00e"; +} + +.fa-searchengin:before { + content: "\f3eb"; +} + +.fa-sellcast:before { + content: "\f2da"; +} + +.fa-sellsy:before { + content: "\f213"; +} + +.fa-server:before { + content: "\f233"; +} + +.fa-servicestack:before { + content: "\f3ec"; +} + +.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-shirtsinbulk:before { + content: "\f214"; +} + +.fa-shopping-bag:before { + content: "\f290"; +} + +.fa-shopping-basket:before { + content: "\f291"; +} + +.fa-shopping-cart:before { + content: "\f07a"; +} + +.fa-shower:before { + content: "\f2cc"; +} + +.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-simplybuilt:before { + content: "\f215"; +} + +.fa-sistrix:before { + content: "\f3ee"; +} + +.fa-sitemap:before { + content: "\f0e8"; +} + +.fa-skyatlas:before { + content: "\f216"; +} + +.fa-skype:before { + content: "\f17e"; +} + +.fa-slack:before { + content: "\f198"; +} + +.fa-slack-hash:before { + content: "\f3ef"; +} + +.fa-sliders-h:before { + content: "\f1de"; +} + +.fa-slideshare:before { + content: "\f1e7"; +} + +.fa-smile:before { + content: "\f118"; +} + +.fa-snapchat:before { + content: "\f2ab"; +} + +.fa-snapchat-ghost:before { + content: "\f2ac"; +} + +.fa-snapchat-square:before { + content: "\f2ad"; +} + +.fa-snowflake:before { + content: "\f2dc"; +} + +.fa-sort:before { + content: "\f0dc"; +} + +.fa-sort-alpha-down:before { + content: "\f15d"; +} + +.fa-sort-alpha-up:before { + content: "\f15e"; +} + +.fa-sort-amount-down:before { + content: "\f160"; +} + +.fa-sort-amount-up:before { + content: "\f161"; +} + +.fa-sort-down:before { + content: "\f0dd"; +} + +.fa-sort-numeric-down:before { + content: "\f162"; +} + +.fa-sort-numeric-up:before { + content: "\f163"; +} + +.fa-sort-up:before { + content: "\f0de"; +} + +.fa-soundcloud:before { + content: "\f1be"; +} + +.fa-space-shuttle:before { + content: "\f197"; +} + +.fa-speakap:before { + content: "\f3f3"; +} + +.fa-spinner:before { + content: "\f110"; +} + +.fa-spotify:before { + content: "\f1bc"; +} + +.fa-square:before { + content: "\f0c8"; +} + +.fa-square-full:before { + content: "\f45c"; +} + +.fa-stack-exchange:before { + content: "\f18d"; +} + +.fa-stack-overflow:before { + content: "\f16c"; +} + +.fa-star:before { + content: "\f005"; +} + +.fa-star-half:before { + content: "\f089"; +} + +.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-strava:before { + content: "\f428"; +} + +.fa-street-view:before { + content: "\f21d"; +} + +.fa-strikethrough:before { + content: "\f0cc"; +} + +.fa-stripe:before { + content: "\f429"; +} + +.fa-stripe-s:before { + content: "\f42a"; +} + +.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-sun:before { + content: "\f185"; +} + +.fa-superpowers:before { + content: "\f2dd"; +} + +.fa-superscript:before { + content: "\f12b"; +} + +.fa-supple:before { + content: "\f3f9"; +} + +.fa-sync:before { + content: "\f021"; +} + +.fa-sync-alt:before { + content: "\f2f1"; +} + +.fa-table:before { + content: "\f0ce"; +} + +.fa-table-tennis:before { + content: "\f45d"; +} + +.fa-tablet:before { + content: "\f10a"; +} + +.fa-tablet-alt:before { + content: "\f3fa"; +} + +.fa-tachometer-alt:before { + content: "\f3fd"; +} + +.fa-tag:before { + content: "\f02b"; +} + +.fa-tags:before { + content: "\f02c"; +} + +.fa-tasks:before { + content: "\f0ae"; +} + +.fa-taxi:before { + content: "\f1ba"; +} + +.fa-telegram:before { + content: "\f2c6"; +} + +.fa-telegram-plane:before { + content: "\f3fe"; +} + +.fa-tencent-weibo:before { + content: "\f1d5"; +} + +.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-themeisle:before { + content: "\f2b2"; +} + +.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-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-toggle-off:before { + content: "\f204"; +} + +.fa-toggle-on:before { + content: "\f205"; +} + +.fa-trademark:before { + content: "\f25c"; +} + +.fa-train:before { + content: "\f238"; +} + +.fa-transgender:before { + content: "\f224"; +} + +.fa-transgender-alt:before { + content: "\f225"; +} + +.fa-trash:before { + content: "\f1f8"; +} + +.fa-trash-alt:before { + content: "\f2ed"; +} + +.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-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-uikit:before { + content: "\f403"; +} + +.fa-umbrella:before { + content: "\f0e9"; +} + +.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-usb:before { + content: "\f287"; +} + +.fa-user:before { + content: "\f007"; +} + +.fa-user-circle:before { + content: "\f2bd"; +} + +.fa-user-md:before { + content: "\f0f0"; +} + +.fa-user-plus:before { + content: "\f234"; +} + +.fa-user-secret:before { + content: "\f21b"; +} + +.fa-user-times:before { + content: "\f235"; +} + +.fa-users:before { + content: "\f0c0"; +} + +.fa-ussunnah:before { + content: "\f407"; +} + +.fa-utensil-spoon:before { + content: "\f2e5"; +} + +.fa-utensils:before { + content: "\f2e7"; +} + +.fa-vaadin:before { + content: "\f408"; +} + +.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-viber:before { + content: "\f409"; +} + +.fa-video:before { + content: "\f03d"; +} + +.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-volleyball-ball:before { + content: "\f45f"; +} + +.fa-volume-down:before { + content: "\f027"; +} + +.fa-volume-off:before { + content: "\f026"; +} + +.fa-volume-up:before { + content: "\f028"; +} + +.fa-vuejs:before { + content: "\f41f"; +} + +.fa-weibo:before { + content: "\f18a"; +} + +.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-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-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-wrench:before { + content: "\f0ad"; +} + +.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-yandex:before { + content: "\f413"; +} + +.fa-yandex-international:before { + content: "\f414"; +} + +.fa-yelp:before { + content: "\f1e9"; +} + +.fa-yen-sign:before { + content: "\f157"; +} + +.fa-yoast:before { + content: "\f2b1"; +} + +.fa-youtube:before { + content: "\f167"; +} + +.fa-youtube-square:before { + content: "\f431"; +} + +.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; + src: url("../fonts/fa-brands-400.eot"); + src: url("../fonts/fa-brands-400.eot?#iefix") format("embedded-opentype"), url("../fonts/fa-brands-400.woff2") format("woff2"), url("../fonts/fa-brands-400.woff") format("woff"), url("../fonts/fa-brands-400.ttf") format("truetype"), url("../fonts/fa-brands-400.svg#fontawesome") format("svg"); +} + +.fab { + font-family: 'Font Awesome 5 Brands', serif; +} + +@font-face { + font-family: 'Font Awesome 5 Free'; + font-style: normal; + font-weight: 400; + src: url("../fonts/fa-regular-400.eot"); + src: url("../fonts/fa-regular-400.eot?#iefix") format("embedded-opentype"), url("../fonts/fa-regular-400.woff2") format("woff2"), url("../fonts/fa-regular-400.woff") format("woff"), url("../fonts/fa-regular-400.ttf") format("truetype"), url("../fonts/fa-regular-400.svg#fontawesome") format("svg"); +} + +.far { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; +} + +@font-face { + font-family: 'Font Awesome 5 Free'; + font-style: normal; + font-weight: 900; + src: url("../fonts/fa-solid-900.eot"); + src: url("../fonts/fa-solid-900.eot?#iefix") format("embedded-opentype"), url("../fonts/fa-solid-900.woff2") format("woff2"), url("../fonts/fa-solid-900.woff") format("woff"), url("../fonts/fa-solid-900.ttf") format("truetype"), url("../fonts/fa-solid-900.svg#fontawesome") format("svg"); +} + +.fa, +.fas { + font-family: 'Font Awesome 5 Free'; + font-weight: 900; +} diff --git a/src/main/resources/assets/css/glyphicons.css b/src/main/resources/assets/css/glyphicons.css new file mode 100644 index 0000000..20d07ef --- /dev/null +++ b/src/main/resources/assets/css/glyphicons.css @@ -0,0 +1,1068 @@ +@font-face { + font-family: 'Glyphicons Halflings'; + + src: url('../fonts/glyphicons-halflings-regular.eot'); + src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); +} + +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings', serif; + font-style: normal; + font-weight: normal; + line-height: 1; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.glyphicon-asterisk:before { + content: "\002a"; +} + +.glyphicon-plus:before { + content: "\002b"; +} + +.glyphicon-euro:before, +.glyphicon-eur:before { + content: "\20ac"; +} + +.glyphicon-minus:before { + content: "\2212"; +} + +.glyphicon-cloud:before { + content: "\2601"; +} + +.glyphicon-envelope:before { + content: "\2709"; +} + +.glyphicon-pencil:before { + content: "\270f"; +} + +.glyphicon-glass:before { + content: "\e001"; +} + +.glyphicon-music:before { + content: "\e002"; +} + +.glyphicon-search:before { + content: "\e003"; +} + +.glyphicon-heart:before { + content: "\e005"; +} + +.glyphicon-star:before { + content: "\e006"; +} + +.glyphicon-star-empty:before { + content: "\e007"; +} + +.glyphicon-user:before { + content: "\e008"; +} + +.glyphicon-film:before { + content: "\e009"; +} + +.glyphicon-th-large:before { + content: "\e010"; +} + +.glyphicon-th:before { + content: "\e011"; +} + +.glyphicon-th-list:before { + content: "\e012"; +} + +.glyphicon-ok:before { + content: "\e013"; +} + +.glyphicon-remove:before { + content: "\e014"; +} + +.glyphicon-zoom-in:before { + content: "\e015"; +} + +.glyphicon-zoom-out:before { + content: "\e016"; +} + +.glyphicon-off:before { + content: "\e017"; +} + +.glyphicon-signal:before { + content: "\e018"; +} + +.glyphicon-cog:before { + content: "\e019"; +} + +.glyphicon-trash:before { + content: "\e020"; +} + +.glyphicon-home:before { + content: "\e021"; +} + +.glyphicon-file:before { + content: "\e022"; +} + +.glyphicon-time:before { + content: "\e023"; +} + +.glyphicon-road:before { + content: "\e024"; +} + +.glyphicon-download-alt:before { + content: "\e025"; +} + +.glyphicon-download:before { + content: "\e026"; +} + +.glyphicon-upload:before { + content: "\e027"; +} + +.glyphicon-inbox:before { + content: "\e028"; +} + +.glyphicon-play-circle:before { + content: "\e029"; +} + +.glyphicon-repeat:before { + content: "\e030"; +} + +.glyphicon-refresh:before { + content: "\e031"; +} + +.glyphicon-list-alt:before { + content: "\e032"; +} + +.glyphicon-lock:before { + content: "\e033"; +} + +.glyphicon-flag:before { + content: "\e034"; +} + +.glyphicon-headphones:before { + content: "\e035"; +} + +.glyphicon-volume-off:before { + content: "\e036"; +} + +.glyphicon-volume-down:before { + content: "\e037"; +} + +.glyphicon-volume-up:before { + content: "\e038"; +} + +.glyphicon-qrcode:before { + content: "\e039"; +} + +.glyphicon-barcode:before { + content: "\e040"; +} + +.glyphicon-tag:before { + content: "\e041"; +} + +.glyphicon-tags:before { + content: "\e042"; +} + +.glyphicon-book:before { + content: "\e043"; +} + +.glyphicon-bookmark:before { + content: "\e044"; +} + +.glyphicon-print:before { + content: "\e045"; +} + +.glyphicon-camera:before { + content: "\e046"; +} + +.glyphicon-font:before { + content: "\e047"; +} + +.glyphicon-bold:before { + content: "\e048"; +} + +.glyphicon-italic:before { + content: "\e049"; +} + +.glyphicon-text-height:before { + content: "\e050"; +} + +.glyphicon-text-width:before { + content: "\e051"; +} + +.glyphicon-align-left:before { + content: "\e052"; +} + +.glyphicon-align-center:before { + content: "\e053"; +} + +.glyphicon-align-right:before { + content: "\e054"; +} + +.glyphicon-align-justify:before { + content: "\e055"; +} + +.glyphicon-list:before { + content: "\e056"; +} + +.glyphicon-indent-left:before { + content: "\e057"; +} + +.glyphicon-indent-right:before { + content: "\e058"; +} + +.glyphicon-facetime-video:before { + content: "\e059"; +} + +.glyphicon-picture:before { + content: "\e060"; +} + +.glyphicon-map-marker:before { + content: "\e062"; +} + +.glyphicon-adjust:before { + content: "\e063"; +} + +.glyphicon-tint:before { + content: "\e064"; +} + +.glyphicon-edit:before { + content: "\e065"; +} + +.glyphicon-share:before { + content: "\e066"; +} + +.glyphicon-check:before { + content: "\e067"; +} + +.glyphicon-move:before { + content: "\e068"; +} + +.glyphicon-step-backward:before { + content: "\e069"; +} + +.glyphicon-fast-backward:before { + content: "\e070"; +} + +.glyphicon-backward:before { + content: "\e071"; +} + +.glyphicon-play:before { + content: "\e072"; +} + +.glyphicon-pause:before { + content: "\e073"; +} + +.glyphicon-stop:before { + content: "\e074"; +} + +.glyphicon-forward:before { + content: "\e075"; +} + +.glyphicon-fast-forward:before { + content: "\e076"; +} + +.glyphicon-step-forward:before { + content: "\e077"; +} + +.glyphicon-eject:before { + content: "\e078"; +} + +.glyphicon-chevron-left:before { + content: "\e079"; +} + +.glyphicon-chevron-right:before { + content: "\e080"; +} + +.glyphicon-plus-sign:before { + content: "\e081"; +} + +.glyphicon-minus-sign:before { + content: "\e082"; +} + +.glyphicon-remove-sign:before { + content: "\e083"; +} + +.glyphicon-ok-sign:before { + content: "\e084"; +} + +.glyphicon-question-sign:before { + content: "\e085"; +} + +.glyphicon-info-sign:before { + content: "\e086"; +} + +.glyphicon-screenshot:before { + content: "\e087"; +} + +.glyphicon-remove-circle:before { + content: "\e088"; +} + +.glyphicon-ok-circle:before { + content: "\e089"; +} + +.glyphicon-ban-circle:before { + content: "\e090"; +} + +.glyphicon-arrow-left:before { + content: "\e091"; +} + +.glyphicon-arrow-right:before { + content: "\e092"; +} + +.glyphicon-arrow-up:before { + content: "\e093"; +} + +.glyphicon-arrow-down:before { + content: "\e094"; +} + +.glyphicon-share-alt:before { + content: "\e095"; +} + +.glyphicon-resize-full:before { + content: "\e096"; +} + +.glyphicon-resize-small:before { + content: "\e097"; +} + +.glyphicon-exclamation-sign:before { + content: "\e101"; +} + +.glyphicon-gift:before { + content: "\e102"; +} + +.glyphicon-leaf:before { + content: "\e103"; +} + +.glyphicon-fire:before { + content: "\e104"; +} + +.glyphicon-eye-open:before { + content: "\e105"; +} + +.glyphicon-eye-close:before { + content: "\e106"; +} + +.glyphicon-warning-sign:before { + content: "\e107"; +} + +.glyphicon-plane:before { + content: "\e108"; +} + +.glyphicon-calendar:before { + content: "\e109"; +} + +.glyphicon-random:before { + content: "\e110"; +} + +.glyphicon-comment:before { + content: "\e111"; +} + +.glyphicon-magnet:before { + content: "\e112"; +} + +.glyphicon-chevron-up:before { + content: "\e113"; +} + +.glyphicon-chevron-down:before { + content: "\e114"; +} + +.glyphicon-retweet:before { + content: "\e115"; +} + +.glyphicon-shopping-cart:before { + content: "\e116"; +} + +.glyphicon-folder-close:before { + content: "\e117"; +} + +.glyphicon-folder-open:before { + content: "\e118"; +} + +.glyphicon-resize-vertical:before { + content: "\e119"; +} + +.glyphicon-resize-horizontal:before { + content: "\e120"; +} + +.glyphicon-hdd:before { + content: "\e121"; +} + +.glyphicon-bullhorn:before { + content: "\e122"; +} + +.glyphicon-bell:before { + content: "\e123"; +} + +.glyphicon-certificate:before { + content: "\e124"; +} + +.glyphicon-thumbs-up:before { + content: "\e125"; +} + +.glyphicon-thumbs-down:before { + content: "\e126"; +} + +.glyphicon-hand-right:before { + content: "\e127"; +} + +.glyphicon-hand-left:before { + content: "\e128"; +} + +.glyphicon-hand-up:before { + content: "\e129"; +} + +.glyphicon-hand-down:before { + content: "\e130"; +} + +.glyphicon-circle-arrow-right:before { + content: "\e131"; +} + +.glyphicon-circle-arrow-left:before { + content: "\e132"; +} + +.glyphicon-circle-arrow-up:before { + content: "\e133"; +} + +.glyphicon-circle-arrow-down:before { + content: "\e134"; +} + +.glyphicon-globe:before { + content: "\e135"; +} + +.glyphicon-wrench:before { + content: "\e136"; +} + +.glyphicon-tasks:before { + content: "\e137"; +} + +.glyphicon-filter:before { + content: "\e138"; +} + +.glyphicon-briefcase:before { + content: "\e139"; +} + +.glyphicon-fullscreen:before { + content: "\e140"; +} + +.glyphicon-dashboard:before { + content: "\e141"; +} + +.glyphicon-paperclip:before { + content: "\e142"; +} + +.glyphicon-heart-empty:before { + content: "\e143"; +} + +.glyphicon-link:before { + content: "\e144"; +} + +.glyphicon-phone:before { + content: "\e145"; +} + +.glyphicon-pushpin:before { + content: "\e146"; +} + +.glyphicon-usd:before { + content: "\e148"; +} + +.glyphicon-gbp:before { + content: "\e149"; +} + +.glyphicon-sort:before { + content: "\e150"; +} + +.glyphicon-sort-by-alphabet:before { + content: "\e151"; +} + +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152"; +} + +.glyphicon-sort-by-order:before { + content: "\e153"; +} + +.glyphicon-sort-by-order-alt:before { + content: "\e154"; +} + +.glyphicon-sort-by-attributes:before { + content: "\e155"; +} + +.glyphicon-sort-by-attributes-alt:before { + content: "\e156"; +} + +.glyphicon-unchecked:before { + content: "\e157"; +} + +.glyphicon-expand:before { + content: "\e158"; +} + +.glyphicon-collapse-down:before { + content: "\e159"; +} + +.glyphicon-collapse-up:before { + content: "\e160"; +} + +.glyphicon-log-in:before { + content: "\e161"; +} + +.glyphicon-flash:before { + content: "\e162"; +} + +.glyphicon-log-out:before { + content: "\e163"; +} + +.glyphicon-new-window:before { + content: "\e164"; +} + +.glyphicon-record:before { + content: "\e165"; +} + +.glyphicon-save:before { + content: "\e166"; +} + +.glyphicon-open:before { + content: "\e167"; +} + +.glyphicon-saved:before { + content: "\e168"; +} + +.glyphicon-import:before { + content: "\e169"; +} + +.glyphicon-export:before { + content: "\e170"; +} + +.glyphicon-send:before { + content: "\e171"; +} + +.glyphicon-floppy-disk:before { + content: "\e172"; +} + +.glyphicon-floppy-saved:before { + content: "\e173"; +} + +.glyphicon-floppy-remove:before { + content: "\e174"; +} + +.glyphicon-floppy-save:before { + content: "\e175"; +} + +.glyphicon-floppy-open:before { + content: "\e176"; +} + +.glyphicon-credit-card:before { + content: "\e177"; +} + +.glyphicon-transfer:before { + content: "\e178"; +} + +.glyphicon-cutlery:before { + content: "\e179"; +} + +.glyphicon-header:before { + content: "\e180"; +} + +.glyphicon-compressed:before { + content: "\e181"; +} + +.glyphicon-earphone:before { + content: "\e182"; +} + +.glyphicon-phone-alt:before { + content: "\e183"; +} + +.glyphicon-tower:before { + content: "\e184"; +} + +.glyphicon-stats:before { + content: "\e185"; +} + +.glyphicon-sd-video:before { + content: "\e186"; +} + +.glyphicon-hd-video:before { + content: "\e187"; +} + +.glyphicon-subtitles:before { + content: "\e188"; +} + +.glyphicon-sound-stereo:before { + content: "\e189"; +} + +.glyphicon-sound-dolby:before { + content: "\e190"; +} + +.glyphicon-sound-5-1:before { + content: "\e191"; +} + +.glyphicon-sound-6-1:before { + content: "\e192"; +} + +.glyphicon-sound-7-1:before { + content: "\e193"; +} + +.glyphicon-copyright-mark:before { + content: "\e194"; +} + +.glyphicon-registration-mark:before { + content: "\e195"; +} + +.glyphicon-cloud-download:before { + content: "\e197"; +} + +.glyphicon-cloud-upload:before { + content: "\e198"; +} + +.glyphicon-tree-conifer:before { + content: "\e199"; +} + +.glyphicon-tree-deciduous:before { + content: "\e200"; +} + +.glyphicon-cd:before { + content: "\e201"; +} + +.glyphicon-save-file:before { + content: "\e202"; +} + +.glyphicon-open-file:before { + content: "\e203"; +} + +.glyphicon-level-up:before { + content: "\e204"; +} + +.glyphicon-copy:before { + content: "\e205"; +} + +.glyphicon-paste:before { + content: "\e206"; +} + +.glyphicon-alert:before { + content: "\e209"; +} + +.glyphicon-equalizer:before { + content: "\e210"; +} + +.glyphicon-king:before { + content: "\e211"; +} + +.glyphicon-queen:before { + content: "\e212"; +} + +.glyphicon-pawn:before { + content: "\e213"; +} + +.glyphicon-bishop:before { + content: "\e214"; +} + +.glyphicon-knight:before { + content: "\e215"; +} + +.glyphicon-baby-formula:before { + content: "\e216"; +} + +.glyphicon-tent:before { + content: "\26fa"; +} + +.glyphicon-blackboard:before { + content: "\e218"; +} + +.glyphicon-bed:before { + content: "\e219"; +} + +.glyphicon-apple:before { + content: "\f8ff"; +} + +.glyphicon-erase:before { + content: "\e221"; +} + +.glyphicon-hourglass:before { + content: "\231b"; +} + +.glyphicon-lamp:before { + content: "\e223"; +} + +.glyphicon-duplicate:before { + content: "\e224"; +} + +.glyphicon-piggy-bank:before { + content: "\e225"; +} + +.glyphicon-scissors:before { + content: "\e226"; +} + +.glyphicon-bitcoin:before { + content: "\e227"; +} + +.glyphicon-btc:before { + content: "\e227"; +} + +.glyphicon-xbt:before { + content: "\e227"; +} + +.glyphicon-yen:before { + content: "\00a5"; +} + +.glyphicon-jpy:before { + content: "\00a5"; +} + +.glyphicon-ruble:before { + content: "\20bd"; +} + +.glyphicon-rub:before { + content: "\20bd"; +} + +.glyphicon-scale:before { + content: "\e230"; +} + +.glyphicon-ice-lolly:before { + content: "\e231"; +} + +.glyphicon-ice-lolly-tasted:before { + content: "\e232"; +} + +.glyphicon-education:before { + content: "\e233"; +} + +.glyphicon-option-horizontal:before { + content: "\e234"; +} + +.glyphicon-option-vertical:before { + content: "\e235"; +} + +.glyphicon-menu-hamburger:before { + content: "\e236"; +} + +.glyphicon-modal-window:before { + content: "\e237"; +} + +.glyphicon-oil:before { + content: "\e238"; +} + +.glyphicon-grain:before { + content: "\e239"; +} + +.glyphicon-sunglasses:before { + content: "\e240"; +} + +.glyphicon-text-size:before { + content: "\e241"; +} + +.glyphicon-text-color:before { + content: "\e242"; +} + +.glyphicon-text-background:before { + content: "\e243"; +} + +.glyphicon-object-align-top:before { + content: "\e244"; +} + +.glyphicon-object-align-bottom:before { + content: "\e245"; +} + +.glyphicon-object-align-horizontal:before { + content: "\e246"; +} + +.glyphicon-object-align-left:before { + content: "\e247"; +} + +.glyphicon-object-align-vertical:before { + content: "\e248"; +} + +.glyphicon-object-align-right:before { + content: "\e249"; +} + +.glyphicon-triangle-right:before { + content: "\e250"; +} + +.glyphicon-triangle-left:before { + content: "\e251"; +} + +.glyphicon-triangle-bottom:before { + content: "\e252"; +} + +.glyphicon-triangle-top:before { + content: "\e253"; +} + +.glyphicon-console:before { + content: "\e254"; +} + +.glyphicon-superscript:before { + content: "\e255"; +} + +.glyphicon-subscript:before { + content: "\e256"; +} + +.glyphicon-menu-left:before { + content: "\e257"; +} + +.glyphicon-menu-right:before { + content: "\e258"; +} + +.glyphicon-menu-down:before { + content: "\e259"; +} + +.glyphicon-menu-up:before { + content: "\e260"; +} \ No newline at end of file diff --git a/src/main/resources/assets/css/nav.css b/src/main/resources/assets/css/nav.css new file mode 100644 index 0000000..48d23ee --- /dev/null +++ b/src/main/resources/assets/css/nav.css @@ -0,0 +1,169 @@ +@charset "utf-8"; +/* 以下实际使用若已初始化可删除 .jq-nav height父级需逐级设置为100%*/ + +body, ul { + margin: 0; + padding: 0 +} + +body { + -webkit-text-size-adjust: 100%; +} + +li { + list-style: none +} + +a { + text-decoration: none; +} + +/* 以上实际使用若已初始化可删除 */ + +/* nav */ +.jq-nav { + width: 220px; + height: 100%; + background: #263238; + transition: all .3s; +} + +.jq-nav a { + display: block; + overflow: hidden; + padding-left: 20px; + line-height: 46px; + max-height: 46px; + color: #ABB1B7; + transition: all .3s; +} + +.jq-nav a span { + margin-left: 30px; +} + +.jq-nav-item { + position: relative; +} + +.jq-nav-item.jq-nav-show { + border-bottom: none; +} + +.jq-nav-item ul { + display: none; + background: rgba(0, 0, 0, .1); +} + +.jq-nav-item.jq-nav-show ul { + display: block; +} + +.jq-nav-item > a:before { + content: ""; + position: absolute; + left: 0; + width: 2px; + height: 46px; + background: #34A0CE; + opacity: 0; + transition: all .3s; +} + +.jq-nav .jq-nav-icon { + font-size: 20px; + position: absolute; + margin-left: -1px; +} + +/* 此处修改导航图标 可自定义iconfont 替换*/ +.icon_1::after { + content: "\e62b"; +} + +.icon_2::after { + content: "\e669"; +} + +.icon_3::after { + content: "\e61d"; +} + +/*---------------------*/ +.jq-nav-more { + float: right; + margin-right: 20px; + font-size: 12px; + transition: transform .3s; +} + +/* 此处为导航右侧箭头 如果自定义iconfont 也需要替换*/ +.jq-nav-more::after { + content: "\e621"; +} + +/*---------------------*/ +.jq-nav-show .jq-nav-more { + transform: rotate(90deg); +} + +.jq-nav-show, .jq-nav-item > a:hover { + color: #FFF; + background: rgba(0, 0, 0, .1); +} + +.jq-nav-show > a:before, .jq-nav-item > a:hover:before { + opacity: 1; +} + +.jq-nav-item li:hover a { + color: #FFF; + background: rgba(0, 0, 0, .1); +} + +/* nav-mini */ +.jq-nav-mini.jq-nav { + width: 60px; +} + +.jq-nav-mini.jq-nav .jq-nav-icon { /* margin-left:-2px; */ +} + +.jq-nav-mini.jq-nav .jq-nav-item > a span { + display: none; +} + +.jq-nav-mini.jq-nav .jq-nav-more { + margin-right: -20px; +} + +.jq-nav-mini.jq-nav .jq-nav-item ul { + position: absolute; + top: 0; + left: 60px; + width: 180px; + z-index: 99; + background: #3C474C; + overflow: hidden; +} + +.jq-nav-mini.jq-nav .jq-nav-item:hover { + background: rgba(255, 255, 255, .1); +} + +.jq-nav-mini.jq-nav .jq-nav-item:hover .jq-nav-item a { + color: #FFF; +} + +.jq-nav-mini.jq-nav .jq-nav-item:hover a:before { + opacity: 1; +} + +.jq-nav-mini.jq-nav .jq-nav-item:hover ul { + display: block; +} + + + + + diff --git a/src/main/resources/assets/css/old/bootstrap.min.css b/src/main/resources/assets/css/old/bootstrap.min.css new file mode 100644 index 0000000..2b93194 --- /dev/null +++ b/src/main/resources/assets/css/old/bootstrap.min.css @@ -0,0 +1,7915 @@ +@import url("https://fonts.googleapis.com/css?family=Roboto:300,400,500,700"); + +/*! + * bootswatch v3.3.7 + * Homepage: http://bootswatch.com + * Copyright 2012-2016 Thomas Park + * Licensed under MIT + * Based on Bootstrap +*//*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ +html { + font-family: sans-serif; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100% +} + +body { + margin: 0 +} + +article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { + display: block +} + +audio, canvas, progress, video { + display: inline-block; + vertical-align: baseline +} + +audio:not([controls]) { + display: none; + height: 0 +} + +[hidden], template { + display: none +} + +a { + background-color: transparent +} + +a:active, a:hover { + outline: 0 +} + +abbr[title] { + border-bottom: 1px dotted +} + +b, strong { + font-weight: bold +} + +dfn { + font-style: italic +} + +h1 { + font-size: 2em; + margin: 0.67em 0 +} + +mark { + background: #ff0; + color: #000 +} + +small { + font-size: 80% +} + +sub, sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline +} + +sup { + top: -0.5em +} + +sub { + bottom: -0.25em +} + +img { + border: 0 +} + +svg:not(:root) { + overflow: hidden +} + +figure { + margin: 1em 40px +} + +hr { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + height: 0 +} + +pre { + overflow: auto +} + +code, kbd, pre, samp { + font-family: monospace, monospace; + font-size: 1em +} + +button, input, optgroup, select, textarea { + color: inherit; + font: inherit; + margin: 0 +} + +button { + overflow: visible +} + +button, select { + text-transform: none +} + +button, html input[type="button"], input[type="reset"], input[type="submit"] { + -webkit-appearance: button; + cursor: pointer +} + +button[disabled], html input[disabled] { + cursor: default +} + +button::-moz-focus-inner, input::-moz-focus-inner { + border: 0; + padding: 0 +} + +input { + line-height: normal +} + +input[type="checkbox"], input[type="radio"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 0 +} + +input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button { + height: auto +} + +input[type="search"] { + -webkit-appearance: textfield; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box +} + +input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none +} + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em +} + +legend { + border: 0; + padding: 0 +} + +textarea { + overflow: auto +} + +optgroup { + font-weight: bold +} + +table { + border-collapse: collapse; + border-spacing: 0 +} + +td, th { + padding: 0 +} + +/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ +@media print { + *, *:before, *:after { + background: transparent !important; + color: #000 !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; + text-shadow: none !important + } + + a, a:visited { + text-decoration: underline + } + + a[href]:after { + content: " (" attr(href) ")" + } + + abbr[title]:after { + content: " (" attr(title) ")" + } + + a[href^="#"]:after, a[href^="javascript:"]:after { + content: "" + } + + pre, blockquote { + border: 1px solid #999; + page-break-inside: avoid + } + + thead { + display: table-header-group + } + + tr, img { + page-break-inside: avoid + } + + img { + max-width: 100% !important + } + + p, h2, h3 { + orphans: 3; + widows: 3 + } + + h2, h3 { + page-break-after: avoid + } + + .navbar { + display: none + } + + .btn > .caret, .dropup > .btn > .caret { + border-top-color: #000 !important + } + + .label { + border: 1px solid #000 + } + + .table { + border-collapse: collapse !important + } + + .table td, .table th { + background-color: #fff !important + } + + .table-bordered th, .table-bordered td { + border: 1px solid #ddd !important + } +} + +/*@font-face {*/ + /*font-family: 'Glyphicons Halflings';*/ + /*src: url('../fonts/glyphicons-halflings-regular.eot');*/ + /*src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg')*/ +/*}*/ + +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: normal; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale +} + +.glyphicon-asterisk:before { + content: "\002a" +} + +.glyphicon-plus:before { + content: "\002b" +} + +.glyphicon-euro:before, .glyphicon-eur:before { + content: "\20ac" +} + +.glyphicon-minus:before { + content: "\2212" +} + +.glyphicon-cloud:before { + content: "\2601" +} + +.glyphicon-envelope:before { + content: "\2709" +} + +.glyphicon-pencil:before { + content: "\270f" +} + +.glyphicon-glass:before { + content: "\e001" +} + +.glyphicon-music:before { + content: "\e002" +} + +.glyphicon-search:before { + content: "\e003" +} + +.glyphicon-heart:before { + content: "\e005" +} + +.glyphicon-star:before { + content: "\e006" +} + +.glyphicon-star-empty:before { + content: "\e007" +} + +.glyphicon-user:before { + content: "\e008" +} + +.glyphicon-film:before { + content: "\e009" +} + +.glyphicon-th-large:before { + content: "\e010" +} + +.glyphicon-th:before { + content: "\e011" +} + +.glyphicon-th-list:before { + content: "\e012" +} + +.glyphicon-ok:before { + content: "\e013" +} + +.glyphicon-remove:before { + content: "\e014" +} + +.glyphicon-zoom-in:before { + content: "\e015" +} + +.glyphicon-zoom-out:before { + content: "\e016" +} + +.glyphicon-off:before { + content: "\e017" +} + +.glyphicon-signal:before { + content: "\e018" +} + +.glyphicon-cog:before { + content: "\e019" +} + +.glyphicon-trash:before { + content: "\e020" +} + +.glyphicon-home:before { + content: "\e021" +} + +.glyphicon-file:before { + content: "\e022" +} + +.glyphicon-time:before { + content: "\e023" +} + +.glyphicon-road:before { + content: "\e024" +} + +.glyphicon-download-alt:before { + content: "\e025" +} + +.glyphicon-download:before { + content: "\e026" +} + +.glyphicon-upload:before { + content: "\e027" +} + +.glyphicon-inbox:before { + content: "\e028" +} + +.glyphicon-play-circle:before { + content: "\e029" +} + +.glyphicon-repeat:before { + content: "\e030" +} + +.glyphicon-refresh:before { + content: "\e031" +} + +.glyphicon-list-alt:before { + content: "\e032" +} + +.glyphicon-lock:before { + content: "\e033" +} + +.glyphicon-flag:before { + content: "\e034" +} + +.glyphicon-headphones:before { + content: "\e035" +} + +.glyphicon-volume-off:before { + content: "\e036" +} + +.glyphicon-volume-down:before { + content: "\e037" +} + +.glyphicon-volume-up:before { + content: "\e038" +} + +.glyphicon-qrcode:before { + content: "\e039" +} + +.glyphicon-barcode:before { + content: "\e040" +} + +.glyphicon-tag:before { + content: "\e041" +} + +.glyphicon-tags:before { + content: "\e042" +} + +.glyphicon-book:before { + content: "\e043" +} + +.glyphicon-bookmark:before { + content: "\e044" +} + +.glyphicon-print:before { + content: "\e045" +} + +.glyphicon-camera:before { + content: "\e046" +} + +.glyphicon-font:before { + content: "\e047" +} + +.glyphicon-bold:before { + content: "\e048" +} + +.glyphicon-italic:before { + content: "\e049" +} + +.glyphicon-text-height:before { + content: "\e050" +} + +.glyphicon-text-width:before { + content: "\e051" +} + +.glyphicon-align-left:before { + content: "\e052" +} + +.glyphicon-align-center:before { + content: "\e053" +} + +.glyphicon-align-right:before { + content: "\e054" +} + +.glyphicon-align-justify:before { + content: "\e055" +} + +.glyphicon-list:before { + content: "\e056" +} + +.glyphicon-indent-left:before { + content: "\e057" +} + +.glyphicon-indent-right:before { + content: "\e058" +} + +.glyphicon-facetime-video:before { + content: "\e059" +} + +.glyphicon-picture:before { + content: "\e060" +} + +.glyphicon-map-marker:before { + content: "\e062" +} + +.glyphicon-adjust:before { + content: "\e063" +} + +.glyphicon-tint:before { + content: "\e064" +} + +.glyphicon-edit:before { + content: "\e065" +} + +.glyphicon-share:before { + content: "\e066" +} + +.glyphicon-check:before { + content: "\e067" +} + +.glyphicon-move:before { + content: "\e068" +} + +.glyphicon-step-backward:before { + content: "\e069" +} + +.glyphicon-fast-backward:before { + content: "\e070" +} + +.glyphicon-backward:before { + content: "\e071" +} + +.glyphicon-play:before { + content: "\e072" +} + +.glyphicon-pause:before { + content: "\e073" +} + +.glyphicon-stop:before { + content: "\e074" +} + +.glyphicon-forward:before { + content: "\e075" +} + +.glyphicon-fast-forward:before { + content: "\e076" +} + +.glyphicon-step-forward:before { + content: "\e077" +} + +.glyphicon-eject:before { + content: "\e078" +} + +.glyphicon-chevron-left:before { + content: "\e079" +} + +.glyphicon-chevron-right:before { + content: "\e080" +} + +.glyphicon-plus-sign:before { + content: "\e081" +} + +.glyphicon-minus-sign:before { + content: "\e082" +} + +.glyphicon-remove-sign:before { + content: "\e083" +} + +.glyphicon-ok-sign:before { + content: "\e084" +} + +.glyphicon-question-sign:before { + content: "\e085" +} + +.glyphicon-info-sign:before { + content: "\e086" +} + +.glyphicon-screenshot:before { + content: "\e087" +} + +.glyphicon-remove-circle:before { + content: "\e088" +} + +.glyphicon-ok-circle:before { + content: "\e089" +} + +.glyphicon-ban-circle:before { + content: "\e090" +} + +.glyphicon-arrow-left:before { + content: "\e091" +} + +.glyphicon-arrow-right:before { + content: "\e092" +} + +.glyphicon-arrow-up:before { + content: "\e093" +} + +.glyphicon-arrow-down:before { + content: "\e094" +} + +.glyphicon-share-alt:before { + content: "\e095" +} + +.glyphicon-resize-full:before { + content: "\e096" +} + +.glyphicon-resize-small:before { + content: "\e097" +} + +.glyphicon-exclamation-sign:before { + content: "\e101" +} + +.glyphicon-gift:before { + content: "\e102" +} + +.glyphicon-leaf:before { + content: "\e103" +} + +.glyphicon-fire:before { + content: "\e104" +} + +.glyphicon-eye-open:before { + content: "\e105" +} + +.glyphicon-eye-close:before { + content: "\e106" +} + +.glyphicon-warning-sign:before { + content: "\e107" +} + +.glyphicon-plane:before { + content: "\e108" +} + +.glyphicon-calendar:before { + content: "\e109" +} + +.glyphicon-random:before { + content: "\e110" +} + +.glyphicon-comment:before { + content: "\e111" +} + +.glyphicon-magnet:before { + content: "\e112" +} + +.glyphicon-chevron-up:before { + content: "\e113" +} + +.glyphicon-chevron-down:before { + content: "\e114" +} + +.glyphicon-retweet:before { + content: "\e115" +} + +.glyphicon-shopping-cart:before { + content: "\e116" +} + +.glyphicon-folder-close:before { + content: "\e117" +} + +.glyphicon-folder-open:before { + content: "\e118" +} + +.glyphicon-resize-vertical:before { + content: "\e119" +} + +.glyphicon-resize-horizontal:before { + content: "\e120" +} + +.glyphicon-hdd:before { + content: "\e121" +} + +.glyphicon-bullhorn:before { + content: "\e122" +} + +.glyphicon-bell:before { + content: "\e123" +} + +.glyphicon-certificate:before { + content: "\e124" +} + +.glyphicon-thumbs-up:before { + content: "\e125" +} + +.glyphicon-thumbs-down:before { + content: "\e126" +} + +.glyphicon-hand-right:before { + content: "\e127" +} + +.glyphicon-hand-left:before { + content: "\e128" +} + +.glyphicon-hand-up:before { + content: "\e129" +} + +.glyphicon-hand-down:before { + content: "\e130" +} + +.glyphicon-circle-arrow-right:before { + content: "\e131" +} + +.glyphicon-circle-arrow-left:before { + content: "\e132" +} + +.glyphicon-circle-arrow-up:before { + content: "\e133" +} + +.glyphicon-circle-arrow-down:before { + content: "\e134" +} + +.glyphicon-globe:before { + content: "\e135" +} + +.glyphicon-wrench:before { + content: "\e136" +} + +.glyphicon-tasks:before { + content: "\e137" +} + +.glyphicon-filter:before { + content: "\e138" +} + +.glyphicon-briefcase:before { + content: "\e139" +} + +.glyphicon-fullscreen:before { + content: "\e140" +} + +.glyphicon-dashboard:before { + content: "\e141" +} + +.glyphicon-paperclip:before { + content: "\e142" +} + +.glyphicon-heart-empty:before { + content: "\e143" +} + +.glyphicon-link:before { + content: "\e144" +} + +.glyphicon-phone:before { + content: "\e145" +} + +.glyphicon-pushpin:before { + content: "\e146" +} + +.glyphicon-usd:before { + content: "\e148" +} + +.glyphicon-gbp:before { + content: "\e149" +} + +.glyphicon-sort:before { + content: "\e150" +} + +.glyphicon-sort-by-alphabet:before { + content: "\e151" +} + +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152" +} + +.glyphicon-sort-by-order:before { + content: "\e153" +} + +.glyphicon-sort-by-order-alt:before { + content: "\e154" +} + +.glyphicon-sort-by-attributes:before { + content: "\e155" +} + +.glyphicon-sort-by-attributes-alt:before { + content: "\e156" +} + +.glyphicon-unchecked:before { + content: "\e157" +} + +.glyphicon-expand:before { + content: "\e158" +} + +.glyphicon-collapse-down:before { + content: "\e159" +} + +.glyphicon-collapse-up:before { + content: "\e160" +} + +.glyphicon-log-in:before { + content: "\e161" +} + +.glyphicon-flash:before { + content: "\e162" +} + +.glyphicon-log-out:before { + content: "\e163" +} + +.glyphicon-new-window:before { + content: "\e164" +} + +.glyphicon-record:before { + content: "\e165" +} + +.glyphicon-save:before { + content: "\e166" +} + +.glyphicon-open:before { + content: "\e167" +} + +.glyphicon-saved:before { + content: "\e168" +} + +.glyphicon-import:before { + content: "\e169" +} + +.glyphicon-export:before { + content: "\e170" +} + +.glyphicon-send:before { + content: "\e171" +} + +.glyphicon-floppy-disk:before { + content: "\e172" +} + +.glyphicon-floppy-saved:before { + content: "\e173" +} + +.glyphicon-floppy-remove:before { + content: "\e174" +} + +.glyphicon-floppy-save:before { + content: "\e175" +} + +.glyphicon-floppy-open:before { + content: "\e176" +} + +.glyphicon-credit-card:before { + content: "\e177" +} + +.glyphicon-transfer:before { + content: "\e178" +} + +.glyphicon-cutlery:before { + content: "\e179" +} + +.glyphicon-header:before { + content: "\e180" +} + +.glyphicon-compressed:before { + content: "\e181" +} + +.glyphicon-earphone:before { + content: "\e182" +} + +.glyphicon-phone-alt:before { + content: "\e183" +} + +.glyphicon-tower:before { + content: "\e184" +} + +.glyphicon-stats:before { + content: "\e185" +} + +.glyphicon-sd-video:before { + content: "\e186" +} + +.glyphicon-hd-video:before { + content: "\e187" +} + +.glyphicon-subtitles:before { + content: "\e188" +} + +.glyphicon-sound-stereo:before { + content: "\e189" +} + +.glyphicon-sound-dolby:before { + content: "\e190" +} + +.glyphicon-sound-5-1:before { + content: "\e191" +} + +.glyphicon-sound-6-1:before { + content: "\e192" +} + +.glyphicon-sound-7-1:before { + content: "\e193" +} + +.glyphicon-copyright-mark:before { + content: "\e194" +} + +.glyphicon-registration-mark:before { + content: "\e195" +} + +.glyphicon-cloud-download:before { + content: "\e197" +} + +.glyphicon-cloud-upload:before { + content: "\e198" +} + +.glyphicon-tree-conifer:before { + content: "\e199" +} + +.glyphicon-tree-deciduous:before { + content: "\e200" +} + +.glyphicon-cd:before { + content: "\e201" +} + +.glyphicon-save-file:before { + content: "\e202" +} + +.glyphicon-open-file:before { + content: "\e203" +} + +.glyphicon-level-up:before { + content: "\e204" +} + +.glyphicon-copy:before { + content: "\e205" +} + +.glyphicon-paste:before { + content: "\e206" +} + +.glyphicon-alert:before { + content: "\e209" +} + +.glyphicon-equalizer:before { + content: "\e210" +} + +.glyphicon-king:before { + content: "\e211" +} + +.glyphicon-queen:before { + content: "\e212" +} + +.glyphicon-pawn:before { + content: "\e213" +} + +.glyphicon-bishop:before { + content: "\e214" +} + +.glyphicon-knight:before { + content: "\e215" +} + +.glyphicon-baby-formula:before { + content: "\e216" +} + +.glyphicon-tent:before { + content: "\26fa" +} + +.glyphicon-blackboard:before { + content: "\e218" +} + +.glyphicon-bed:before { + content: "\e219" +} + +.glyphicon-apple:before { + content: "\f8ff" +} + +.glyphicon-erase:before { + content: "\e221" +} + +.glyphicon-hourglass:before { + content: "\231b" +} + +.glyphicon-lamp:before { + content: "\e223" +} + +.glyphicon-duplicate:before { + content: "\e224" +} + +.glyphicon-piggy-bank:before { + content: "\e225" +} + +.glyphicon-scissors:before { + content: "\e226" +} + +.glyphicon-bitcoin:before { + content: "\e227" +} + +.glyphicon-btc:before { + content: "\e227" +} + +.glyphicon-xbt:before { + content: "\e227" +} + +.glyphicon-yen:before { + content: "\00a5" +} + +.glyphicon-jpy:before { + content: "\00a5" +} + +.glyphicon-ruble:before { + content: "\20bd" +} + +.glyphicon-rub:before { + content: "\20bd" +} + +.glyphicon-scale:before { + content: "\e230" +} + +.glyphicon-ice-lolly:before { + content: "\e231" +} + +.glyphicon-ice-lolly-tasted:before { + content: "\e232" +} + +.glyphicon-education:before { + content: "\e233" +} + +.glyphicon-option-horizontal:before { + content: "\e234" +} + +.glyphicon-option-vertical:before { + content: "\e235" +} + +.glyphicon-menu-hamburger:before { + content: "\e236" +} + +.glyphicon-modal-window:before { + content: "\e237" +} + +.glyphicon-oil:before { + content: "\e238" +} + +.glyphicon-grain:before { + content: "\e239" +} + +.glyphicon-sunglasses:before { + content: "\e240" +} + +.glyphicon-text-size:before { + content: "\e241" +} + +.glyphicon-text-color:before { + content: "\e242" +} + +.glyphicon-text-background:before { + content: "\e243" +} + +.glyphicon-object-align-top:before { + content: "\e244" +} + +.glyphicon-object-align-bottom:before { + content: "\e245" +} + +.glyphicon-object-align-horizontal:before { + content: "\e246" +} + +.glyphicon-object-align-left:before { + content: "\e247" +} + +.glyphicon-object-align-vertical:before { + content: "\e248" +} + +.glyphicon-object-align-right:before { + content: "\e249" +} + +.glyphicon-triangle-right:before { + content: "\e250" +} + +.glyphicon-triangle-left:before { + content: "\e251" +} + +.glyphicon-triangle-bottom:before { + content: "\e252" +} + +.glyphicon-triangle-top:before { + content: "\e253" +} + +.glyphicon-console:before { + content: "\e254" +} + +.glyphicon-superscript:before { + content: "\e255" +} + +.glyphicon-subscript:before { + content: "\e256" +} + +.glyphicon-menu-left:before { + content: "\e257" +} + +.glyphicon-menu-right:before { + content: "\e258" +} + +.glyphicon-menu-down:before { + content: "\e259" +} + +.glyphicon-menu-up:before { + content: "\e260" +} + +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box +} + +*:before, *:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box +} + +html { + font-size: 10px; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0) +} + +body { + font-family: "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 13px; + line-height: 1.846; + color: #666666; + background-color: #ffffff +} + +input, button, select, textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit +} + +a { + color: #2196f3; + text-decoration: none +} + +a:hover, a:focus { + color: #0a6ebd; + text-decoration: underline +} + +a:focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px +} + +figure { + margin: 0 +} + +img { + vertical-align: middle +} + +.img-responsive, .thumbnail > img, .thumbnail a > img, .carousel-inner > .item > img, .carousel-inner > .item > a > img { + display: block; + max-width: 100%; + height: auto +} + +.img-rounded { + border-radius: 3px +} + +.img-thumbnail { + padding: 4px; + line-height: 1.846; + background-color: #ffffff; + border: 1px solid #dddddd; + border-radius: 3px; + -webkit-transition: all .2s ease-in-out; + -o-transition: all .2s ease-in-out; + transition: all .2s ease-in-out; + display: inline-block; + max-width: 100%; + height: auto +} + +.img-circle { + border-radius: 50% +} + +hr { + margin-top: 23px; + margin-bottom: 23px; + border: 0; + border-top: 1px solid #eeeeee +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0 +} + +.sr-only-focusable:active, .sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto +} + +[role="button"] { + cursor: pointer +} + +h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { + font-family: inherit; + font-weight: 400; + line-height: 1.1; + color: #444444 +} + +h1 small, h2 small, h3 small, h4 small, h5 small, h6 small, .h1 small, .h2 small, .h3 small, .h4 small, .h5 small, .h6 small, h1 .small, h2 .small, h3 .small, h4 .small, h5 .small, h6 .small, .h1 .small, .h2 .small, .h3 .small, .h4 .small, .h5 .small, .h6 .small { + font-weight: normal; + line-height: 1; + color: #bbbbbb +} + +h1, .h1, h2, .h2, h3, .h3 { + margin-top: 23px; + margin-bottom: 11.5px +} + +h1 small, .h1 small, h2 small, .h2 small, h3 small, .h3 small, h1 .small, .h1 .small, h2 .small, .h2 .small, h3 .small, .h3 .small { + font-size: 65% +} + +h4, .h4, h5, .h5, h6, .h6 { + margin-top: 11.5px; + margin-bottom: 11.5px +} + +h4 small, .h4 small, h5 small, .h5 small, h6 small, .h6 small, h4 .small, .h4 .small, h5 .small, .h5 .small, h6 .small, .h6 .small { + font-size: 75% +} + +h1, .h1 { + font-size: 56px +} + +h2, .h2 { + font-size: 45px +} + +h3, .h3 { + font-size: 34px +} + +h4, .h4 { + font-size: 24px +} + +h5, .h5 { + font-size: 20px +} + +h6, .h6 { + font-size: 14px +} + +p { + margin: 0 0 11.5px +} + +.lead { + margin-bottom: 23px; + font-size: 14px; + font-weight: 300; + line-height: 1.4 +} + +@media (min-width: 768px) { + .lead { + font-size: 19.5px + } +} + +small, .small { + font-size: 92% +} + +mark, .mark { + background-color: #ffe0b2; + padding: .2em +} + +.text-left { + text-align: left +} + +.text-right { + text-align: right +} + +.text-center { + text-align: center +} + +.text-justify { + text-align: justify +} + +.text-nowrap { + white-space: nowrap +} + +.text-lowercase { + text-transform: lowercase +} + +.text-uppercase { + text-transform: uppercase +} + +.text-capitalize { + text-transform: capitalize +} + +.text-muted { + color: #bbbbbb +} + +.text-primary { + color: #2196f3 +} + +a.text-primary:hover, a.text-primary:focus { + color: #0c7cd5 +} + +.text-success { + color: #4caf50 +} + +a.text-success:hover, a.text-success:focus { + color: #3d8b40 +} + +.text-info { + color: #9c27b0 +} + +a.text-info:hover, a.text-info:focus { + color: #771e86 +} + +.text-warning { + color: #ff9800 +} + +a.text-warning:hover, a.text-warning:focus { + color: #cc7a00 +} + +.text-danger { + color: #e51c23 +} + +a.text-danger:hover, a.text-danger:focus { + color: #b9151b +} + +.bg-primary { + color: #fff; + background-color: #2196f3 +} + +a.bg-primary:hover, a.bg-primary:focus { + background-color: #0c7cd5 +} + +.bg-success { + background-color: #dff0d8 +} + +a.bg-success:hover, a.bg-success:focus { + background-color: #c1e2b3 +} + +.bg-info { + background-color: #e1bee7 +} + +a.bg-info:hover, a.bg-info:focus { + background-color: #d099d9 +} + +.bg-warning { + background-color: #ffe0b2 +} + +a.bg-warning:hover, a.bg-warning:focus { + background-color: #ffcb7f +} + +.bg-danger { + background-color: #f9bdbb +} + +a.bg-danger:hover, a.bg-danger:focus { + background-color: #f5908c +} + +.page-header { + padding-bottom: 10.5px; + margin: 46px 0 23px; + border-bottom: 1px solid #eeeeee +} + +ul, ol { + margin-top: 0; + margin-bottom: 11.5px +} + +ul ul, ol ul, ul ol, ol ol { + margin-bottom: 0 +} + +.list-unstyled { + padding-left: 0; + list-style: none +} + +.list-inline { + padding-left: 0; + list-style: none; + margin-left: -5px +} + +.list-inline > li { + display: inline-block; + padding-left: 5px; + padding-right: 5px +} + +dl { + margin-top: 0; + margin-bottom: 23px +} + +dt, dd { + line-height: 1.846 +} + +dt { + font-weight: bold +} + +dd { + margin-left: 0 +} + +@media (min-width: 768px) { + .dl-horizontal dt { + float: left; + width: 160px; + clear: left; + text-align: right; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap + } + + .dl-horizontal dd { + margin-left: 180px + } +} + +abbr[title], abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #bbbbbb +} + +.initialism { + font-size: 90%; + text-transform: uppercase +} + +blockquote { + padding: 11.5px 23px; + margin: 0 0 23px; + font-size: 16.25px; + border-left: 5px solid #eeeeee +} + +blockquote p:last-child, blockquote ul:last-child, blockquote ol:last-child { + margin-bottom: 0 +} + +blockquote footer, blockquote small, blockquote .small { + display: block; + font-size: 80%; + line-height: 1.846; + color: #bbbbbb +} + +blockquote footer:before, blockquote small:before, blockquote .small:before { + content: '\2014 \00A0' +} + +.blockquote-reverse, blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + border-right: 5px solid #eeeeee; + border-left: 0; + text-align: right +} + +.blockquote-reverse footer:before, blockquote.pull-right footer:before, .blockquote-reverse small:before, blockquote.pull-right small:before, .blockquote-reverse .small:before, blockquote.pull-right .small:before { + content: '' +} + +.blockquote-reverse footer:after, blockquote.pull-right footer:after, .blockquote-reverse small:after, blockquote.pull-right small:after, .blockquote-reverse .small:after, blockquote.pull-right .small:after { + content: '\00A0 \2014' +} + +address { + margin-bottom: 23px; + font-style: normal; + line-height: 1.846 +} + +code, kbd, pre, samp { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace +} + +code { + padding: 2px 4px; + font-size: 90%; + color: #c7254e; + background-color: #f9f2f4; + border-radius: 3px +} + +kbd { + padding: 2px 4px; + font-size: 90%; + color: #ffffff; + background-color: #333333; + border-radius: 3px; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25) +} + +kbd kbd { + padding: 0; + font-size: 100%; + font-weight: bold; + -webkit-box-shadow: none; + box-shadow: none +} + +pre { + display: block; + padding: 11px; + margin: 0 0 11.5px; + font-size: 12px; + line-height: 1.846; + word-break: break-all; + word-wrap: break-word; + color: #212121; + background-color: #f5f5f5; + border: 1px solid #cccccc; + border-radius: 3px +} + +pre code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0 +} + +.pre-scrollable { + max-height: 340px; + overflow-y: scroll +} + +.container { + margin-right: auto; + margin-left: auto; + padding-left: 15px; + padding-right: 15px +} + +@media (min-width: 768px) { + .container { + width: 750px + } +} + +@media (min-width: 992px) { + .container { + width: 970px + } +} + +@media (min-width: 1200px) { + .container { + width: 1170px + } +} + +.container-fluid { + margin-right: auto; + margin-left: auto; + padding-left: 15px; + padding-right: 15px +} + +.row { + margin-left: -15px; + margin-right: -15px +} + +.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { + position: relative; + min-height: 1px; + padding-left: 15px; + padding-right: 15px +} + +.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { + float: left +} + +.col-xs-12 { + width: 100% +} + +.col-xs-11 { + width: 91.66666667% +} + +.col-xs-10 { + width: 83.33333333% +} + +.col-xs-9 { + width: 75% +} + +.col-xs-8 { + width: 66.66666667% +} + +.col-xs-7 { + width: 58.33333333% +} + +.col-xs-6 { + width: 50% +} + +.col-xs-5 { + width: 41.66666667% +} + +.col-xs-4 { + width: 33.33333333% +} + +.col-xs-3 { + width: 25% +} + +.col-xs-2 { + width: 16.66666667% +} + +.col-xs-1 { + width: 8.33333333% +} + +.col-xs-pull-12 { + right: 100% +} + +.col-xs-pull-11 { + right: 91.66666667% +} + +.col-xs-pull-10 { + right: 83.33333333% +} + +.col-xs-pull-9 { + right: 75% +} + +.col-xs-pull-8 { + right: 66.66666667% +} + +.col-xs-pull-7 { + right: 58.33333333% +} + +.col-xs-pull-6 { + right: 50% +} + +.col-xs-pull-5 { + right: 41.66666667% +} + +.col-xs-pull-4 { + right: 33.33333333% +} + +.col-xs-pull-3 { + right: 25% +} + +.col-xs-pull-2 { + right: 16.66666667% +} + +.col-xs-pull-1 { + right: 8.33333333% +} + +.col-xs-pull-0 { + right: auto +} + +.col-xs-push-12 { + left: 100% +} + +.col-xs-push-11 { + left: 91.66666667% +} + +.col-xs-push-10 { + left: 83.33333333% +} + +.col-xs-push-9 { + left: 75% +} + +.col-xs-push-8 { + left: 66.66666667% +} + +.col-xs-push-7 { + left: 58.33333333% +} + +.col-xs-push-6 { + left: 50% +} + +.col-xs-push-5 { + left: 41.66666667% +} + +.col-xs-push-4 { + left: 33.33333333% +} + +.col-xs-push-3 { + left: 25% +} + +.col-xs-push-2 { + left: 16.66666667% +} + +.col-xs-push-1 { + left: 8.33333333% +} + +.col-xs-push-0 { + left: auto +} + +.col-xs-offset-12 { + margin-left: 100% +} + +.col-xs-offset-11 { + margin-left: 91.66666667% +} + +.col-xs-offset-10 { + margin-left: 83.33333333% +} + +.col-xs-offset-9 { + margin-left: 75% +} + +.col-xs-offset-8 { + margin-left: 66.66666667% +} + +.col-xs-offset-7 { + margin-left: 58.33333333% +} + +.col-xs-offset-6 { + margin-left: 50% +} + +.col-xs-offset-5 { + margin-left: 41.66666667% +} + +.col-xs-offset-4 { + margin-left: 33.33333333% +} + +.col-xs-offset-3 { + margin-left: 25% +} + +.col-xs-offset-2 { + margin-left: 16.66666667% +} + +.col-xs-offset-1 { + margin-left: 8.33333333% +} + +.col-xs-offset-0 { + margin-left: 0% +} + +@media (min-width: 768px) { + .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { + float: left + } + + .col-sm-12 { + width: 100% + } + + .col-sm-11 { + width: 91.66666667% + } + + .col-sm-10 { + width: 83.33333333% + } + + .col-sm-9 { + width: 75% + } + + .col-sm-8 { + width: 66.66666667% + } + + .col-sm-7 { + width: 58.33333333% + } + + .col-sm-6 { + width: 50% + } + + .col-sm-5 { + width: 41.66666667% + } + + .col-sm-4 { + width: 33.33333333% + } + + .col-sm-3 { + width: 25% + } + + .col-sm-2 { + width: 16.66666667% + } + + .col-sm-1 { + width: 8.33333333% + } + + .col-sm-pull-12 { + right: 100% + } + + .col-sm-pull-11 { + right: 91.66666667% + } + + .col-sm-pull-10 { + right: 83.33333333% + } + + .col-sm-pull-9 { + right: 75% + } + + .col-sm-pull-8 { + right: 66.66666667% + } + + .col-sm-pull-7 { + right: 58.33333333% + } + + .col-sm-pull-6 { + right: 50% + } + + .col-sm-pull-5 { + right: 41.66666667% + } + + .col-sm-pull-4 { + right: 33.33333333% + } + + .col-sm-pull-3 { + right: 25% + } + + .col-sm-pull-2 { + right: 16.66666667% + } + + .col-sm-pull-1 { + right: 8.33333333% + } + + .col-sm-pull-0 { + right: auto + } + + .col-sm-push-12 { + left: 100% + } + + .col-sm-push-11 { + left: 91.66666667% + } + + .col-sm-push-10 { + left: 83.33333333% + } + + .col-sm-push-9 { + left: 75% + } + + .col-sm-push-8 { + left: 66.66666667% + } + + .col-sm-push-7 { + left: 58.33333333% + } + + .col-sm-push-6 { + left: 50% + } + + .col-sm-push-5 { + left: 41.66666667% + } + + .col-sm-push-4 { + left: 33.33333333% + } + + .col-sm-push-3 { + left: 25% + } + + .col-sm-push-2 { + left: 16.66666667% + } + + .col-sm-push-1 { + left: 8.33333333% + } + + .col-sm-push-0 { + left: auto + } + + .col-sm-offset-12 { + margin-left: 100% + } + + .col-sm-offset-11 { + margin-left: 91.66666667% + } + + .col-sm-offset-10 { + margin-left: 83.33333333% + } + + .col-sm-offset-9 { + margin-left: 75% + } + + .col-sm-offset-8 { + margin-left: 66.66666667% + } + + .col-sm-offset-7 { + margin-left: 58.33333333% + } + + .col-sm-offset-6 { + margin-left: 50% + } + + .col-sm-offset-5 { + margin-left: 41.66666667% + } + + .col-sm-offset-4 { + margin-left: 33.33333333% + } + + .col-sm-offset-3 { + margin-left: 25% + } + + .col-sm-offset-2 { + margin-left: 16.66666667% + } + + .col-sm-offset-1 { + margin-left: 8.33333333% + } + + .col-sm-offset-0 { + margin-left: 0% + } +} + +@media (min-width: 992px) { + .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { + float: left + } + + .col-md-12 { + width: 100% + } + + .col-md-11 { + width: 91.66666667% + } + + .col-md-10 { + width: 83.33333333% + } + + .col-md-9 { + width: 75% + } + + .col-md-8 { + width: 66.66666667% + } + + .col-md-7 { + width: 58.33333333% + } + + .col-md-6 { + width: 50% + } + + .col-md-5 { + width: 41.66666667% + } + + .col-md-4 { + width: 33.33333333% + } + + .col-md-3 { + width: 25% + } + + .col-md-2 { + width: 16.66666667% + } + + .col-md-1 { + width: 8.33333333% + } + + .col-md-pull-12 { + right: 100% + } + + .col-md-pull-11 { + right: 91.66666667% + } + + .col-md-pull-10 { + right: 83.33333333% + } + + .col-md-pull-9 { + right: 75% + } + + .col-md-pull-8 { + right: 66.66666667% + } + + .col-md-pull-7 { + right: 58.33333333% + } + + .col-md-pull-6 { + right: 50% + } + + .col-md-pull-5 { + right: 41.66666667% + } + + .col-md-pull-4 { + right: 33.33333333% + } + + .col-md-pull-3 { + right: 25% + } + + .col-md-pull-2 { + right: 16.66666667% + } + + .col-md-pull-1 { + right: 8.33333333% + } + + .col-md-pull-0 { + right: auto + } + + .col-md-push-12 { + left: 100% + } + + .col-md-push-11 { + left: 91.66666667% + } + + .col-md-push-10 { + left: 83.33333333% + } + + .col-md-push-9 { + left: 75% + } + + .col-md-push-8 { + left: 66.66666667% + } + + .col-md-push-7 { + left: 58.33333333% + } + + .col-md-push-6 { + left: 50% + } + + .col-md-push-5 { + left: 41.66666667% + } + + .col-md-push-4 { + left: 33.33333333% + } + + .col-md-push-3 { + left: 25% + } + + .col-md-push-2 { + left: 16.66666667% + } + + .col-md-push-1 { + left: 8.33333333% + } + + .col-md-push-0 { + left: auto + } + + .col-md-offset-12 { + margin-left: 100% + } + + .col-md-offset-11 { + margin-left: 91.66666667% + } + + .col-md-offset-10 { + margin-left: 83.33333333% + } + + .col-md-offset-9 { + margin-left: 75% + } + + .col-md-offset-8 { + margin-left: 66.66666667% + } + + .col-md-offset-7 { + margin-left: 58.33333333% + } + + .col-md-offset-6 { + margin-left: 50% + } + + .col-md-offset-5 { + margin-left: 41.66666667% + } + + .col-md-offset-4 { + margin-left: 33.33333333% + } + + .col-md-offset-3 { + margin-left: 25% + } + + .col-md-offset-2 { + margin-left: 16.66666667% + } + + .col-md-offset-1 { + margin-left: 8.33333333% + } + + .col-md-offset-0 { + margin-left: 0% + } +} + +@media (min-width: 1200px) { + .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { + float: left + } + + .col-lg-12 { + width: 100% + } + + .col-lg-11 { + width: 91.66666667% + } + + .col-lg-10 { + width: 83.33333333% + } + + .col-lg-9 { + width: 75% + } + + .col-lg-8 { + width: 66.66666667% + } + + .col-lg-7 { + width: 58.33333333% + } + + .col-lg-6 { + width: 50% + } + + .col-lg-5 { + width: 41.66666667% + } + + .col-lg-4 { + width: 33.33333333% + } + + .col-lg-3 { + width: 25% + } + + .col-lg-2 { + width: 16.66666667% + } + + .col-lg-1 { + width: 8.33333333% + } + + .col-lg-pull-12 { + right: 100% + } + + .col-lg-pull-11 { + right: 91.66666667% + } + + .col-lg-pull-10 { + right: 83.33333333% + } + + .col-lg-pull-9 { + right: 75% + } + + .col-lg-pull-8 { + right: 66.66666667% + } + + .col-lg-pull-7 { + right: 58.33333333% + } + + .col-lg-pull-6 { + right: 50% + } + + .col-lg-pull-5 { + right: 41.66666667% + } + + .col-lg-pull-4 { + right: 33.33333333% + } + + .col-lg-pull-3 { + right: 25% + } + + .col-lg-pull-2 { + right: 16.66666667% + } + + .col-lg-pull-1 { + right: 8.33333333% + } + + .col-lg-pull-0 { + right: auto + } + + .col-lg-push-12 { + left: 100% + } + + .col-lg-push-11 { + left: 91.66666667% + } + + .col-lg-push-10 { + left: 83.33333333% + } + + .col-lg-push-9 { + left: 75% + } + + .col-lg-push-8 { + left: 66.66666667% + } + + .col-lg-push-7 { + left: 58.33333333% + } + + .col-lg-push-6 { + left: 50% + } + + .col-lg-push-5 { + left: 41.66666667% + } + + .col-lg-push-4 { + left: 33.33333333% + } + + .col-lg-push-3 { + left: 25% + } + + .col-lg-push-2 { + left: 16.66666667% + } + + .col-lg-push-1 { + left: 8.33333333% + } + + .col-lg-push-0 { + left: auto + } + + .col-lg-offset-12 { + margin-left: 100% + } + + .col-lg-offset-11 { + margin-left: 91.66666667% + } + + .col-lg-offset-10 { + margin-left: 83.33333333% + } + + .col-lg-offset-9 { + margin-left: 75% + } + + .col-lg-offset-8 { + margin-left: 66.66666667% + } + + .col-lg-offset-7 { + margin-left: 58.33333333% + } + + .col-lg-offset-6 { + margin-left: 50% + } + + .col-lg-offset-5 { + margin-left: 41.66666667% + } + + .col-lg-offset-4 { + margin-left: 33.33333333% + } + + .col-lg-offset-3 { + margin-left: 25% + } + + .col-lg-offset-2 { + margin-left: 16.66666667% + } + + .col-lg-offset-1 { + margin-left: 8.33333333% + } + + .col-lg-offset-0 { + margin-left: 0% + } +} + +table { + background-color: transparent +} + +caption { + padding-top: 8px; + padding-bottom: 8px; + color: #bbbbbb; + text-align: left +} + +th { + text-align: left +} + +.table { + width: 100%; + max-width: 100%; + margin-bottom: 23px +} + +.table > thead > tr > th, .table > tbody > tr > th, .table > tfoot > tr > th, .table > thead > tr > td, .table > tbody > tr > td, .table > tfoot > tr > td { + padding: 8px; + line-height: 1.846; + vertical-align: top; + border-top: 1px solid #dddddd +} + +.table > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid #dddddd +} + +.table > caption + thead > tr:first-child > th, .table > colgroup + thead > tr:first-child > th, .table > thead:first-child > tr:first-child > th, .table > caption + thead > tr:first-child > td, .table > colgroup + thead > tr:first-child > td, .table > thead:first-child > tr:first-child > td { + border-top: 0 +} + +.table > tbody + tbody { + border-top: 2px solid #dddddd +} + +.table .table { + background-color: #ffffff +} + +.table-condensed > thead > tr > th, .table-condensed > tbody > tr > th, .table-condensed > tfoot > tr > th, .table-condensed > thead > tr > td, .table-condensed > tbody > tr > td, .table-condensed > tfoot > tr > td { + padding: 5px +} + +.table-bordered { + border: 1px solid #dddddd +} + +.table-bordered > thead > tr > th, .table-bordered > tbody > tr > th, .table-bordered > tfoot > tr > th, .table-bordered > thead > tr > td, .table-bordered > tbody > tr > td, .table-bordered > tfoot > tr > td { + border: 1px solid #dddddd +} + +.table-bordered > thead > tr > th, .table-bordered > thead > tr > td { + border-bottom-width: 2px +} + +.table-striped > tbody > tr:nth-of-type(odd) { + background-color: #f9f9f9 +} + +.table-hover > tbody > tr:hover { + background-color: #f5f5f5 +} + +table col[class*="col-"] { + position: static; + float: none; + display: table-column +} + +table td[class*="col-"], table th[class*="col-"] { + position: static; + float: none; + display: table-cell +} + +.table > thead > tr > td.active, .table > tbody > tr > td.active, .table > tfoot > tr > td.active, .table > thead > tr > th.active, .table > tbody > tr > th.active, .table > tfoot > tr > th.active, .table > thead > tr.active > td, .table > tbody > tr.active > td, .table > tfoot > tr.active > td, .table > thead > tr.active > th, .table > tbody > tr.active > th, .table > tfoot > tr.active > th { + background-color: #f5f5f5 +} + +.table-hover > tbody > tr > td.active:hover, .table-hover > tbody > tr > th.active:hover, .table-hover > tbody > tr.active:hover > td, .table-hover > tbody > tr:hover > .active, .table-hover > tbody > tr.active:hover > th { + background-color: #e8e8e8 +} + +.table > thead > tr > td.success, .table > tbody > tr > td.success, .table > tfoot > tr > td.success, .table > thead > tr > th.success, .table > tbody > tr > th.success, .table > tfoot > tr > th.success, .table > thead > tr.success > td, .table > tbody > tr.success > td, .table > tfoot > tr.success > td, .table > thead > tr.success > th, .table > tbody > tr.success > th, .table > tfoot > tr.success > th { + background-color: #dff0d8 +} + +.table-hover > tbody > tr > td.success:hover, .table-hover > tbody > tr > th.success:hover, .table-hover > tbody > tr.success:hover > td, .table-hover > tbody > tr:hover > .success, .table-hover > tbody > tr.success:hover > th { + background-color: #d0e9c6 +} + +.table > thead > tr > td.info, .table > tbody > tr > td.info, .table > tfoot > tr > td.info, .table > thead > tr > th.info, .table > tbody > tr > th.info, .table > tfoot > tr > th.info, .table > thead > tr.info > td, .table > tbody > tr.info > td, .table > tfoot > tr.info > td, .table > thead > tr.info > th, .table > tbody > tr.info > th, .table > tfoot > tr.info > th { + background-color: #e1bee7 +} + +.table-hover > tbody > tr > td.info:hover, .table-hover > tbody > tr > th.info:hover, .table-hover > tbody > tr.info:hover > td, .table-hover > tbody > tr:hover > .info, .table-hover > tbody > tr.info:hover > th { + background-color: #d8abe0 +} + +.table > thead > tr > td.warning, .table > tbody > tr > td.warning, .table > tfoot > tr > td.warning, .table > thead > tr > th.warning, .table > tbody > tr > th.warning, .table > tfoot > tr > th.warning, .table > thead > tr.warning > td, .table > tbody > tr.warning > td, .table > tfoot > tr.warning > td, .table > thead > tr.warning > th, .table > tbody > tr.warning > th, .table > tfoot > tr.warning > th { + background-color: #ffe0b2 +} + +.table-hover > tbody > tr > td.warning:hover, .table-hover > tbody > tr > th.warning:hover, .table-hover > tbody > tr.warning:hover > td, .table-hover > tbody > tr:hover > .warning, .table-hover > tbody > tr.warning:hover > th { + background-color: #ffd699 +} + +.table > thead > tr > td.danger, .table > tbody > tr > td.danger, .table > tfoot > tr > td.danger, .table > thead > tr > th.danger, .table > tbody > tr > th.danger, .table > tfoot > tr > th.danger, .table > thead > tr.danger > td, .table > tbody > tr.danger > td, .table > tfoot > tr.danger > td, .table > thead > tr.danger > th, .table > tbody > tr.danger > th, .table > tfoot > tr.danger > th { + background-color: #f9bdbb +} + +.table-hover > tbody > tr > td.danger:hover, .table-hover > tbody > tr > th.danger:hover, .table-hover > tbody > tr.danger:hover > td, .table-hover > tbody > tr:hover > .danger, .table-hover > tbody > tr.danger:hover > th { + background-color: #f7a6a4 +} + +.table-responsive { + overflow-x: auto; + min-height: 0.01% +} + +@media screen and (max-width: 767px) { + .table-responsive { + width: 100%; + margin-bottom: 17.25px; + overflow-y: hidden; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 1px solid #dddddd + } + + .table-responsive > .table { + margin-bottom: 0 + } + + .table-responsive > .table > thead > tr > th, .table-responsive > .table > tbody > tr > th, .table-responsive > .table > tfoot > tr > th, .table-responsive > .table > thead > tr > td, .table-responsive > .table > tbody > tr > td, .table-responsive > .table > tfoot > tr > td { + white-space: nowrap + } + + .table-responsive > .table-bordered { + border: 0 + } + + .table-responsive > .table-bordered > thead > tr > th:first-child, .table-responsive > .table-bordered > tbody > tr > th:first-child, .table-responsive > .table-bordered > tfoot > tr > th:first-child, .table-responsive > .table-bordered > thead > tr > td:first-child, .table-responsive > .table-bordered > tbody > tr > td:first-child, .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0 + } + + .table-responsive > .table-bordered > thead > tr > th:last-child, .table-responsive > .table-bordered > tbody > tr > th:last-child, .table-responsive > .table-bordered > tfoot > tr > th:last-child, .table-responsive > .table-bordered > thead > tr > td:last-child, .table-responsive > .table-bordered > tbody > tr > td:last-child, .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0 + } + + .table-responsive > .table-bordered > tbody > tr:last-child > th, .table-responsive > .table-bordered > tfoot > tr:last-child > th, .table-responsive > .table-bordered > tbody > tr:last-child > td, .table-responsive > .table-bordered > tfoot > tr:last-child > td { + border-bottom: 0 + } +} + +fieldset { + padding: 0; + margin: 0; + border: 0; + min-width: 0 +} + +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 23px; + font-size: 19.5px; + line-height: inherit; + color: #212121; + border: 0; + border-bottom: 1px solid #e5e5e5 +} + +label { + display: inline-block; + max-width: 100%; + margin-bottom: 5px; + font-weight: bold +} + +input[type="search"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box +} + +input[type="radio"], input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + line-height: normal +} + +input[type="file"] { + display: block +} + +input[type="range"] { + display: block; + width: 100% +} + +select[multiple], select[size] { + height: auto +} + +input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px +} + +output { + display: block; + padding-top: 7px; + font-size: 13px; + line-height: 1.846; + color: #666666 +} + +.form-control { + display: block; + width: 100%; + height: 37px; + padding: 6px 16px; + font-size: 13px; + line-height: 1.846; + color: #666666; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 3px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s +} + +.form-control:focus { + border-color: #66afe9; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6) +} + +.form-control::-moz-placeholder { + color: #bbbbbb; + opacity: 1 +} + +.form-control:-ms-input-placeholder { + color: #bbbbbb +} + +.form-control::-webkit-input-placeholder { + color: #bbbbbb +} + +.form-control::-ms-expand { + border: 0; + background-color: transparent +} + +.form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { + background-color: transparent; + opacity: 1 +} + +.form-control[disabled], fieldset[disabled] .form-control { + cursor: not-allowed +} + +textarea.form-control { + height: auto +} + +input[type="search"] { + -webkit-appearance: none +} + +@media screen and (-webkit-min-device-pixel-ratio: 0) { + input[type="date"].form-control, input[type="time"].form-control, input[type="datetime-local"].form-control, input[type="month"].form-control { + line-height: 37px + } + + input[type="date"].input-sm, input[type="time"].input-sm, input[type="datetime-local"].input-sm, input[type="month"].input-sm, .input-group-sm input[type="date"], .input-group-sm input[type="time"], .input-group-sm input[type="datetime-local"], .input-group-sm input[type="month"] { + line-height: 30px + } + + input[type="date"].input-lg, input[type="time"].input-lg, input[type="datetime-local"].input-lg, input[type="month"].input-lg, .input-group-lg input[type="date"], .input-group-lg input[type="time"], .input-group-lg input[type="datetime-local"], .input-group-lg input[type="month"] { + line-height: 45px + } +} + +.form-group { + margin-bottom: 15px +} + +.radio, .checkbox { + position: relative; + display: block; + margin-top: 10px; + margin-bottom: 10px +} + +.radio label, .checkbox label { + min-height: 23px; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + cursor: pointer +} + +.radio input[type="radio"], .radio-inline input[type="radio"], .checkbox input[type="checkbox"], .checkbox-inline input[type="checkbox"] { + position: absolute; + margin-left: -20px; + margin-top: 4px \9 +} + +.radio + .radio, .checkbox + .checkbox { + margin-top: -5px +} + +.radio-inline, .checkbox-inline { + position: relative; + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + vertical-align: middle; + font-weight: normal; + cursor: pointer +} + +.radio-inline + .radio-inline, .checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px +} + +input[type="radio"][disabled], input[type="checkbox"][disabled], input[type="radio"].disabled, input[type="checkbox"].disabled, fieldset[disabled] input[type="radio"], fieldset[disabled] input[type="checkbox"] { + cursor: not-allowed +} + +.radio-inline.disabled, .checkbox-inline.disabled, fieldset[disabled] .radio-inline, fieldset[disabled] .checkbox-inline { + cursor: not-allowed +} + +.radio.disabled label, .checkbox.disabled label, fieldset[disabled] .radio label, fieldset[disabled] .checkbox label { + cursor: not-allowed +} + +.form-control-static { + padding-top: 7px; + padding-bottom: 7px; + margin-bottom: 0; + min-height: 36px +} + +.form-control-static.input-lg, .form-control-static.input-sm { + padding-left: 0; + padding-right: 0 +} + +.input-sm { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px +} + +select.input-sm { + height: 30px; + line-height: 30px +} + +textarea.input-sm, select[multiple].input-sm { + height: auto +} + +.form-group-sm .form-control { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px +} + +.form-group-sm select.form-control { + height: 30px; + line-height: 30px +} + +.form-group-sm textarea.form-control, .form-group-sm select[multiple].form-control { + height: auto +} + +.form-group-sm .form-control-static { + height: 30px; + min-height: 35px; + padding: 6px 10px; + font-size: 12px; + line-height: 1.5 +} + +.input-lg { + height: 45px; + padding: 10px 16px; + font-size: 17px; + line-height: 1.3333333; + border-radius: 3px +} + +select.input-lg { + height: 45px; + line-height: 45px +} + +textarea.input-lg, select[multiple].input-lg { + height: auto +} + +.form-group-lg .form-control { + height: 45px; + padding: 10px 16px; + font-size: 17px; + line-height: 1.3333333; + border-radius: 3px +} + +.form-group-lg select.form-control { + height: 45px; + line-height: 45px +} + +.form-group-lg textarea.form-control, .form-group-lg select[multiple].form-control { + height: auto +} + +.form-group-lg .form-control-static { + height: 45px; + min-height: 40px; + padding: 11px 16px; + font-size: 17px; + line-height: 1.3333333 +} + +.has-feedback { + position: relative +} + +.has-feedback .form-control { + padding-right: 46.25px +} + +.form-control-feedback { + position: absolute; + top: 0; + right: 0; + z-index: 2; + display: block; + width: 37px; + height: 37px; + line-height: 37px; + text-align: center; + pointer-events: none +} + +.input-lg + .form-control-feedback, .input-group-lg + .form-control-feedback, .form-group-lg .form-control + .form-control-feedback { + width: 45px; + height: 45px; + line-height: 45px +} + +.input-sm + .form-control-feedback, .input-group-sm + .form-control-feedback, .form-group-sm .form-control + .form-control-feedback { + width: 30px; + height: 30px; + line-height: 30px +} + +.has-success .help-block, .has-success .control-label, .has-success .radio, .has-success .checkbox, .has-success .radio-inline, .has-success .checkbox-inline, .has-success.radio label, .has-success.checkbox label, .has-success.radio-inline label, .has-success.checkbox-inline label { + color: #4caf50 +} + +.has-success .form-control { + border-color: #4caf50; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075) +} + +.has-success .form-control:focus { + border-color: #3d8b40; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #92cf94; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #92cf94 +} + +.has-success .input-group-addon { + color: #4caf50; + border-color: #4caf50; + background-color: #dff0d8 +} + +.has-success .form-control-feedback { + color: #4caf50 +} + +.has-warning .help-block, .has-warning .control-label, .has-warning .radio, .has-warning .checkbox, .has-warning .radio-inline, .has-warning .checkbox-inline, .has-warning.radio label, .has-warning.checkbox label, .has-warning.radio-inline label, .has-warning.checkbox-inline label { + color: #ff9800 +} + +.has-warning .form-control { + border-color: #ff9800; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075) +} + +.has-warning .form-control:focus { + border-color: #cc7a00; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffc166; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffc166 +} + +.has-warning .input-group-addon { + color: #ff9800; + border-color: #ff9800; + background-color: #ffe0b2 +} + +.has-warning .form-control-feedback { + color: #ff9800 +} + +.has-error .help-block, .has-error .control-label, .has-error .radio, .has-error .checkbox, .has-error .radio-inline, .has-error .checkbox-inline, .has-error.radio label, .has-error.checkbox label, .has-error.radio-inline label, .has-error.checkbox-inline label { + color: #e51c23 +} + +.has-error .form-control { + border-color: #e51c23; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075) +} + +.has-error .form-control:focus { + border-color: #b9151b; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ef787c; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ef787c +} + +.has-error .input-group-addon { + color: #e51c23; + border-color: #e51c23; + background-color: #f9bdbb +} + +.has-error .form-control-feedback { + color: #e51c23 +} + +.has-feedback label ~ .form-control-feedback { + top: 28px +} + +.has-feedback label.sr-only ~ .form-control-feedback { + top: 0 +} + +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #a6a6a6 +} + +@media (min-width: 768px) { + .form-inline .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle + } + + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle + } + + .form-inline .form-control-static { + display: inline-block + } + + .form-inline .input-group { + display: inline-table; + vertical-align: middle + } + + .form-inline .input-group .input-group-addon, .form-inline .input-group .input-group-btn, .form-inline .input-group .form-control { + width: auto + } + + .form-inline .input-group > .form-control { + width: 100% + } + + .form-inline .control-label { + margin-bottom: 0; + vertical-align: middle + } + + .form-inline .radio, .form-inline .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle + } + + .form-inline .radio label, .form-inline .checkbox label { + padding-left: 0 + } + + .form-inline .radio input[type="radio"], .form-inline .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0 + } + + .form-inline .has-feedback .form-control-feedback { + top: 0 + } +} + +.form-horizontal .radio, .form-horizontal .checkbox, .form-horizontal .radio-inline, .form-horizontal .checkbox-inline { + margin-top: 0; + margin-bottom: 0; + padding-top: 7px +} + +.form-horizontal .radio, .form-horizontal .checkbox { + min-height: 30px +} + +.form-horizontal .form-group { + margin-left: -15px; + margin-right: -15px +} + +@media (min-width: 768px) { + .form-horizontal .control-label { + text-align: right; + margin-bottom: 0; + padding-top: 7px + } +} + +.form-horizontal .has-feedback .form-control-feedback { + right: 15px +} + +@media (min-width: 768px) { + .form-horizontal .form-group-lg .control-label { + padding-top: 11px; + font-size: 17px + } +} + +@media (min-width: 768px) { + .form-horizontal .form-group-sm .control-label { + padding-top: 6px; + font-size: 12px + } +} + +.btn { + display: inline-block; + margin-bottom: 0; + font-weight: normal; + text-align: center; + vertical-align: middle; + -ms-touch-action: manipulation; + touch-action: manipulation; + cursor: pointer; + background-image: none; + border: 1px solid transparent; + white-space: nowrap; + padding: 6px 16px; + font-size: 13px; + line-height: 1.846; + border-radius: 3px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none +} + +.btn:focus, .btn:active:focus, .btn.active:focus, .btn.focus, .btn:active.focus, .btn.active.focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px +} + +.btn:hover, .btn:focus, .btn.focus { + color: #444444; + text-decoration: none +} + +.btn:active, .btn.active { + outline: 0; + background-image: none; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125) +} + +.btn.disabled, .btn[disabled], fieldset[disabled] .btn { + cursor: not-allowed; + opacity: 0.65; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + box-shadow: none +} + +a.btn.disabled, fieldset[disabled] a.btn { + pointer-events: none +} + +.btn-default { + color: #444444; + background-color: #ffffff; + border-color: transparent +} + +.btn-default:focus, .btn-default.focus { + color: #444444; + background-color: #e6e6e6; + border-color: rgba(0, 0, 0, 0) +} + +.btn-default:hover { + color: #444444; + background-color: #e6e6e6; + border-color: rgba(0, 0, 0, 0) +} + +.btn-default:active, .btn-default.active, .open > .dropdown-toggle.btn-default { + color: #444444; + background-color: #e6e6e6; + border-color: rgba(0, 0, 0, 0) +} + +.btn-default:active:hover, .btn-default.active:hover, .open > .dropdown-toggle.btn-default:hover, .btn-default:active:focus, .btn-default.active:focus, .open > .dropdown-toggle.btn-default:focus, .btn-default:active.focus, .btn-default.active.focus, .open > .dropdown-toggle.btn-default.focus { + color: #444444; + background-color: #d4d4d4; + border-color: rgba(0, 0, 0, 0) +} + +.btn-default:active, .btn-default.active, .open > .dropdown-toggle.btn-default { + background-image: none +} + +.btn-default.disabled:hover, .btn-default[disabled]:hover, fieldset[disabled] .btn-default:hover, .btn-default.disabled:focus, .btn-default[disabled]:focus, fieldset[disabled] .btn-default:focus, .btn-default.disabled.focus, .btn-default[disabled].focus, fieldset[disabled] .btn-default.focus { + background-color: #ffffff; + border-color: transparent +} + +.btn-default .badge { + color: #ffffff; + background-color: #444444 +} + +.btn-primary { + color: #ffffff; + background-color: #2196f3; + border-color: transparent +} + +.btn-primary:focus, .btn-primary.focus { + color: #ffffff; + background-color: #0c7cd5; + border-color: rgba(0, 0, 0, 0) +} + +.btn-primary:hover { + color: #ffffff; + background-color: #0c7cd5; + border-color: rgba(0, 0, 0, 0) +} + +.btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary { + color: #ffffff; + background-color: #0c7cd5; + border-color: rgba(0, 0, 0, 0) +} + +.btn-primary:active:hover, .btn-primary.active:hover, .open > .dropdown-toggle.btn-primary:hover, .btn-primary:active:focus, .btn-primary.active:focus, .open > .dropdown-toggle.btn-primary:focus, .btn-primary:active.focus, .btn-primary.active.focus, .open > .dropdown-toggle.btn-primary.focus { + color: #ffffff; + background-color: #0a68b4; + border-color: rgba(0, 0, 0, 0) +} + +.btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary { + background-image: none +} + +.btn-primary.disabled:hover, .btn-primary[disabled]:hover, fieldset[disabled] .btn-primary:hover, .btn-primary.disabled:focus, .btn-primary[disabled]:focus, fieldset[disabled] .btn-primary:focus, .btn-primary.disabled.focus, .btn-primary[disabled].focus, fieldset[disabled] .btn-primary.focus { + background-color: #2196f3; + border-color: transparent +} + +.btn-primary .badge { + color: #2196f3; + background-color: #ffffff +} + +.btn-success { + color: #ffffff; + background-color: #4caf50; + border-color: transparent +} + +.btn-success:focus, .btn-success.focus { + color: #ffffff; + background-color: #3d8b40; + border-color: rgba(0, 0, 0, 0) +} + +.btn-success:hover { + color: #ffffff; + background-color: #3d8b40; + border-color: rgba(0, 0, 0, 0) +} + +.btn-success:active, .btn-success.active, .open > .dropdown-toggle.btn-success { + color: #ffffff; + background-color: #3d8b40; + border-color: rgba(0, 0, 0, 0) +} + +.btn-success:active:hover, .btn-success.active:hover, .open > .dropdown-toggle.btn-success:hover, .btn-success:active:focus, .btn-success.active:focus, .open > .dropdown-toggle.btn-success:focus, .btn-success:active.focus, .btn-success.active.focus, .open > .dropdown-toggle.btn-success.focus { + color: #ffffff; + background-color: #327334; + border-color: rgba(0, 0, 0, 0) +} + +.btn-success:active, .btn-success.active, .open > .dropdown-toggle.btn-success { + background-image: none +} + +.btn-success.disabled:hover, .btn-success[disabled]:hover, fieldset[disabled] .btn-success:hover, .btn-success.disabled:focus, .btn-success[disabled]:focus, fieldset[disabled] .btn-success:focus, .btn-success.disabled.focus, .btn-success[disabled].focus, fieldset[disabled] .btn-success.focus { + background-color: #4caf50; + border-color: transparent +} + +.btn-success .badge { + color: #4caf50; + background-color: #ffffff +} + +.btn-info { + color: #ffffff; + background-color: #9c27b0; + border-color: transparent +} + +.btn-info:focus, .btn-info.focus { + color: #ffffff; + background-color: #771e86; + border-color: rgba(0, 0, 0, 0) +} + +.btn-info:hover { + color: #ffffff; + background-color: #771e86; + border-color: rgba(0, 0, 0, 0) +} + +.btn-info:active, .btn-info.active, .open > .dropdown-toggle.btn-info { + color: #ffffff; + background-color: #771e86; + border-color: rgba(0, 0, 0, 0) +} + +.btn-info:active:hover, .btn-info.active:hover, .open > .dropdown-toggle.btn-info:hover, .btn-info:active:focus, .btn-info.active:focus, .open > .dropdown-toggle.btn-info:focus, .btn-info:active.focus, .btn-info.active.focus, .open > .dropdown-toggle.btn-info.focus { + color: #ffffff; + background-color: #5d1769; + border-color: rgba(0, 0, 0, 0) +} + +.btn-info:active, .btn-info.active, .open > .dropdown-toggle.btn-info { + background-image: none +} + +.btn-info.disabled:hover, .btn-info[disabled]:hover, fieldset[disabled] .btn-info:hover, .btn-info.disabled:focus, .btn-info[disabled]:focus, fieldset[disabled] .btn-info:focus, .btn-info.disabled.focus, .btn-info[disabled].focus, fieldset[disabled] .btn-info.focus { + background-color: #9c27b0; + border-color: transparent +} + +.btn-info .badge { + color: #9c27b0; + background-color: #ffffff +} + +.btn-warning { + color: #ffffff; + background-color: #ff9800; + border-color: transparent +} + +.btn-warning:focus, .btn-warning.focus { + color: #ffffff; + background-color: #cc7a00; + border-color: rgba(0, 0, 0, 0) +} + +.btn-warning:hover { + color: #ffffff; + background-color: #cc7a00; + border-color: rgba(0, 0, 0, 0) +} + +.btn-warning:active, .btn-warning.active, .open > .dropdown-toggle.btn-warning { + color: #ffffff; + background-color: #cc7a00; + border-color: rgba(0, 0, 0, 0) +} + +.btn-warning:active:hover, .btn-warning.active:hover, .open > .dropdown-toggle.btn-warning:hover, .btn-warning:active:focus, .btn-warning.active:focus, .open > .dropdown-toggle.btn-warning:focus, .btn-warning:active.focus, .btn-warning.active.focus, .open > .dropdown-toggle.btn-warning.focus { + color: #ffffff; + background-color: #a86400; + border-color: rgba(0, 0, 0, 0) +} + +.btn-warning:active, .btn-warning.active, .open > .dropdown-toggle.btn-warning { + background-image: none +} + +.btn-warning.disabled:hover, .btn-warning[disabled]:hover, fieldset[disabled] .btn-warning:hover, .btn-warning.disabled:focus, .btn-warning[disabled]:focus, fieldset[disabled] .btn-warning:focus, .btn-warning.disabled.focus, .btn-warning[disabled].focus, fieldset[disabled] .btn-warning.focus { + background-color: #ff9800; + border-color: transparent +} + +.btn-warning .badge { + color: #ff9800; + background-color: #ffffff +} + +.btn-danger { + color: #ffffff; + background-color: #e51c23; + border-color: transparent +} + +.btn-danger:focus, .btn-danger.focus { + color: #ffffff; + background-color: #b9151b; + border-color: rgba(0, 0, 0, 0) +} + +.btn-danger:hover { + color: #ffffff; + background-color: #b9151b; + border-color: rgba(0, 0, 0, 0) +} + +.btn-danger:active, .btn-danger.active, .open > .dropdown-toggle.btn-danger { + color: #ffffff; + background-color: #b9151b; + border-color: rgba(0, 0, 0, 0) +} + +.btn-danger:active:hover, .btn-danger.active:hover, .open > .dropdown-toggle.btn-danger:hover, .btn-danger:active:focus, .btn-danger.active:focus, .open > .dropdown-toggle.btn-danger:focus, .btn-danger:active.focus, .btn-danger.active.focus, .open > .dropdown-toggle.btn-danger.focus { + color: #ffffff; + background-color: #991216; + border-color: rgba(0, 0, 0, 0) +} + +.btn-danger:active, .btn-danger.active, .open > .dropdown-toggle.btn-danger { + background-image: none +} + +.btn-danger.disabled:hover, .btn-danger[disabled]:hover, fieldset[disabled] .btn-danger:hover, .btn-danger.disabled:focus, .btn-danger[disabled]:focus, fieldset[disabled] .btn-danger:focus, .btn-danger.disabled.focus, .btn-danger[disabled].focus, fieldset[disabled] .btn-danger.focus { + background-color: #e51c23; + border-color: transparent +} + +.btn-danger .badge { + color: #e51c23; + background-color: #ffffff +} + +.btn-link { + color: #2196f3; + font-weight: normal; + border-radius: 0 +} + +.btn-link, .btn-link:active, .btn-link.active, .btn-link[disabled], fieldset[disabled] .btn-link { + background-color: transparent; + -webkit-box-shadow: none; + box-shadow: none +} + +.btn-link, .btn-link:hover, .btn-link:focus, .btn-link:active { + border-color: transparent +} + +.btn-link:hover, .btn-link:focus { + color: #0a6ebd; + text-decoration: underline; + background-color: transparent +} + +.btn-link[disabled]:hover, fieldset[disabled] .btn-link:hover, .btn-link[disabled]:focus, fieldset[disabled] .btn-link:focus { + color: #bbbbbb; + text-decoration: none +} + +.btn-lg, .btn-group-lg > .btn { + padding: 10px 16px; + font-size: 17px; + line-height: 1.3333333; + border-radius: 3px +} + +.btn-sm, .btn-group-sm > .btn { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px +} + +.btn-xs, .btn-group-xs > .btn { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px +} + +.btn-block { + display: block; + width: 100% +} + +.btn-block + .btn-block { + margin-top: 5px +} + +input[type="submit"].btn-block, input[type="reset"].btn-block, input[type="button"].btn-block { + width: 100% +} + +.fade { + opacity: 0; + -webkit-transition: opacity 0.15s linear; + -o-transition: opacity 0.15s linear; + transition: opacity 0.15s linear +} + +.fade.in { + opacity: 1 +} + +.collapse { + display: none +} + +.collapse.in { + display: block +} + +tr.collapse.in { + display: table-row +} + +tbody.collapse.in { + display: table-row-group +} + +.collapsing { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition-property: height, visibility; + -o-transition-property: height, visibility; + transition-property: height, visibility; + -webkit-transition-duration: 0.35s; + -o-transition-duration: 0.35s; + transition-duration: 0.35s; + -webkit-transition-timing-function: ease; + -o-transition-timing-function: ease; + transition-timing-function: ease +} + +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px dashed; + border-top: 4px solid \9; + border-right: 4px solid transparent; + border-left: 4px solid transparent +} + +.dropup, .dropdown { + position: relative +} + +.dropdown-toggle:focus { + outline: 0 +} + +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + list-style: none; + font-size: 13px; + text-align: left; + background-color: #ffffff; + border: 1px solid #cccccc; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 3px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + -webkit-background-clip: padding-box; + background-clip: padding-box +} + +.dropdown-menu.pull-right { + right: 0; + left: auto +} + +.dropdown-menu .divider { + height: 1px; + margin: 10.5px 0; + overflow: hidden; + background-color: #e5e5e5 +} + +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 1.846; + color: #666666; + white-space: nowrap +} + +.dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus { + text-decoration: none; + color: #141414; + background-color: #eeeeee +} + +.dropdown-menu > .active > a, .dropdown-menu > .active > a:hover, .dropdown-menu > .active > a:focus { + color: #ffffff; + text-decoration: none; + outline: 0; + background-color: #2196f3 +} + +.dropdown-menu > .disabled > a, .dropdown-menu > .disabled > a:hover, .dropdown-menu > .disabled > a:focus { + color: #bbbbbb +} + +.dropdown-menu > .disabled > a:hover, .dropdown-menu > .disabled > a:focus { + text-decoration: none; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + cursor: not-allowed +} + +.open > .dropdown-menu { + display: block +} + +.open > a { + outline: 0 +} + +.dropdown-menu-right { + left: auto; + right: 0 +} + +.dropdown-menu-left { + left: 0; + right: auto +} + +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: 12px; + line-height: 1.846; + color: #bbbbbb; + white-space: nowrap +} + +.dropdown-backdrop { + position: fixed; + left: 0; + right: 0; + bottom: 0; + top: 0; + z-index: 990 +} + +.pull-right > .dropdown-menu { + right: 0; + left: auto +} + +.dropup .caret, .navbar-fixed-bottom .dropdown .caret { + border-top: 0; + border-bottom: 4px dashed; + border-bottom: 4px solid \9; + content: "" +} + +.dropup .dropdown-menu, .navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 2px +} + +@media (min-width: 768px) { + .navbar-right .dropdown-menu { + left: auto; + right: 0 + } + + .navbar-right .dropdown-menu-left { + left: 0; + right: auto + } +} + +.btn-group, .btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle +} + +.btn-group > .btn, .btn-group-vertical > .btn { + position: relative; + float: left +} + +.btn-group > .btn:hover, .btn-group-vertical > .btn:hover, .btn-group > .btn:focus, .btn-group-vertical > .btn:focus, .btn-group > .btn:active, .btn-group-vertical > .btn:active, .btn-group > .btn.active, .btn-group-vertical > .btn.active { + z-index: 2 +} + +.btn-group .btn + .btn, .btn-group .btn + .btn-group, .btn-group .btn-group + .btn, .btn-group .btn-group + .btn-group { + margin-left: -1px +} + +.btn-toolbar { + margin-left: -5px +} + +.btn-toolbar .btn, .btn-toolbar .btn-group, .btn-toolbar .input-group { + float: left +} + +.btn-toolbar > .btn, .btn-toolbar > .btn-group, .btn-toolbar > .input-group { + margin-left: 5px +} + +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0 +} + +.btn-group > .btn:first-child { + margin-left: 0 +} + +.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { + border-bottom-right-radius: 0; + border-top-right-radius: 0 +} + +.btn-group > .btn:last-child:not(:first-child), .btn-group > .dropdown-toggle:not(:first-child) { + border-bottom-left-radius: 0; + border-top-left-radius: 0 +} + +.btn-group > .btn-group { + float: left +} + +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0 +} + +.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child, .btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-bottom-right-radius: 0; + border-top-right-radius: 0 +} + +.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-bottom-left-radius: 0; + border-top-left-radius: 0 +} + +.btn-group .dropdown-toggle:active, .btn-group.open .dropdown-toggle { + outline: 0 +} + +.btn-group > .btn + .dropdown-toggle { + padding-left: 8px; + padding-right: 8px +} + +.btn-group > .btn-lg + .dropdown-toggle { + padding-left: 12px; + padding-right: 12px +} + +.btn-group.open .dropdown-toggle { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125) +} + +.btn-group.open .dropdown-toggle.btn-link { + -webkit-box-shadow: none; + box-shadow: none +} + +.btn .caret { + margin-left: 0 +} + +.btn-lg .caret { + border-width: 5px 5px 0; + border-bottom-width: 0 +} + +.dropup .btn-lg .caret { + border-width: 0 5px 5px +} + +.btn-group-vertical > .btn, .btn-group-vertical > .btn-group, .btn-group-vertical > .btn-group > .btn { + display: block; + float: none; + width: 100%; + max-width: 100% +} + +.btn-group-vertical > .btn-group > .btn { + float: none +} + +.btn-group-vertical > .btn + .btn, .btn-group-vertical > .btn + .btn-group, .btn-group-vertical > .btn-group + .btn, .btn-group-vertical > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0 +} + +.btn-group-vertical > .btn:not(:first-child):not(:last-child) { + border-radius: 0 +} + +.btn-group-vertical > .btn:first-child:not(:last-child) { + border-top-right-radius: 3px; + border-top-left-radius: 3px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0 +} + +.btn-group-vertical > .btn:last-child:not(:first-child) { + border-top-right-radius: 0; + border-top-left-radius: 0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px +} + +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0 +} + +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, .btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0 +} + +.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-right-radius: 0; + border-top-left-radius: 0 +} + +.btn-group-justified { + display: table; + width: 100%; + table-layout: fixed; + border-collapse: separate +} + +.btn-group-justified > .btn, .btn-group-justified > .btn-group { + float: none; + display: table-cell; + width: 1% +} + +.btn-group-justified > .btn-group .btn { + width: 100% +} + +.btn-group-justified > .btn-group .dropdown-menu { + left: auto +} + +[data-toggle="buttons"] > .btn input[type="radio"], [data-toggle="buttons"] > .btn-group > .btn input[type="radio"], [data-toggle="buttons"] > .btn input[type="checkbox"], [data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none +} + +.input-group { + position: relative; + display: table; + border-collapse: separate +} + +.input-group[class*="col-"] { + float: none; + padding-left: 0; + padding-right: 0 +} + +.input-group .form-control { + position: relative; + z-index: 2; + float: left; + width: 100%; + margin-bottom: 0 +} + +.input-group .form-control:focus { + z-index: 3 +} + +.input-group-lg > .form-control, .input-group-lg > .input-group-addon, .input-group-lg > .input-group-btn > .btn { + height: 45px; + padding: 10px 16px; + font-size: 17px; + line-height: 1.3333333; + border-radius: 3px +} + +select.input-group-lg > .form-control, select.input-group-lg > .input-group-addon, select.input-group-lg > .input-group-btn > .btn { + height: 45px; + line-height: 45px +} + +textarea.input-group-lg > .form-control, textarea.input-group-lg > .input-group-addon, textarea.input-group-lg > .input-group-btn > .btn, select[multiple].input-group-lg > .form-control, select[multiple].input-group-lg > .input-group-addon, select[multiple].input-group-lg > .input-group-btn > .btn { + height: auto +} + +.input-group-sm > .form-control, .input-group-sm > .input-group-addon, .input-group-sm > .input-group-btn > .btn { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px +} + +select.input-group-sm > .form-control, select.input-group-sm > .input-group-addon, select.input-group-sm > .input-group-btn > .btn { + height: 30px; + line-height: 30px +} + +textarea.input-group-sm > .form-control, textarea.input-group-sm > .input-group-addon, textarea.input-group-sm > .input-group-btn > .btn, select[multiple].input-group-sm > .form-control, select[multiple].input-group-sm > .input-group-addon, select[multiple].input-group-sm > .input-group-btn > .btn { + height: auto +} + +.input-group-addon, .input-group-btn, .input-group .form-control { + display: table-cell +} + +.input-group-addon:not(:first-child):not(:last-child), .input-group-btn:not(:first-child):not(:last-child), .input-group .form-control:not(:first-child):not(:last-child) { + border-radius: 0 +} + +.input-group-addon, .input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle +} + +.input-group-addon { + padding: 6px 16px; + font-size: 13px; + font-weight: normal; + line-height: 1; + color: #666666; + text-align: center; + background-color: transparent; + border: 1px solid transparent; + border-radius: 3px +} + +.input-group-addon.input-sm { + padding: 5px 10px; + font-size: 12px; + border-radius: 3px +} + +.input-group-addon.input-lg { + padding: 10px 16px; + font-size: 17px; + border-radius: 3px +} + +.input-group-addon input[type="radio"], .input-group-addon input[type="checkbox"] { + margin-top: 0 +} + +.input-group .form-control:first-child, .input-group-addon:first-child, .input-group-btn:first-child > .btn, .input-group-btn:first-child > .btn-group > .btn, .input-group-btn:first-child > .dropdown-toggle, .input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), .input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + border-bottom-right-radius: 0; + border-top-right-radius: 0 +} + +.input-group-addon:first-child { + border-right: 0 +} + +.input-group .form-control:last-child, .input-group-addon:last-child, .input-group-btn:last-child > .btn, .input-group-btn:last-child > .btn-group > .btn, .input-group-btn:last-child > .dropdown-toggle, .input-group-btn:first-child > .btn:not(:first-child), .input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + border-bottom-left-radius: 0; + border-top-left-radius: 0 +} + +.input-group-addon:last-child { + border-left: 0 +} + +.input-group-btn { + position: relative; + font-size: 0; + white-space: nowrap +} + +.input-group-btn > .btn { + position: relative +} + +.input-group-btn > .btn + .btn { + margin-left: -1px +} + +.input-group-btn > .btn:hover, .input-group-btn > .btn:focus, .input-group-btn > .btn:active { + z-index: 2 +} + +.input-group-btn:first-child > .btn, .input-group-btn:first-child > .btn-group { + margin-right: -1px +} + +.input-group-btn:last-child > .btn, .input-group-btn:last-child > .btn-group { + z-index: 2; + margin-left: -1px +} + +.nav { + margin-bottom: 0; + padding-left: 0; + list-style: none +} + +.nav > li { + position: relative; + display: block +} + +.nav > li > a { + position: relative; + display: block; + padding: 10px 15px +} + +.nav > li > a:hover, .nav > li > a:focus { + text-decoration: none; + background-color: #eeeeee +} + +.nav > li.disabled > a { + color: #bbbbbb +} + +.nav > li.disabled > a:hover, .nav > li.disabled > a:focus { + color: #bbbbbb; + text-decoration: none; + background-color: transparent; + cursor: not-allowed +} + +.nav .open > a, .nav .open > a:hover, .nav .open > a:focus { + background-color: #eeeeee; + border-color: #2196f3 +} + +.nav .nav-divider { + height: 1px; + margin: 10.5px 0; + overflow: hidden; + background-color: #e5e5e5 +} + +.nav > li > a > img { + max-width: none +} + +.nav-tabs { + border-bottom: 1px solid transparent +} + +.nav-tabs > li { + float: left; + margin-bottom: -1px +} + +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.846; + border: 1px solid transparent; + border-radius: 3px 3px 0 0 +} + +.nav-tabs > li > a:hover { + border-color: #eeeeee #eeeeee transparent +} + +.nav-tabs > li.active > a, .nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:focus { + color: #666666; + background-color: transparent; + border: 1px solid transparent; + border-bottom-color: transparent; + cursor: default +} + +.nav-tabs.nav-justified { + width: 100%; + border-bottom: 0 +} + +.nav-tabs.nav-justified > li { + float: none +} + +.nav-tabs.nav-justified > li > a { + text-align: center; + margin-bottom: 5px +} + +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto +} + +@media (min-width: 768px) { + .nav-tabs.nav-justified > li { + display: table-cell; + width: 1% + } + + .nav-tabs.nav-justified > li > a { + margin-bottom: 0 + } +} + +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 3px +} + +.nav-tabs.nav-justified > .active > a, .nav-tabs.nav-justified > .active > a:hover, .nav-tabs.nav-justified > .active > a:focus { + border: 1px solid transparent +} + +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid transparent; + border-radius: 3px 3px 0 0 + } + + .nav-tabs.nav-justified > .active > a, .nav-tabs.nav-justified > .active > a:hover, .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #ffffff + } +} + +.nav-pills > li { + float: left +} + +.nav-pills > li > a { + border-radius: 3px +} + +.nav-pills > li + li { + margin-left: 2px +} + +.nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus { + color: #ffffff; + background-color: #2196f3 +} + +.nav-stacked > li { + float: none +} + +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0 +} + +.nav-justified { + width: 100% +} + +.nav-justified > li { + float: none +} + +.nav-justified > li > a { + text-align: center; + margin-bottom: 5px +} + +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto +} + +@media (min-width: 768px) { + .nav-justified > li { + display: table-cell; + width: 1% + } + + .nav-justified > li > a { + margin-bottom: 0 + } +} + +.nav-tabs-justified { + border-bottom: 0 +} + +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 3px +} + +.nav-tabs-justified > .active > a, .nav-tabs-justified > .active > a:hover, .nav-tabs-justified > .active > a:focus { + border: 1px solid transparent +} + +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid transparent; + border-radius: 3px 3px 0 0 + } + + .nav-tabs-justified > .active > a, .nav-tabs-justified > .active > a:hover, .nav-tabs-justified > .active > a:focus { + border-bottom-color: #ffffff + } +} + +.tab-content > .tab-pane { + display: none +} + +.tab-content > .active { + display: block +} + +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-right-radius: 0; + border-top-left-radius: 0 +} + +.navbar { + position: relative; + min-height: 64px; + margin-bottom: 23px; + border: 1px solid transparent +} + +@media (min-width: 768px) { + .navbar { + border-radius: 3px + } +} + +@media (min-width: 768px) { + .navbar-header { + float: left + } +} + +.navbar-collapse { + overflow-x: visible; + padding-right: 15px; + padding-left: 15px; + border-top: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); + -webkit-overflow-scrolling: touch +} + +.navbar-collapse.in { + overflow-y: auto +} + +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + -webkit-box-shadow: none; + box-shadow: none + } + + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important + } + + .navbar-collapse.in { + overflow-y: visible + } + + .navbar-fixed-top .navbar-collapse, .navbar-static-top .navbar-collapse, .navbar-fixed-bottom .navbar-collapse { + padding-left: 0; + padding-right: 0 + } +} + +.navbar-fixed-top .navbar-collapse, .navbar-fixed-bottom .navbar-collapse { + max-height: 340px +} + +@media (max-device-width: 480px) and (orientation: landscape) { + .navbar-fixed-top .navbar-collapse, .navbar-fixed-bottom .navbar-collapse { + max-height: 200px + } +} + +.container > .navbar-header, .container-fluid > .navbar-header, .container > .navbar-collapse, .container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px +} + +@media (min-width: 768px) { + .container > .navbar-header, .container-fluid > .navbar-header, .container > .navbar-collapse, .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0 + } +} + +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px +} + +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0 + } +} + +.navbar-fixed-top, .navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030 +} + +@media (min-width: 768px) { + .navbar-fixed-top, .navbar-fixed-bottom { + border-radius: 0 + } +} + +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px +} + +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0 +} + +.navbar-brand { + float: left; + padding: 20.5px 15px; + font-size: 17px; + line-height: 23px; + height: 64px +} + +.navbar-brand:hover, .navbar-brand:focus { + text-decoration: none +} + +.navbar-brand > img { + display: block +} + +@media (min-width: 768px) { + .navbar > .container .navbar-brand, .navbar > .container-fluid .navbar-brand { + margin-left: -15px + } +} + +.navbar-toggle { + position: relative; + float: right; + margin-right: 15px; + padding: 9px 10px; + margin-top: 15px; + margin-bottom: 15px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 3px +} + +.navbar-toggle:focus { + outline: 0 +} + +.navbar-toggle .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px +} + +.navbar-toggle .icon-bar + .icon-bar { + margin-top: 4px +} + +@media (min-width: 768px) { + .navbar-toggle { + display: none + } +} + +.navbar-nav { + margin: 10.25px -15px +} + +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 23px +} + +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + -webkit-box-shadow: none; + box-shadow: none + } + + .navbar-nav .open .dropdown-menu > li > a, .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px + } + + .navbar-nav .open .dropdown-menu > li > a { + line-height: 23px + } + + .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none + } +} + +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0 + } + + .navbar-nav > li { + float: left + } + + .navbar-nav > li > a { + padding-top: 20.5px; + padding-bottom: 20.5px + } +} + +.navbar-form { + margin-left: -15px; + margin-right: -15px; + padding: 10px 15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + margin-top: 13.5px; + margin-bottom: 13.5px +} + +@media (min-width: 768px) { + .navbar-form .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle + } + + .navbar-form .form-control { + display: inline-block; + width: auto; + vertical-align: middle + } + + .navbar-form .form-control-static { + display: inline-block + } + + .navbar-form .input-group { + display: inline-table; + vertical-align: middle + } + + .navbar-form .input-group .input-group-addon, .navbar-form .input-group .input-group-btn, .navbar-form .input-group .form-control { + width: auto + } + + .navbar-form .input-group > .form-control { + width: 100% + } + + .navbar-form .control-label { + margin-bottom: 0; + vertical-align: middle + } + + .navbar-form .radio, .navbar-form .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle + } + + .navbar-form .radio label, .navbar-form .checkbox label { + padding-left: 0 + } + + .navbar-form .radio input[type="radio"], .navbar-form .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0 + } + + .navbar-form .has-feedback .form-control-feedback { + top: 0 + } +} + +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px + } + + .navbar-form .form-group:last-child { + margin-bottom: 0 + } +} + +@media (min-width: 768px) { + .navbar-form { + width: auto; + border: 0; + margin-left: 0; + margin-right: 0; + padding-top: 0; + padding-bottom: 0; + -webkit-box-shadow: none; + box-shadow: none + } +} + +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-right-radius: 0; + border-top-left-radius: 0 +} + +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + margin-bottom: 0; + border-top-right-radius: 3px; + border-top-left-radius: 3px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0 +} + +.navbar-btn { + margin-top: 13.5px; + margin-bottom: 13.5px +} + +.navbar-btn.btn-sm { + margin-top: 17px; + margin-bottom: 17px +} + +.navbar-btn.btn-xs { + margin-top: 21px; + margin-bottom: 21px +} + +.navbar-text { + margin-top: 20.5px; + margin-bottom: 20.5px +} + +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-left: 15px; + margin-right: 15px + } +} + +@media (min-width: 768px) { + .navbar-left { + float: left !important + } + + .navbar-right { + float: right !important; + margin-right: -15px + } + + .navbar-right ~ .navbar-right { + margin-right: 0 + } +} + +.navbar-default { + background-color: #ffffff; + border-color: transparent +} + +.navbar-default .navbar-brand { + color: #666666 +} + +.navbar-default .navbar-brand:hover, .navbar-default .navbar-brand:focus { + color: #212121; + background-color: transparent +} + +.navbar-default .navbar-text { + color: #bbbbbb +} + +.navbar-default .navbar-nav > li > a { + color: #666666 +} + +.navbar-default .navbar-nav > li > a:hover, .navbar-default .navbar-nav > li > a:focus { + color: #212121; + background-color: transparent +} + +.navbar-default .navbar-nav > .active > a, .navbar-default .navbar-nav > .active > a:hover, .navbar-default .navbar-nav > .active > a:focus { + color: #212121; + background-color: #eeeeee +} + +.navbar-default .navbar-nav > .disabled > a, .navbar-default .navbar-nav > .disabled > a:hover, .navbar-default .navbar-nav > .disabled > a:focus { + color: #cccccc; + background-color: transparent +} + +.navbar-default .navbar-toggle { + border-color: transparent +} + +.navbar-default .navbar-toggle:hover, .navbar-default .navbar-toggle:focus { + background-color: transparent +} + +.navbar-default .navbar-toggle .icon-bar { + background-color: rgba(0, 0, 0, 0.5) +} + +.navbar-default .navbar-collapse, .navbar-default .navbar-form { + border-color: transparent +} + +.navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .open > a:hover, .navbar-default .navbar-nav > .open > a:focus { + background-color: #eeeeee; + color: #212121 +} + +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #666666 + } + + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: #212121; + background-color: transparent + } + + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #212121; + background-color: #eeeeee + } + + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #cccccc; + background-color: transparent + } +} + +.navbar-default .navbar-link { + color: #666666 +} + +.navbar-default .navbar-link:hover { + color: #212121 +} + +.navbar-default .btn-link { + color: #666666 +} + +.navbar-default .btn-link:hover, .navbar-default .btn-link:focus { + color: #212121 +} + +.navbar-default .btn-link[disabled]:hover, fieldset[disabled] .navbar-default .btn-link:hover, .navbar-default .btn-link[disabled]:focus, fieldset[disabled] .navbar-default .btn-link:focus { + color: #cccccc +} + +.navbar-inverse { + background-color: #2196f3; + border-color: transparent +} + +.navbar-inverse .navbar-brand { + color: #b2dbfb +} + +.navbar-inverse .navbar-brand:hover, .navbar-inverse .navbar-brand:focus { + color: #ffffff; + background-color: transparent +} + +.navbar-inverse .navbar-text { + color: #bbbbbb +} + +.navbar-inverse .navbar-nav > li > a { + color: #b2dbfb +} + +.navbar-inverse .navbar-nav > li > a:hover, .navbar-inverse .navbar-nav > li > a:focus { + color: #ffffff; + background-color: transparent +} + +.navbar-inverse .navbar-nav > .active > a, .navbar-inverse .navbar-nav > .active > a:hover, .navbar-inverse .navbar-nav > .active > a:focus { + color: #ffffff; + background-color: #0c7cd5 +} + +.navbar-inverse .navbar-nav > .disabled > a, .navbar-inverse .navbar-nav > .disabled > a:hover, .navbar-inverse .navbar-nav > .disabled > a:focus { + color: #444444; + background-color: transparent +} + +.navbar-inverse .navbar-toggle { + border-color: transparent +} + +.navbar-inverse .navbar-toggle:hover, .navbar-inverse .navbar-toggle:focus { + background-color: transparent +} + +.navbar-inverse .navbar-toggle .icon-bar { + background-color: rgba(0, 0, 0, 0.5) +} + +.navbar-inverse .navbar-collapse, .navbar-inverse .navbar-form { + border-color: #0c84e4 +} + +.navbar-inverse .navbar-nav > .open > a, .navbar-inverse .navbar-nav > .open > a:hover, .navbar-inverse .navbar-nav > .open > a:focus { + background-color: #0c7cd5; + color: #ffffff +} + +@media (max-width: 767px) { + .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { + border-color: transparent + } + + .navbar-inverse .navbar-nav .open .dropdown-menu .divider { + background-color: transparent + } + + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { + color: #b2dbfb + } + + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { + color: #ffffff; + background-color: transparent + } + + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #ffffff; + background-color: #0c7cd5 + } + + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #444444; + background-color: transparent + } +} + +.navbar-inverse .navbar-link { + color: #b2dbfb +} + +.navbar-inverse .navbar-link:hover { + color: #ffffff +} + +.navbar-inverse .btn-link { + color: #b2dbfb +} + +.navbar-inverse .btn-link:hover, .navbar-inverse .btn-link:focus { + color: #ffffff +} + +.navbar-inverse .btn-link[disabled]:hover, fieldset[disabled] .navbar-inverse .btn-link:hover, .navbar-inverse .btn-link[disabled]:focus, fieldset[disabled] .navbar-inverse .btn-link:focus { + color: #444444 +} + +.breadcrumb { + padding: 8px 15px; + margin-bottom: 23px; + list-style: none; + background-color: #f5f5f5; + border-radius: 3px +} + +.breadcrumb > li { + display: inline-block +} + +.breadcrumb > li + li:before { + content: "/\00a0"; + padding: 0 5px; + color: #cccccc +} + +.breadcrumb > .active { + color: #bbbbbb +} + +.pagination { + display: inline-block; + padding-left: 0; + margin: 23px 0; + border-radius: 3px +} + +.pagination > li { + display: inline +} + +.pagination > li > a, .pagination > li > span { + position: relative; + float: left; + padding: 6px 16px; + line-height: 1.846; + text-decoration: none; + color: #2196f3; + background-color: #ffffff; + border: 1px solid #dddddd; + margin-left: -1px +} + +.pagination > li:first-child > a, .pagination > li:first-child > span { + margin-left: 0; + border-bottom-left-radius: 3px; + border-top-left-radius: 3px +} + +.pagination > li:last-child > a, .pagination > li:last-child > span { + border-bottom-right-radius: 3px; + border-top-right-radius: 3px +} + +.pagination > li > a:hover, .pagination > li > span:hover, .pagination > li > a:focus, .pagination > li > span:focus { + z-index: 2; + color: #0a6ebd; + background-color: #eeeeee; + border-color: #dddddd +} + +.pagination > .active > a, .pagination > .active > span, .pagination > .active > a:hover, .pagination > .active > span:hover, .pagination > .active > a:focus, .pagination > .active > span:focus { + z-index: 3; + color: #ffffff; + background-color: #2196f3; + border-color: #2196f3; + cursor: default +} + +.pagination > .disabled > span, .pagination > .disabled > span:hover, .pagination > .disabled > span:focus, .pagination > .disabled > a, .pagination > .disabled > a:hover, .pagination > .disabled > a:focus { + color: #bbbbbb; + background-color: #ffffff; + border-color: #dddddd; + cursor: not-allowed +} + +.pagination-lg > li > a, .pagination-lg > li > span { + padding: 10px 16px; + font-size: 17px; + line-height: 1.3333333 +} + +.pagination-lg > li:first-child > a, .pagination-lg > li:first-child > span { + border-bottom-left-radius: 3px; + border-top-left-radius: 3px +} + +.pagination-lg > li:last-child > a, .pagination-lg > li:last-child > span { + border-bottom-right-radius: 3px; + border-top-right-radius: 3px +} + +.pagination-sm > li > a, .pagination-sm > li > span { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5 +} + +.pagination-sm > li:first-child > a, .pagination-sm > li:first-child > span { + border-bottom-left-radius: 3px; + border-top-left-radius: 3px +} + +.pagination-sm > li:last-child > a, .pagination-sm > li:last-child > span { + border-bottom-right-radius: 3px; + border-top-right-radius: 3px +} + +.pager { + padding-left: 0; + margin: 23px 0; + list-style: none; + text-align: center +} + +.pager li { + display: inline +} + +.pager li > a, .pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #ffffff; + border: 1px solid #dddddd; + border-radius: 15px +} + +.pager li > a:hover, .pager li > a:focus { + text-decoration: none; + background-color: #eeeeee +} + +.pager .next > a, .pager .next > span { + float: right +} + +.pager .previous > a, .pager .previous > span { + float: left +} + +.pager .disabled > a, .pager .disabled > a:hover, .pager .disabled > a:focus, .pager .disabled > span { + color: #bbbbbb; + background-color: #ffffff; + cursor: not-allowed +} + +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + color: #ffffff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em +} + +a.label:hover, a.label:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer +} + +.label:empty { + display: none +} + +.btn .label { + position: relative; + top: -1px +} + +.label-default { + background-color: #bbbbbb +} + +.label-default[href]:hover, .label-default[href]:focus { + background-color: #a2a2a2 +} + +.label-primary { + background-color: #2196f3 +} + +.label-primary[href]:hover, .label-primary[href]:focus { + background-color: #0c7cd5 +} + +.label-success { + background-color: #4caf50 +} + +.label-success[href]:hover, .label-success[href]:focus { + background-color: #3d8b40 +} + +.label-info { + background-color: #9c27b0 +} + +.label-info[href]:hover, .label-info[href]:focus { + background-color: #771e86 +} + +.label-warning { + background-color: #ff9800 +} + +.label-warning[href]:hover, .label-warning[href]:focus { + background-color: #cc7a00 +} + +.label-danger { + background-color: #e51c23 +} + +.label-danger[href]:hover, .label-danger[href]:focus { + background-color: #b9151b +} + +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: 12px; + font-weight: normal; + color: #ffffff; + line-height: 1; + vertical-align: middle; + white-space: nowrap; + text-align: center; + background-color: #bbbbbb; + border-radius: 10px +} + +.badge:empty { + display: none +} + +.btn .badge { + position: relative; + top: -1px +} + +.btn-xs .badge, .btn-group-xs > .btn .badge { + top: 0; + padding: 1px 5px +} + +a.badge:hover, a.badge:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer +} + +.list-group-item.active > .badge, .nav-pills > .active > a > .badge { + color: #2196f3; + background-color: #ffffff +} + +.list-group-item > .badge { + float: right +} + +.list-group-item > .badge + .badge { + margin-right: 5px +} + +.nav-pills > li > a > .badge { + margin-left: 3px +} + +.jumbotron { + padding-top: 30px; + padding-bottom: 30px; + margin-bottom: 30px; + color: inherit; + background-color: #f9f9f9 +} + +.jumbotron h1, .jumbotron .h1 { + color: #444444 +} + +.jumbotron p { + margin-bottom: 15px; + font-size: 20px; + font-weight: 200 +} + +.jumbotron > hr { + border-top-color: #e0e0e0 +} + +.container .jumbotron, .container-fluid .jumbotron { + border-radius: 3px; + padding-left: 15px; + padding-right: 15px +} + +.jumbotron .container { + max-width: 100% +} + +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px + } + + .container .jumbotron, .container-fluid .jumbotron { + padding-left: 60px; + padding-right: 60px + } + + .jumbotron h1, .jumbotron .h1 { + font-size: 59px + } +} + +.thumbnail { + display: block; + padding: 4px; + margin-bottom: 23px; + line-height: 1.846; + background-color: #ffffff; + border: 1px solid #dddddd; + border-radius: 3px; + -webkit-transition: border .2s ease-in-out; + -o-transition: border .2s ease-in-out; + transition: border .2s ease-in-out +} + +.thumbnail > img, .thumbnail a > img { + margin-left: auto; + margin-right: auto +} + +a.thumbnail:hover, a.thumbnail:focus, a.thumbnail.active { + border-color: #2196f3 +} + +.thumbnail .caption { + padding: 9px; + color: #666666 +} + +.alert { + padding: 15px; + margin-bottom: 23px; + border: 1px solid transparent; + border-radius: 3px +} + +.alert h4 { + margin-top: 0; + color: inherit +} + +.alert .alert-link { + font-weight: bold +} + +.alert > p, .alert > ul { + margin-bottom: 0 +} + +.alert > p + p { + margin-top: 5px +} + +.alert-dismissable, .alert-dismissible { + padding-right: 35px +} + +.alert-dismissable .close, .alert-dismissible .close { + position: relative; + top: -2px; + right: -21px; + color: inherit +} + +.alert-success { + background-color: #dff0d8; + border-color: #d6e9c6; + color: #4caf50 +} + +.alert-success hr { + border-top-color: #c9e2b3 +} + +.alert-success .alert-link { + color: #3d8b40 +} + +.alert-info { + background-color: #e1bee7; + border-color: #cba4dd; + color: #9c27b0 +} + +.alert-info hr { + border-top-color: #c191d6 +} + +.alert-info .alert-link { + color: #771e86 +} + +.alert-warning { + background-color: #ffe0b2; + border-color: #ffc599; + color: #ff9800 +} + +.alert-warning hr { + border-top-color: #ffb67f +} + +.alert-warning .alert-link { + color: #cc7a00 +} + +.alert-danger { + background-color: #f9bdbb; + border-color: #f7a4af; + color: #e51c23 +} + +.alert-danger hr { + border-top-color: #f58c9a +} + +.alert-danger .alert-link { + color: #b9151b +} + +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0 + } + to { + background-position: 0 0 + } +} + +@-o-keyframes progress-bar-stripes { + from { + background-position: 40px 0 + } + to { + background-position: 0 0 + } +} + +@keyframes progress-bar-stripes { + from { + background-position: 40px 0 + } + to { + background-position: 0 0 + } +} + +.progress { + overflow: hidden; + height: 23px; + margin-bottom: 23px; + background-color: #f5f5f5; + border-radius: 3px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1) +} + +.progress-bar { + float: left; + width: 0%; + height: 100%; + font-size: 12px; + line-height: 23px; + color: #ffffff; + text-align: center; + background-color: #2196f3; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -webkit-transition: width 0.6s ease; + -o-transition: width 0.6s ease; + transition: width 0.6s ease +} + +.progress-striped .progress-bar, .progress-bar-striped { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + background-size: 40px 40px +} + +.progress.active .progress-bar, .progress-bar.active { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite +} + +.progress-bar-success { + background-color: #4caf50 +} + +.progress-striped .progress-bar-success { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent) +} + +.progress-bar-info { + background-color: #9c27b0 +} + +.progress-striped .progress-bar-info { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent) +} + +.progress-bar-warning { + background-color: #ff9800 +} + +.progress-striped .progress-bar-warning { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent) +} + +.progress-bar-danger { + background-color: #e51c23 +} + +.progress-striped .progress-bar-danger { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent) +} + +.media { + margin-top: 15px +} + +.media:first-child { + margin-top: 0 +} + +.media, .media-body { + zoom: 1; + overflow: hidden +} + +.media-body { + width: 10000px +} + +.media-object { + display: block +} + +.media-object.img-thumbnail { + max-width: none +} + +.media-right, .media > .pull-right { + padding-left: 10px +} + +.media-left, .media > .pull-left { + padding-right: 10px +} + +.media-left, .media-right, .media-body { + display: table-cell; + vertical-align: top +} + +.media-middle { + vertical-align: middle +} + +.media-bottom { + vertical-align: bottom +} + +.media-heading { + margin-top: 0; + margin-bottom: 5px +} + +.media-list { + padding-left: 0; + list-style: none +} + +.list-group { + margin-bottom: 20px; + padding-left: 0 +} + +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + margin-bottom: -1px; + background-color: #ffffff; + border: 1px solid #dddddd +} + +.list-group-item:first-child { + border-top-right-radius: 3px; + border-top-left-radius: 3px +} + +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px +} + +a.list-group-item, button.list-group-item { + color: #555555 +} + +a.list-group-item .list-group-item-heading, button.list-group-item .list-group-item-heading { + color: #333333 +} + +a.list-group-item:hover, button.list-group-item:hover, a.list-group-item:focus, button.list-group-item:focus { + text-decoration: none; + color: #555555; + background-color: #f5f5f5 +} + +button.list-group-item { + width: 100%; + text-align: left +} + +.list-group-item.disabled, .list-group-item.disabled:hover, .list-group-item.disabled:focus { + background-color: #eeeeee; + color: #bbbbbb; + cursor: not-allowed +} + +.list-group-item.disabled .list-group-item-heading, .list-group-item.disabled:hover .list-group-item-heading, .list-group-item.disabled:focus .list-group-item-heading { + color: inherit +} + +.list-group-item.disabled .list-group-item-text, .list-group-item.disabled:hover .list-group-item-text, .list-group-item.disabled:focus .list-group-item-text { + color: #bbbbbb +} + +.list-group-item.active, .list-group-item.active:hover, .list-group-item.active:focus { + z-index: 2; + color: #ffffff; + background-color: #2196f3; + border-color: #2196f3 +} + +.list-group-item.active .list-group-item-heading, .list-group-item.active:hover .list-group-item-heading, .list-group-item.active:focus .list-group-item-heading, .list-group-item.active .list-group-item-heading > small, .list-group-item.active:hover .list-group-item-heading > small, .list-group-item.active:focus .list-group-item-heading > small, .list-group-item.active .list-group-item-heading > .small, .list-group-item.active:hover .list-group-item-heading > .small, .list-group-item.active:focus .list-group-item-heading > .small { + color: inherit +} + +.list-group-item.active .list-group-item-text, .list-group-item.active:hover .list-group-item-text, .list-group-item.active:focus .list-group-item-text { + color: #e3f2fd +} + +.list-group-item-success { + color: #4caf50; + background-color: #dff0d8 +} + +a.list-group-item-success, button.list-group-item-success { + color: #4caf50 +} + +a.list-group-item-success .list-group-item-heading, button.list-group-item-success .list-group-item-heading { + color: inherit +} + +a.list-group-item-success:hover, button.list-group-item-success:hover, a.list-group-item-success:focus, button.list-group-item-success:focus { + color: #4caf50; + background-color: #d0e9c6 +} + +a.list-group-item-success.active, button.list-group-item-success.active, a.list-group-item-success.active:hover, button.list-group-item-success.active:hover, a.list-group-item-success.active:focus, button.list-group-item-success.active:focus { + color: #fff; + background-color: #4caf50; + border-color: #4caf50 +} + +.list-group-item-info { + color: #9c27b0; + background-color: #e1bee7 +} + +a.list-group-item-info, button.list-group-item-info { + color: #9c27b0 +} + +a.list-group-item-info .list-group-item-heading, button.list-group-item-info .list-group-item-heading { + color: inherit +} + +a.list-group-item-info:hover, button.list-group-item-info:hover, a.list-group-item-info:focus, button.list-group-item-info:focus { + color: #9c27b0; + background-color: #d8abe0 +} + +a.list-group-item-info.active, button.list-group-item-info.active, a.list-group-item-info.active:hover, button.list-group-item-info.active:hover, a.list-group-item-info.active:focus, button.list-group-item-info.active:focus { + color: #fff; + background-color: #9c27b0; + border-color: #9c27b0 +} + +.list-group-item-warning { + color: #ff9800; + background-color: #ffe0b2 +} + +a.list-group-item-warning, button.list-group-item-warning { + color: #ff9800 +} + +a.list-group-item-warning .list-group-item-heading, button.list-group-item-warning .list-group-item-heading { + color: inherit +} + +a.list-group-item-warning:hover, button.list-group-item-warning:hover, a.list-group-item-warning:focus, button.list-group-item-warning:focus { + color: #ff9800; + background-color: #ffd699 +} + +a.list-group-item-warning.active, button.list-group-item-warning.active, a.list-group-item-warning.active:hover, button.list-group-item-warning.active:hover, a.list-group-item-warning.active:focus, button.list-group-item-warning.active:focus { + color: #fff; + background-color: #ff9800; + border-color: #ff9800 +} + +.list-group-item-danger { + color: #e51c23; + background-color: #f9bdbb +} + +a.list-group-item-danger, button.list-group-item-danger { + color: #e51c23 +} + +a.list-group-item-danger .list-group-item-heading, button.list-group-item-danger .list-group-item-heading { + color: inherit +} + +a.list-group-item-danger:hover, button.list-group-item-danger:hover, a.list-group-item-danger:focus, button.list-group-item-danger:focus { + color: #e51c23; + background-color: #f7a6a4 +} + +a.list-group-item-danger.active, button.list-group-item-danger.active, a.list-group-item-danger.active:hover, button.list-group-item-danger.active:hover, a.list-group-item-danger.active:focus, button.list-group-item-danger.active:focus { + color: #fff; + background-color: #e51c23; + border-color: #e51c23 +} + +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px +} + +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3 +} + +.panel { + margin-bottom: 23px; + background-color: #ffffff; + border: 1px solid transparent; + border-radius: 3px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05) +} + +.panel-body { + padding: 15px +} + +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-right-radius: 2px; + border-top-left-radius: 2px +} + +.panel-heading > .dropdown .dropdown-toggle { + color: inherit +} + +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 15px; + color: inherit +} + +.panel-title > a, .panel-title > small, .panel-title > .small, .panel-title > small > a, .panel-title > .small > a { + color: inherit +} + +.panel-footer { + padding: 10px 15px; + background-color: #f5f5f5; + border-top: 1px solid #dddddd; + border-bottom-right-radius: 2px; + border-bottom-left-radius: 2px +} + +.panel > .list-group, .panel > .panel-collapse > .list-group { + margin-bottom: 0 +} + +.panel > .list-group .list-group-item, .panel > .panel-collapse > .list-group .list-group-item { + border-width: 1px 0; + border-radius: 0 +} + +.panel > .list-group:first-child .list-group-item:first-child, .panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { + border-top: 0; + border-top-right-radius: 2px; + border-top-left-radius: 2px +} + +.panel > .list-group:last-child .list-group-item:last-child, .panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { + border-bottom: 0; + border-bottom-right-radius: 2px; + border-bottom-left-radius: 2px +} + +.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child { + border-top-right-radius: 0; + border-top-left-radius: 0 +} + +.panel-heading + .list-group .list-group-item:first-child { + border-top-width: 0 +} + +.list-group + .panel-footer { + border-top-width: 0 +} + +.panel > .table, .panel > .table-responsive > .table, .panel > .panel-collapse > .table { + margin-bottom: 0 +} + +.panel > .table caption, .panel > .table-responsive > .table caption, .panel > .panel-collapse > .table caption { + padding-left: 15px; + padding-right: 15px +} + +.panel > .table:first-child, .panel > .table-responsive:first-child > .table:first-child { + border-top-right-radius: 2px; + border-top-left-radius: 2px +} + +.panel > .table:first-child > thead:first-child > tr:first-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, .panel > .table:first-child > tbody:first-child > tr:first-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { + border-top-left-radius: 2px; + border-top-right-radius: 2px +} + +.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, .panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, .panel > .table:first-child > thead:first-child > tr:first-child th:first-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, .panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { + border-top-left-radius: 2px +} + +.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, .panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, .panel > .table:first-child > thead:first-child > tr:first-child th:last-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, .panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { + border-top-right-radius: 2px +} + +.panel > .table:last-child, .panel > .table-responsive:last-child > .table:last-child { + border-bottom-right-radius: 2px; + border-bottom-left-radius: 2px +} + +.panel > .table:last-child > tbody:last-child > tr:last-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, .panel > .table:last-child > tfoot:last-child > tr:last-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { + border-bottom-left-radius: 2px; + border-bottom-right-radius: 2px +} + +.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, .panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, .panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, .panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { + border-bottom-left-radius: 2px +} + +.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, .panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, .panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, .panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { + border-bottom-right-radius: 2px +} + +.panel > .panel-body + .table, .panel > .panel-body + .table-responsive, .panel > .table + .panel-body, .panel > .table-responsive + .panel-body { + border-top: 1px solid #dddddd +} + +.panel > .table > tbody:first-child > tr:first-child th, .panel > .table > tbody:first-child > tr:first-child td { + border-top: 0 +} + +.panel > .table-bordered, .panel > .table-responsive > .table-bordered { + border: 0 +} + +.panel > .table-bordered > thead > tr > th:first-child, .panel > .table-responsive > .table-bordered > thead > tr > th:first-child, .panel > .table-bordered > tbody > tr > th:first-child, .panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, .panel > .table-bordered > tfoot > tr > th:first-child, .panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, .panel > .table-bordered > thead > tr > td:first-child, .panel > .table-responsive > .table-bordered > thead > tr > td:first-child, .panel > .table-bordered > tbody > tr > td:first-child, .panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, .panel > .table-bordered > tfoot > tr > td:first-child, .panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0 +} + +.panel > .table-bordered > thead > tr > th:last-child, .panel > .table-responsive > .table-bordered > thead > tr > th:last-child, .panel > .table-bordered > tbody > tr > th:last-child, .panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, .panel > .table-bordered > tfoot > tr > th:last-child, .panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, .panel > .table-bordered > thead > tr > td:last-child, .panel > .table-responsive > .table-bordered > thead > tr > td:last-child, .panel > .table-bordered > tbody > tr > td:last-child, .panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, .panel > .table-bordered > tfoot > tr > td:last-child, .panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0 +} + +.panel > .table-bordered > thead > tr:first-child > td, .panel > .table-responsive > .table-bordered > thead > tr:first-child > td, .panel > .table-bordered > tbody > tr:first-child > td, .panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, .panel > .table-bordered > thead > tr:first-child > th, .panel > .table-responsive > .table-bordered > thead > tr:first-child > th, .panel > .table-bordered > tbody > tr:first-child > th, .panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { + border-bottom: 0 +} + +.panel > .table-bordered > tbody > tr:last-child > td, .panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, .panel > .table-bordered > tfoot > tr:last-child > td, .panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, .panel > .table-bordered > tbody > tr:last-child > th, .panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, .panel > .table-bordered > tfoot > tr:last-child > th, .panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { + border-bottom: 0 +} + +.panel > .table-responsive { + border: 0; + margin-bottom: 0 +} + +.panel-group { + margin-bottom: 23px +} + +.panel-group .panel { + margin-bottom: 0; + border-radius: 3px +} + +.panel-group .panel + .panel { + margin-top: 5px +} + +.panel-group .panel-heading { + border-bottom: 0 +} + +.panel-group .panel-heading + .panel-collapse > .panel-body, .panel-group .panel-heading + .panel-collapse > .list-group { + border-top: 1px solid #dddddd +} + +.panel-group .panel-footer { + border-top: 0 +} + +.panel-group .panel-footer + .panel-collapse .panel-body { + border-bottom: 1px solid #dddddd +} + +.panel-default { + border-color: #dddddd +} + +.panel-default > .panel-heading { + color: #212121; + background-color: #f5f5f5; + border-color: #dddddd +} + +.panel-default > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #dddddd +} + +.panel-default > .panel-heading .badge { + color: #f5f5f5; + background-color: #212121 +} + +.panel-default > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #dddddd +} + +.panel-primary { + border-color: #2196f3 +} + +.panel-primary > .panel-heading { + color: #ffffff; + background-color: #2196f3; + border-color: #2196f3 +} + +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #2196f3 +} + +.panel-primary > .panel-heading .badge { + color: #2196f3; + background-color: #ffffff +} + +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #2196f3 +} + +.panel-success { + border-color: #d6e9c6 +} + +.panel-success > .panel-heading { + color: #ffffff; + background-color: #4caf50; + border-color: #d6e9c6 +} + +.panel-success > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #d6e9c6 +} + +.panel-success > .panel-heading .badge { + color: #4caf50; + background-color: #ffffff +} + +.panel-success > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #d6e9c6 +} + +.panel-info { + border-color: #cba4dd +} + +.panel-info > .panel-heading { + color: #ffffff; + background-color: #9c27b0; + border-color: #cba4dd +} + +.panel-info > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #cba4dd +} + +.panel-info > .panel-heading .badge { + color: #9c27b0; + background-color: #ffffff +} + +.panel-info > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #cba4dd +} + +.panel-warning { + border-color: #ffc599 +} + +.panel-warning > .panel-heading { + color: #ffffff; + background-color: #ff9800; + border-color: #ffc599 +} + +.panel-warning > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ffc599 +} + +.panel-warning > .panel-heading .badge { + color: #ff9800; + background-color: #ffffff +} + +.panel-warning > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ffc599 +} + +.panel-danger { + border-color: #f7a4af +} + +.panel-danger > .panel-heading { + color: #ffffff; + background-color: #e51c23; + border-color: #f7a4af +} + +.panel-danger > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #f7a4af +} + +.panel-danger > .panel-heading .badge { + color: #e51c23; + background-color: #ffffff +} + +.panel-danger > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #f7a4af +} + +.embed-responsive { + position: relative; + display: block; + height: 0; + padding: 0; + overflow: hidden +} + +.embed-responsive .embed-responsive-item, .embed-responsive iframe, .embed-responsive embed, .embed-responsive object, .embed-responsive video { + position: absolute; + top: 0; + left: 0; + bottom: 0; + height: 100%; + width: 100%; + border: 0 +} + +.embed-responsive-16by9 { + padding-bottom: 56.25% +} + +.embed-responsive-4by3 { + padding-bottom: 75% +} + +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f9f9f9; + border: 1px solid transparent; + border-radius: 3px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05) +} + +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, 0.15) +} + +.well-lg { + padding: 24px; + border-radius: 3px +} + +.well-sm { + padding: 9px; + border-radius: 3px +} + +.close { + float: right; + font-size: 19.5px; + font-weight: normal; + line-height: 1; + color: #000000; + text-shadow: none; + opacity: 0.2; + filter: alpha(opacity=20) +} + +.close:hover, .close:focus { + color: #000000; + text-decoration: none; + cursor: pointer; + opacity: 0.5; + filter: alpha(opacity=50) +} + +button.close { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none +} + +.modal-open { + overflow: hidden +} + +.modal { + display: none; + overflow: hidden; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1050; + -webkit-overflow-scrolling: touch; + outline: 0 +} + +.modal.fade .modal-dialog { + -webkit-transform: translate(0, -25%); + -ms-transform: translate(0, -25%); + -o-transform: translate(0, -25%); + transform: translate(0, -25%); + -webkit-transition: -webkit-transform .3s ease-out; + -o-transition: -o-transform .3s ease-out; + transition: transform .3s ease-out +} + +.modal.in .modal-dialog { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + -o-transform: translate(0, 0); + transform: translate(0, 0) +} + +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto +} + +.modal-dialog { + position: relative; + width: auto; + margin: 10px +} + +.modal-content { + position: relative; + background-color: #ffffff; + border: 1px solid #999999; + border: 1px solid transparent; + border-radius: 3px; + -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); + box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); + -webkit-background-clip: padding-box; + background-clip: padding-box; + outline: 0 +} + +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000000 +} + +.modal-backdrop.fade { + opacity: 0; + filter: alpha(opacity=0) +} + +.modal-backdrop.in { + opacity: 0.5; + filter: alpha(opacity=50) +} + +.modal-header { + padding: 15px; + border-bottom: 1px solid transparent +} + +.modal-header .close { + margin-top: -2px +} + +.modal-title { + margin: 0; + line-height: 1.846 +} + +.modal-body { + position: relative; + padding: 15px +} + +.modal-footer { + padding: 15px; + text-align: right; + border-top: 1px solid transparent +} + +.modal-footer .btn + .btn { + margin-left: 5px; + margin-bottom: 0 +} + +.modal-footer .btn-group .btn + .btn { + margin-left: -1px +} + +.modal-footer .btn-block + .btn-block { + margin-left: 0 +} + +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll +} + +@media (min-width: 768px) { + .modal-dialog { + width: 600px; + margin: 30px auto + } + + .modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5) + } + + .modal-sm { + width: 300px + } +} + +@media (min-width: 992px) { + .modal-lg { + width: 900px + } +} + +.tooltip { + position: absolute; + z-index: 1070; + display: block; + font-family: "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-style: normal; + font-weight: normal; + letter-spacing: normal; + line-break: auto; + line-height: 1.846; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + white-space: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + font-size: 12px; + opacity: 0; + filter: alpha(opacity=0) +} + +.tooltip.in { + opacity: 0.9; + filter: alpha(opacity=90) +} + +.tooltip.top { + margin-top: -3px; + padding: 5px 0 +} + +.tooltip.right { + margin-left: 3px; + padding: 0 5px +} + +.tooltip.bottom { + margin-top: 3px; + padding: 5px 0 +} + +.tooltip.left { + margin-left: -3px; + padding: 0 5px +} + +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #ffffff; + text-align: center; + background-color: #727272; + border-radius: 3px +} + +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid +} + +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #727272 +} + +.tooltip.top-left .tooltip-arrow { + bottom: 0; + right: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #727272 +} + +.tooltip.top-right .tooltip-arrow { + bottom: 0; + left: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #727272 +} + +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #727272 +} + +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #727272 +} + +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #727272 +} + +.tooltip.bottom-left .tooltip-arrow { + top: 0; + right: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #727272 +} + +.tooltip.bottom-right .tooltip-arrow { + top: 0; + left: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #727272 +} + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: none; + max-width: 276px; + padding: 1px; + font-family: "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-style: normal; + font-weight: normal; + letter-spacing: normal; + line-break: auto; + line-height: 1.846; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + white-space: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + font-size: 13px; + background-color: #ffffff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid transparent; + border-radius: 3px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2) +} + +.popover.top { + margin-top: -10px +} + +.popover.right { + margin-left: 10px +} + +.popover.bottom { + margin-top: 10px +} + +.popover.left { + margin-left: -10px +} + +.popover-title { + margin: 0; + padding: 8px 14px; + font-size: 13px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-radius: 2px 2px 0 0 +} + +.popover-content { + padding: 9px 14px +} + +.popover > .arrow, .popover > .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid +} + +.popover > .arrow { + border-width: 11px +} + +.popover > .arrow:after { + border-width: 10px; + content: "" +} + +.popover.top > .arrow { + left: 50%; + margin-left: -11px; + border-bottom-width: 0; + border-top-color: rgba(0, 0, 0, 0); + border-top-color: rgba(0, 0, 0, 0.075); + bottom: -11px +} + +.popover.top > .arrow:after { + content: " "; + bottom: 1px; + margin-left: -10px; + border-bottom-width: 0; + border-top-color: #ffffff +} + +.popover.right > .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-left-width: 0; + border-right-color: rgba(0, 0, 0, 0); + border-right-color: rgba(0, 0, 0, 0.075) +} + +.popover.right > .arrow:after { + content: " "; + left: 1px; + bottom: -10px; + border-left-width: 0; + border-right-color: #ffffff +} + +.popover.bottom > .arrow { + left: 50%; + margin-left: -11px; + border-top-width: 0; + border-bottom-color: rgba(0, 0, 0, 0); + border-bottom-color: rgba(0, 0, 0, 0.075); + top: -11px +} + +.popover.bottom > .arrow:after { + content: " "; + top: 1px; + margin-left: -10px; + border-top-width: 0; + border-bottom-color: #ffffff +} + +.popover.left > .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-right-width: 0; + border-left-color: rgba(0, 0, 0, 0); + border-left-color: rgba(0, 0, 0, 0.075) +} + +.popover.left > .arrow:after { + content: " "; + right: 1px; + border-right-width: 0; + border-left-color: #ffffff; + bottom: -10px +} + +.carousel { + position: relative +} + +.carousel-inner { + position: relative; + overflow: hidden; + width: 100% +} + +.carousel-inner > .item { + display: none; + position: relative; + -webkit-transition: .6s ease-in-out left; + -o-transition: .6s ease-in-out left; + transition: .6s ease-in-out left +} + +.carousel-inner > .item > img, .carousel-inner > .item > a > img { + line-height: 1 +} + +@media all and (transform-3d),(-webkit-transform-3d) { + .carousel-inner > .item { + -webkit-transition: -webkit-transform .6s ease-in-out; + -o-transition: -o-transform .6s ease-in-out; + transition: transform .6s ease-in-out; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-perspective: 1000px; + perspective: 1000px + } + + .carousel-inner > .item.next, .carousel-inner > .item.active.right { + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + left: 0 + } + + .carousel-inner > .item.prev, .carousel-inner > .item.active.left { + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + left: 0 + } + + .carousel-inner > .item.next.left, .carousel-inner > .item.prev.right, .carousel-inner > .item.active { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + left: 0 + } +} + +.carousel-inner > .active, .carousel-inner > .next, .carousel-inner > .prev { + display: block +} + +.carousel-inner > .active { + left: 0 +} + +.carousel-inner > .next, .carousel-inner > .prev { + position: absolute; + top: 0; + width: 100% +} + +.carousel-inner > .next { + left: 100% +} + +.carousel-inner > .prev { + left: -100% +} + +.carousel-inner > .next.left, .carousel-inner > .prev.right { + left: 0 +} + +.carousel-inner > .active.left { + left: -100% +} + +.carousel-inner > .active.right { + left: 100% +} + +.carousel-control { + position: absolute; + top: 0; + left: 0; + bottom: 0; + width: 15%; + opacity: 0.5; + filter: alpha(opacity=50); + font-size: 20px; + color: #ffffff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); + background-color: rgba(0, 0, 0, 0) +} + +.carousel-control.left { + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.5) 0, rgba(0, 0, 0, 0.0001) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5) 0, rgba(0, 0, 0, 0.0001) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, 0.5)), to(rgba(0, 0, 0, 0.0001))); + background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0, rgba(0, 0, 0, 0.0001) 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1) +} + +.carousel-control.right { + left: auto; + right: 0; + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0, rgba(0, 0, 0, 0.5) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0, rgba(0, 0, 0, 0.5) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, 0.0001)), to(rgba(0, 0, 0, 0.5))); + background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0, rgba(0, 0, 0, 0.5) 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1) +} + +.carousel-control:hover, .carousel-control:focus { + outline: 0; + color: #ffffff; + text-decoration: none; + opacity: 0.9; + filter: alpha(opacity=90) +} + +.carousel-control .icon-prev, .carousel-control .icon-next, .carousel-control .glyphicon-chevron-left, .carousel-control .glyphicon-chevron-right { + position: absolute; + top: 50%; + margin-top: -10px; + z-index: 5; + display: inline-block +} + +.carousel-control .icon-prev, .carousel-control .glyphicon-chevron-left { + left: 50%; + margin-left: -10px +} + +.carousel-control .icon-next, .carousel-control .glyphicon-chevron-right { + right: 50%; + margin-right: -10px +} + +.carousel-control .icon-prev, .carousel-control .icon-next { + width: 20px; + height: 20px; + line-height: 1; + font-family: serif +} + +.carousel-control .icon-prev:before { + content: '\2039' +} + +.carousel-control .icon-next:before { + content: '\203a' +} + +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + margin-left: -30%; + padding-left: 0; + list-style: none; + text-align: center +} + +.carousel-indicators li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + border: 1px solid #ffffff; + border-radius: 10px; + cursor: pointer; + background-color: #000 \9; + background-color: rgba(0, 0, 0, 0) +} + +.carousel-indicators .active { + margin: 0; + width: 12px; + height: 12px; + background-color: #ffffff +} + +.carousel-caption { + position: absolute; + left: 15%; + right: 15%; + bottom: 20px; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #ffffff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6) +} + +.carousel-caption .btn { + text-shadow: none +} + +@media screen and (min-width: 768px) { + .carousel-control .glyphicon-chevron-left, .carousel-control .glyphicon-chevron-right, .carousel-control .icon-prev, .carousel-control .icon-next { + width: 30px; + height: 30px; + margin-top: -10px; + font-size: 30px + } + + .carousel-control .glyphicon-chevron-left, .carousel-control .icon-prev { + margin-left: -10px + } + + .carousel-control .glyphicon-chevron-right, .carousel-control .icon-next { + margin-right: -10px + } + + .carousel-caption { + left: 20%; + right: 20%; + padding-bottom: 30px + } + + .carousel-indicators { + bottom: 20px + } +} + +.clearfix:before, .clearfix:after, .dl-horizontal dd:before, .dl-horizontal dd:after, .container:before, .container:after, .container-fluid:before, .container-fluid:after, .row:before, .row:after, .form-horizontal .form-group:before, .form-horizontal .form-group:after, .btn-toolbar:before, .btn-toolbar:after, .btn-group-vertical > .btn-group:before, .btn-group-vertical > .btn-group:after, .nav:before, .nav:after, .navbar:before, .navbar:after, .navbar-header:before, .navbar-header:after, .navbar-collapse:before, .navbar-collapse:after, .pager:before, .pager:after, .panel-body:before, .panel-body:after, .modal-header:before, .modal-header:after, .modal-footer:before, .modal-footer:after { + content: " "; + display: table +} + +.clearfix:after, .dl-horizontal dd:after, .container:after, .container-fluid:after, .row:after, .form-horizontal .form-group:after, .btn-toolbar:after, .btn-group-vertical > .btn-group:after, .nav:after, .navbar:after, .navbar-header:after, .navbar-collapse:after, .pager:after, .panel-body:after, .modal-header:after, .modal-footer:after { + clear: both +} + +.center-block { + display: block; + margin-left: auto; + margin-right: auto +} + +.pull-right { + float: right !important +} + +.pull-left { + float: left !important +} + +.hide { + display: none !important +} + +.show { + display: block !important +} + +.invisible { + visibility: hidden +} + +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0 +} + +.hidden { + display: none !important +} + +.affix { + position: fixed +} + +@-ms-viewport { + width: device-width +} + +.visible-xs, .visible-sm, .visible-md, .visible-lg { + display: none !important +} + +.visible-xs-block, .visible-xs-inline, .visible-xs-inline-block, .visible-sm-block, .visible-sm-inline, .visible-sm-inline-block, .visible-md-block, .visible-md-inline, .visible-md-inline-block, .visible-lg-block, .visible-lg-inline, .visible-lg-inline-block { + display: none !important +} + +@media (max-width: 767px) { + .visible-xs { + display: block !important + } + + table.visible-xs { + display: table !important + } + + tr.visible-xs { + display: table-row !important + } + + th.visible-xs, td.visible-xs { + display: table-cell !important + } +} + +@media (max-width: 767px) { + .visible-xs-block { + display: block !important + } +} + +@media (max-width: 767px) { + .visible-xs-inline { + display: inline !important + } +} + +@media (max-width: 767px) { + .visible-xs-inline-block { + display: inline-block !important + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm { + display: block !important + } + + table.visible-sm { + display: table !important + } + + tr.visible-sm { + display: table-row !important + } + + th.visible-sm, td.visible-sm { + display: table-cell !important + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-block { + display: block !important + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline { + display: inline !important + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline-block { + display: inline-block !important + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md { + display: block !important + } + + table.visible-md { + display: table !important + } + + tr.visible-md { + display: table-row !important + } + + th.visible-md, td.visible-md { + display: table-cell !important + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-block { + display: block !important + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline { + display: inline !important + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline-block { + display: inline-block !important + } +} + +@media (min-width: 1200px) { + .visible-lg { + display: block !important + } + + table.visible-lg { + display: table !important + } + + tr.visible-lg { + display: table-row !important + } + + th.visible-lg, td.visible-lg { + display: table-cell !important + } +} + +@media (min-width: 1200px) { + .visible-lg-block { + display: block !important + } +} + +@media (min-width: 1200px) { + .visible-lg-inline { + display: inline !important + } +} + +@media (min-width: 1200px) { + .visible-lg-inline-block { + display: inline-block !important + } +} + +@media (max-width: 767px) { + .hidden-xs { + display: none !important + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .hidden-sm { + display: none !important + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-md { + display: none !important + } +} + +@media (min-width: 1200px) { + .hidden-lg { + display: none !important + } +} + +.visible-print { + display: none !important +} + +@media print { + .visible-print { + display: block !important + } + + table.visible-print { + display: table !important + } + + tr.visible-print { + display: table-row !important + } + + th.visible-print, td.visible-print { + display: table-cell !important + } +} + +.visible-print-block { + display: none !important +} + +@media print { + .visible-print-block { + display: block !important + } +} + +.visible-print-inline { + display: none !important +} + +@media print { + .visible-print-inline { + display: inline !important + } +} + +.visible-print-inline-block { + display: none !important +} + +@media print { + .visible-print-inline-block { + display: inline-block !important + } +} + +@media print { + .hidden-print { + display: none !important + } +} + +.navbar { + border: none; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3) +} + +.navbar-brand { + font-size: 24px +} + +.navbar-inverse .navbar-form input[type=text], .navbar-inverse .navbar-form input[type=password] { + color: #fff; + -webkit-box-shadow: inset 0 -1px 0 #b2dbfb; + box-shadow: inset 0 -1px 0 #b2dbfb +} + +.navbar-inverse .navbar-form input[type=text]::-moz-placeholder, .navbar-inverse .navbar-form input[type=password]::-moz-placeholder { + color: #b2dbfb; + opacity: 1 +} + +.navbar-inverse .navbar-form input[type=text]:-ms-input-placeholder, .navbar-inverse .navbar-form input[type=password]:-ms-input-placeholder { + color: #b2dbfb +} + +.navbar-inverse .navbar-form input[type=text]::-webkit-input-placeholder, .navbar-inverse .navbar-form input[type=password]::-webkit-input-placeholder { + color: #b2dbfb +} + +.navbar-inverse .navbar-form input[type=text]:focus, .navbar-inverse .navbar-form input[type=password]:focus { + -webkit-box-shadow: inset 0 -2px 0 #fff; + box-shadow: inset 0 -2px 0 #fff +} + +.btn-default { + -webkit-background-size: 200% 200%; + background-size: 200% 200%; + background-position: 50% +} + +.btn-default:focus { + background-color: #ffffff +} + +.btn-default:hover, .btn-default:active:hover { + background-color: #f0f0f0 +} + +.btn-default:active { + background-color: #e0e0e0; + background-image: -webkit-radial-gradient(circle, #e0e0e0 10%, #fff 11%); + background-image: -o-radial-gradient(circle, #e0e0e0 10%, #fff 11%); + background-image: radial-gradient(circle, #e0e0e0 10%, #fff 11%); + background-repeat: no-repeat; + -webkit-background-size: 1000% 1000%; + background-size: 1000% 1000%; + -webkit-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4); + box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4) +} + +.btn-primary { + -webkit-background-size: 200% 200%; + background-size: 200% 200%; + background-position: 50% +} + +.btn-primary:focus { + background-color: #2196f3 +} + +.btn-primary:hover, .btn-primary:active:hover { + background-color: #0d87e9 +} + +.btn-primary:active { + background-color: #0b76cc; + background-image: -webkit-radial-gradient(circle, #0b76cc 10%, #2196f3 11%); + background-image: -o-radial-gradient(circle, #0b76cc 10%, #2196f3 11%); + background-image: radial-gradient(circle, #0b76cc 10%, #2196f3 11%); + background-repeat: no-repeat; + -webkit-background-size: 1000% 1000%; + background-size: 1000% 1000%; + -webkit-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4); + box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4) +} + +.btn-success { + -webkit-background-size: 200% 200%; + background-size: 200% 200%; + background-position: 50% +} + +.btn-success:focus { + background-color: #4caf50 +} + +.btn-success:hover, .btn-success:active:hover { + background-color: #439a46 +} + +.btn-success:active { + background-color: #39843c; + background-image: -webkit-radial-gradient(circle, #39843c 10%, #4caf50 11%); + background-image: -o-radial-gradient(circle, #39843c 10%, #4caf50 11%); + background-image: radial-gradient(circle, #39843c 10%, #4caf50 11%); + background-repeat: no-repeat; + -webkit-background-size: 1000% 1000%; + background-size: 1000% 1000%; + -webkit-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4); + box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4) +} + +.btn-info { + -webkit-background-size: 200% 200%; + background-size: 200% 200%; + background-position: 50% +} + +.btn-info:focus { + background-color: #9c27b0 +} + +.btn-info:hover, .btn-info:active:hover { + background-color: #862197 +} + +.btn-info:active { + background-color: #701c7e; + background-image: -webkit-radial-gradient(circle, #701c7e 10%, #9c27b0 11%); + background-image: -o-radial-gradient(circle, #701c7e 10%, #9c27b0 11%); + background-image: radial-gradient(circle, #701c7e 10%, #9c27b0 11%); + background-repeat: no-repeat; + -webkit-background-size: 1000% 1000%; + background-size: 1000% 1000%; + -webkit-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4); + box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4) +} + +.btn-warning { + -webkit-background-size: 200% 200%; + background-size: 200% 200%; + background-position: 50% +} + +.btn-warning:focus { + background-color: #ff9800 +} + +.btn-warning:hover, .btn-warning:active:hover { + background-color: #e08600 +} + +.btn-warning:active { + background-color: #c27400; + background-image: -webkit-radial-gradient(circle, #c27400 10%, #ff9800 11%); + background-image: -o-radial-gradient(circle, #c27400 10%, #ff9800 11%); + background-image: radial-gradient(circle, #c27400 10%, #ff9800 11%); + background-repeat: no-repeat; + -webkit-background-size: 1000% 1000%; + background-size: 1000% 1000%; + -webkit-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4); + box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4) +} + +.btn-danger { + -webkit-background-size: 200% 200%; + background-size: 200% 200%; + background-position: 50% +} + +.btn-danger:focus { + background-color: #e51c23 +} + +.btn-danger:hover, .btn-danger:active:hover { + background-color: #cb171e +} + +.btn-danger:active { + background-color: #b0141a; + background-image: -webkit-radial-gradient(circle, #b0141a 10%, #e51c23 11%); + background-image: -o-radial-gradient(circle, #b0141a 10%, #e51c23 11%); + background-image: radial-gradient(circle, #b0141a 10%, #e51c23 11%); + background-repeat: no-repeat; + -webkit-background-size: 1000% 1000%; + background-size: 1000% 1000%; + -webkit-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4); + box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4) +} + +.btn-link { + -webkit-background-size: 200% 200%; + background-size: 200% 200%; + background-position: 50% +} + +.btn-link:focus { + background-color: #ffffff +} + +.btn-link:hover, .btn-link:active:hover { + background-color: #f0f0f0 +} + +.btn-link:active { + background-color: #e0e0e0; + background-image: -webkit-radial-gradient(circle, #e0e0e0 10%, #fff 11%); + background-image: -o-radial-gradient(circle, #e0e0e0 10%, #fff 11%); + background-image: radial-gradient(circle, #e0e0e0 10%, #fff 11%); + background-repeat: no-repeat; + -webkit-background-size: 1000% 1000%; + background-size: 1000% 1000%; + -webkit-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4); + box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4) +} + +.btn { + text-transform: uppercase; + border: none; + -webkit-box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.4); + box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.4); + -webkit-transition: all 0.4s; + -o-transition: all 0.4s; + transition: all 0.4s +} + +.btn-link { + border-radius: 3px; + -webkit-box-shadow: none; + box-shadow: none; + color: #444444 +} + +.btn-link:hover, .btn-link:focus { + -webkit-box-shadow: none; + box-shadow: none; + color: #444444; + text-decoration: none +} + +.btn-default.disabled { + background-color: rgba(0, 0, 0, 0.1); + color: rgba(0, 0, 0, 0.4); + opacity: 1 +} + +.btn-group .btn + .btn, .btn-group .btn + .btn-group, .btn-group .btn-group + .btn, .btn-group .btn-group + .btn-group { + margin-left: 0 +} + +.btn-group-vertical > .btn + .btn, .btn-group-vertical > .btn + .btn-group, .btn-group-vertical > .btn-group + .btn, .btn-group-vertical > .btn-group + .btn-group { + margin-top: 0 +} + +body { + -webkit-font-smoothing: antialiased; + letter-spacing: .1px +} + +p { + margin: 0 0 1em +} + +input, button { + -webkit-font-smoothing: antialiased; + letter-spacing: .1px +} + +a { + -webkit-transition: all 0.2s; + -o-transition: all 0.2s; + transition: all 0.2s +} + +.table-hover > tbody > tr, .table-hover > tbody > tr > th, .table-hover > tbody > tr > td { + -webkit-transition: all 0.2s; + -o-transition: all 0.2s; + transition: all 0.2s +} + +label { + font-weight: normal +} + +textarea, textarea.form-control, input.form-control, input[type=text], input[type=password], input[type=email], input[type=number], [type=text].form-control, [type=password].form-control, [type=email].form-control, [type=tel].form-control, [contenteditable].form-control { + padding: 0; + border: none; + border-radius: 0; + -webkit-appearance: none; + -webkit-box-shadow: inset 0 -1px 0 #ddd; + box-shadow: inset 0 -1px 0 #ddd; + font-size: 16px +} + +textarea:focus, textarea.form-control:focus, input.form-control:focus, input[type=text]:focus, input[type=password]:focus, input[type=email]:focus, input[type=number]:focus, [type=text].form-control:focus, [type=password].form-control:focus, [type=email].form-control:focus, [type=tel].form-control:focus, [contenteditable].form-control:focus { + -webkit-box-shadow: inset 0 -2px 0 #2196f3; + box-shadow: inset 0 -2px 0 #2196f3 +} + +textarea[disabled], textarea.form-control[disabled], input.form-control[disabled], input[type=text][disabled], input[type=password][disabled], input[type=email][disabled], input[type=number][disabled], [type=text].form-control[disabled], [type=password].form-control[disabled], [type=email].form-control[disabled], [type=tel].form-control[disabled], [contenteditable].form-control[disabled], textarea[readonly], textarea.form-control[readonly], input.form-control[readonly], input[type=text][readonly], input[type=password][readonly], input[type=email][readonly], input[type=number][readonly], [type=text].form-control[readonly], [type=password].form-control[readonly], [type=email].form-control[readonly], [type=tel].form-control[readonly], [contenteditable].form-control[readonly] { + -webkit-box-shadow: none; + box-shadow: none; + border-bottom: 1px dotted #ddd +} + +textarea.input-sm, textarea.form-control.input-sm, input.form-control.input-sm, input[type=text].input-sm, input[type=password].input-sm, input[type=email].input-sm, input[type=number].input-sm, [type=text].form-control.input-sm, [type=password].form-control.input-sm, [type=email].form-control.input-sm, [type=tel].form-control.input-sm, [contenteditable].form-control.input-sm { + font-size: 12px +} + +textarea.input-lg, textarea.form-control.input-lg, input.form-control.input-lg, input[type=text].input-lg, input[type=password].input-lg, input[type=email].input-lg, input[type=number].input-lg, [type=text].form-control.input-lg, [type=password].form-control.input-lg, [type=email].form-control.input-lg, [type=tel].form-control.input-lg, [contenteditable].form-control.input-lg { + font-size: 17px +} + +select, select.form-control { + border: 0; + border-radius: 0; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + padding-left: 0; + padding-right: 0 \9; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAAAJ1BMVEVmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmaP/QSjAAAADHRSTlMAAgMJC0uWpKa6wMxMdjkoAAAANUlEQVR4AeXJyQEAERAAsNl7Hf3X6xt0QL6JpZWq30pdvdadme+0PMdzvHm8YThHcT1H7K0BtOMDniZhWOgAAAAASUVORK5CYII=); + -webkit-background-size: 13px 13px; + background-size: 13px; + background-repeat: no-repeat; + background-position: right center; + -webkit-box-shadow: inset 0 -1px 0 #ddd; + box-shadow: inset 0 -1px 0 #ddd; + font-size: 16px; + line-height: 1.5 +} + +select::-ms-expand, select.form-control::-ms-expand { + display: none +} + +select.input-sm, select.form-control.input-sm { + font-size: 12px +} + +select.input-lg, select.form-control.input-lg { + font-size: 17px +} + +select:focus, select.form-control:focus { + -webkit-box-shadow: inset 0 -2px 0 #2196f3; + box-shadow: inset 0 -2px 0 #2196f3; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAAAJ1BMVEUhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISF8S9ewAAAADHRSTlMAAgMJC0uWpKa6wMxMdjkoAAAANUlEQVR4AeXJyQEAERAAsNl7Hf3X6xt0QL6JpZWq30pdvdadme+0PMdzvHm8YThHcT1H7K0BtOMDniZhWOgAAAAASUVORK5CYII=) +} + +select[multiple], select.form-control[multiple] { + background: none +} + +.radio label, .radio-inline label, .checkbox label, .checkbox-inline label { + padding-left: 25px +} + +.radio input[type="radio"], .radio-inline input[type="radio"], .checkbox input[type="radio"], .checkbox-inline input[type="radio"], .radio input[type="checkbox"], .radio-inline input[type="checkbox"], .checkbox input[type="checkbox"], .checkbox-inline input[type="checkbox"] { + margin-left: -25px +} + +input[type="radio"], .radio input[type="radio"], .radio-inline input[type="radio"] { + position: relative; + margin-top: 6px; + margin-right: 4px; + vertical-align: top; + border: none; + background-color: transparent; + -webkit-appearance: none; + appearance: none; + cursor: pointer +} + +input[type="radio"]:focus, .radio input[type="radio"]:focus, .radio-inline input[type="radio"]:focus { + outline: none +} + +input[type="radio"]:before, .radio input[type="radio"]:before, .radio-inline input[type="radio"]:before, input[type="radio"]:after, .radio input[type="radio"]:after, .radio-inline input[type="radio"]:after { + content: ""; + display: block; + width: 18px; + height: 18px; + border-radius: 50%; + -webkit-transition: 240ms; + -o-transition: 240ms; + transition: 240ms +} + +input[type="radio"]:before, .radio input[type="radio"]:before, .radio-inline input[type="radio"]:before { + position: absolute; + left: 0; + top: -3px; + background-color: #2196f3; + -webkit-transform: scale(0); + -ms-transform: scale(0); + -o-transform: scale(0); + transform: scale(0) +} + +input[type="radio"]:after, .radio input[type="radio"]:after, .radio-inline input[type="radio"]:after { + position: relative; + top: -3px; + border: 2px solid #666666 +} + +input[type="radio"]:checked:before, .radio input[type="radio"]:checked:before, .radio-inline input[type="radio"]:checked:before { + -webkit-transform: scale(.5); + -ms-transform: scale(.5); + -o-transform: scale(.5); + transform: scale(.5) +} + +input[type="radio"]:disabled:checked:before, .radio input[type="radio"]:disabled:checked:before, .radio-inline input[type="radio"]:disabled:checked:before { + background-color: #bbbbbb +} + +input[type="radio"]:checked:after, .radio input[type="radio"]:checked:after, .radio-inline input[type="radio"]:checked:after { + border-color: #2196f3 +} + +input[type="radio"]:disabled:after, .radio input[type="radio"]:disabled:after, .radio-inline input[type="radio"]:disabled:after, input[type="radio"]:disabled:checked:after, .radio input[type="radio"]:disabled:checked:after, .radio-inline input[type="radio"]:disabled:checked:after { + border-color: #bbbbbb +} + +input[type="checkbox"], .checkbox input[type="checkbox"], .checkbox-inline input[type="checkbox"] { + position: relative; + border: none; + margin-bottom: -4px; + -webkit-appearance: none; + appearance: none; + cursor: pointer +} + +input[type="checkbox"]:focus, .checkbox input[type="checkbox"]:focus, .checkbox-inline input[type="checkbox"]:focus { + outline: none +} + +input[type="checkbox"]:focus:after, .checkbox input[type="checkbox"]:focus:after, .checkbox-inline input[type="checkbox"]:focus:after { + border-color: #2196f3 +} + +input[type="checkbox"]:after, .checkbox input[type="checkbox"]:after, .checkbox-inline input[type="checkbox"]:after { + content: ""; + display: block; + width: 18px; + height: 18px; + margin-top: -2px; + margin-right: 5px; + border: 2px solid #666666; + border-radius: 2px; + -webkit-transition: 240ms; + -o-transition: 240ms; + transition: 240ms +} + +input[type="checkbox"]:checked:before, .checkbox input[type="checkbox"]:checked:before, .checkbox-inline input[type="checkbox"]:checked:before { + content: ""; + position: absolute; + top: 0; + left: 6px; + display: table; + width: 6px; + height: 12px; + border: 2px solid #fff; + border-top-width: 0; + border-left-width: 0; + -webkit-transform: rotate(45deg); + -ms-transform: rotate(45deg); + -o-transform: rotate(45deg); + transform: rotate(45deg) +} + +input[type="checkbox"]:checked:after, .checkbox input[type="checkbox"]:checked:after, .checkbox-inline input[type="checkbox"]:checked:after { + background-color: #2196f3; + border-color: #2196f3 +} + +input[type="checkbox"]:disabled:after, .checkbox input[type="checkbox"]:disabled:after, .checkbox-inline input[type="checkbox"]:disabled:after { + border-color: #bbbbbb +} + +input[type="checkbox"]:disabled:checked:after, .checkbox input[type="checkbox"]:disabled:checked:after, .checkbox-inline input[type="checkbox"]:disabled:checked:after { + background-color: #bbbbbb; + border-color: transparent +} + +.has-warning input:not([type=checkbox]), .has-warning .form-control, .has-warning input.form-control[readonly], .has-warning input[type=text][readonly], .has-warning [type=text].form-control[readonly], .has-warning input:not([type=checkbox]):focus, .has-warning .form-control:focus { + border-bottom: none; + -webkit-box-shadow: inset 0 -2px 0 #ff9800; + box-shadow: inset 0 -2px 0 #ff9800 +} + +.has-error input:not([type=checkbox]), .has-error .form-control, .has-error input.form-control[readonly], .has-error input[type=text][readonly], .has-error [type=text].form-control[readonly], .has-error input:not([type=checkbox]):focus, .has-error .form-control:focus { + border-bottom: none; + -webkit-box-shadow: inset 0 -2px 0 #e51c23; + box-shadow: inset 0 -2px 0 #e51c23 +} + +.has-success input:not([type=checkbox]), .has-success .form-control, .has-success input.form-control[readonly], .has-success input[type=text][readonly], .has-success [type=text].form-control[readonly], .has-success input:not([type=checkbox]):focus, .has-success .form-control:focus { + border-bottom: none; + -webkit-box-shadow: inset 0 -2px 0 #4caf50; + box-shadow: inset 0 -2px 0 #4caf50 +} + +.has-warning .input-group-addon, .has-error .input-group-addon, .has-success .input-group-addon { + color: #666666; + border-color: transparent; + background-color: transparent +} + +.form-group-lg select, .form-group-lg select.form-control { + line-height: 1.5 +} + +.nav-tabs > li > a, .nav-tabs > li > a:focus { + margin-right: 0; + background-color: transparent; + border: none; + color: #666666; + -webkit-box-shadow: inset 0 -1px 0 #ddd; + box-shadow: inset 0 -1px 0 #ddd; + -webkit-transition: all 0.2s; + -o-transition: all 0.2s; + transition: all 0.2s +} + +.nav-tabs > li > a:hover, .nav-tabs > li > a:focus:hover { + background-color: transparent; + -webkit-box-shadow: inset 0 -2px 0 #2196f3; + box-shadow: inset 0 -2px 0 #2196f3; + color: #2196f3 +} + +.nav-tabs > li.active > a, .nav-tabs > li.active > a:focus { + border: none; + -webkit-box-shadow: inset 0 -2px 0 #2196f3; + box-shadow: inset 0 -2px 0 #2196f3; + color: #2196f3 +} + +.nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:focus:hover { + border: none; + color: #2196f3 +} + +.nav-tabs > li.disabled > a { + -webkit-box-shadow: inset 0 -1px 0 #ddd; + box-shadow: inset 0 -1px 0 #ddd +} + +.nav-tabs.nav-justified > li > a, .nav-tabs.nav-justified > li > a:hover, .nav-tabs.nav-justified > li > a:focus, .nav-tabs.nav-justified > .active > a, .nav-tabs.nav-justified > .active > a:hover, .nav-tabs.nav-justified > .active > a:focus { + border: none +} + +.nav-tabs .dropdown-menu { + margin-top: 0 +} + +.dropdown-menu { + margin-top: 0; + border: none; + -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) +} + +.alert { + border: none; + color: #fff +} + +.alert-success { + background-color: #4caf50 +} + +.alert-info { + background-color: #9c27b0 +} + +.alert-warning { + background-color: #ff9800 +} + +.alert-danger { + background-color: #e51c23 +} + +.alert a:not(.close):not(.btn), .alert .alert-link { + color: #fff; + font-weight: bold +} + +.alert .close { + color: #fff +} + +.badge { + padding: 4px 6px 4px +} + +.progress { + position: relative; + z-index: 1; + height: 6px; + border-radius: 0; + -webkit-box-shadow: none; + box-shadow: none +} + +.progress-bar { + -webkit-box-shadow: none; + box-shadow: none +} + +.progress-bar:last-child { + border-radius: 0 3px 3px 0 +} + +.progress-bar:last-child:before { + display: block; + content: ""; + position: absolute; + width: 100%; + height: 100%; + left: 0; + right: 0; + z-index: -1; + background-color: #cae6fc +} + +.progress-bar-success:last-child.progress-bar:before { + background-color: #c7e7c8 +} + +.progress-bar-info:last-child.progress-bar:before { + background-color: #edc9f3 +} + +.progress-bar-warning:last-child.progress-bar:before { + background-color: #ffe0b3 +} + +.progress-bar-danger:last-child.progress-bar:before { + background-color: #f28e92 +} + +.close { + font-size: 34px; + font-weight: 300; + line-height: 24px; + opacity: 0.6; + -webkit-transition: all 0.2s; + -o-transition: all 0.2s; + transition: all 0.2s +} + +.close:hover { + opacity: 1 +} + +.list-group-item { + padding: 15px +} + +.list-group-item-text { + color: #bbbbbb +} + +.well { + border-radius: 0; + -webkit-box-shadow: none; + box-shadow: none +} + +.panel { + border: none; + border-radius: 2px; + -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) +} + +.panel-heading { + border-bottom: none +} + +.panel-footer { + border-top: none +} + +.popover { + border: none; + -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) +} + +.carousel-caption h1, .carousel-caption h2, .carousel-caption h3, .carousel-caption h4, .carousel-caption h5, .carousel-caption h6 { + color: inherit +} \ No newline at end of file diff --git a/src/main/resources/assets/css/pretty-checkbox.min.css b/src/main/resources/assets/css/pretty-checkbox.min.css new file mode 100644 index 0000000..dd424f3 --- /dev/null +++ b/src/main/resources/assets/css/pretty-checkbox.min.css @@ -0,0 +1,869 @@ +/** + * pretty-checkbox.css + * + * A pure CSS library to beautify checkbox and radio buttons + * + * Source: https://github.com/lokesh-coder/pretty-checkbox + * Demo: https://lokesh-coder.github.io/pretty-checkbox + * + * Copyright (c) 2017 Lokesh rajendran + */ + +.pretty * { + box-sizing: border-box +} + +.pretty input:not([type=checkbox]):not([type=radio]) { + display: none +} + +.pretty { + position: relative; + display: inline-block; + margin-right: 1em; + white-space: nowrap; + line-height: 1 +} + +.pretty input { + position: absolute; + left: 0; + top: 0; + min-width: 1em; + width: 100%; + height: 100%; + z-index: 2; + opacity: 0; + margin: 0; + padding: 0; + cursor: pointer +} + +.pretty .state label { + position: initial; + display: inline-block; + font-weight: 400; + margin: 0; + text-indent: 1.5em; + min-width: calc(1em + 2px) +} + +.pretty .state label:after, .pretty .state label:before { + content: ''; + width: calc(1em + 2px); + height: calc(1em + 2px); + display: block; + box-sizing: border-box; + border-radius: 0; + border: 1px solid transparent; + z-index: 0; + position: absolute; + left: 0; + top: calc((0% - (100% - 1em)) - 8%); + background-color: transparent +} + +.pretty .state label:before { + border-color: #bdc3c7 +} + +.pretty .state.p-is-hover, .pretty .state.p-is-indeterminate { + display: none +} + +@-webkit-keyframes zoom { + 0% { + opacity: 0; + -webkit-transform: scale(0); + transform: scale(0) + } +} + +@keyframes zoom { + 0% { + opacity: 0; + -webkit-transform: scale(0); + transform: scale(0) + } +} + +@-webkit-keyframes tada { + 0% { + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + opacity: 0; + -webkit-transform: scale(7); + transform: scale(7) + } + 38% { + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + opacity: 1; + -webkit-transform: scale(1); + transform: scale(1) + } + 55% { + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + -webkit-transform: scale(1.5); + transform: scale(1.5) + } + 72% { + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + -webkit-transform: scale(1); + transform: scale(1) + } + 81% { + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + -webkit-transform: scale(1.24); + transform: scale(1.24) + } + 89% { + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + -webkit-transform: scale(1); + transform: scale(1) + } + 95% { + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + -webkit-transform: scale(1.04); + transform: scale(1.04) + } + 100% { + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + -webkit-transform: scale(1); + transform: scale(1) + } +} + +@keyframes tada { + 0% { + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + opacity: 0; + -webkit-transform: scale(7); + transform: scale(7) + } + 38% { + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + opacity: 1; + -webkit-transform: scale(1); + transform: scale(1) + } + 55% { + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + -webkit-transform: scale(1.5); + transform: scale(1.5) + } + 72% { + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + -webkit-transform: scale(1); + transform: scale(1) + } + 81% { + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + -webkit-transform: scale(1.24); + transform: scale(1.24) + } + 89% { + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + -webkit-transform: scale(1); + transform: scale(1) + } + 95% { + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + -webkit-transform: scale(1.04); + transform: scale(1.04) + } + 100% { + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + -webkit-transform: scale(1); + transform: scale(1) + } +} + +@-webkit-keyframes jelly { + 0% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1) + } + 30% { + -webkit-transform: scale3d(.75, 1.25, 1); + transform: scale3d(.75, 1.25, 1) + } + 40% { + -webkit-transform: scale3d(1.25, .75, 1); + transform: scale3d(1.25, .75, 1) + } + 50% { + -webkit-transform: scale3d(.85, 1.15, 1); + transform: scale3d(.85, 1.15, 1) + } + 65% { + -webkit-transform: scale3d(1.05, .95, 1); + transform: scale3d(1.05, .95, 1) + } + 75% { + -webkit-transform: scale3d(.95, 1.05, 1); + transform: scale3d(.95, 1.05, 1) + } + 100% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1) + } +} + +@keyframes jelly { + 0% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1) + } + 30% { + -webkit-transform: scale3d(.75, 1.25, 1); + transform: scale3d(.75, 1.25, 1) + } + 40% { + -webkit-transform: scale3d(1.25, .75, 1); + transform: scale3d(1.25, .75, 1) + } + 50% { + -webkit-transform: scale3d(.85, 1.15, 1); + transform: scale3d(.85, 1.15, 1) + } + 65% { + -webkit-transform: scale3d(1.05, .95, 1); + transform: scale3d(1.05, .95, 1) + } + 75% { + -webkit-transform: scale3d(.95, 1.05, 1); + transform: scale3d(.95, 1.05, 1) + } + 100% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1) + } +} + +@-webkit-keyframes rotate { + 0% { + opacity: 0; + -webkit-transform: translateZ(-200px) rotate(-45deg); + transform: translateZ(-200px) rotate(-45deg) + } + 100% { + opacity: 1; + -webkit-transform: translateZ(0) rotate(0); + transform: translateZ(0) rotate(0) + } +} + +@keyframes rotate { + 0% { + opacity: 0; + -webkit-transform: translateZ(-200px) rotate(-45deg); + transform: translateZ(-200px) rotate(-45deg) + } + 100% { + opacity: 1; + -webkit-transform: translateZ(0) rotate(0); + transform: translateZ(0) rotate(0) + } +} + +@-webkit-keyframes pulse { + 0% { + box-shadow: 0 0 0 0 #bdc3c7 + } + 100% { + box-shadow: 0 0 0 1.5em rgba(189, 195, 199, 0) + } +} + +@keyframes pulse { + 0% { + box-shadow: 0 0 0 0 #bdc3c7 + } + 100% { + box-shadow: 0 0 0 1.5em rgba(189, 195, 199, 0) + } +} + +.pretty.p-default.p-fill .state label:after { + -webkit-transform: scale(1); + -ms-transform: scale(1); + transform: scale(1) +} + +.pretty.p-default .state label:after { + -webkit-transform: scale(.6); + -ms-transform: scale(.6); + transform: scale(.6) +} + +.pretty.p-default input:checked ~ .state label:after { + background-color: #bdc3c7 !important +} + +.pretty.p-default.p-thick .state label:after, .pretty.p-default.p-thick .state label:before { + border-width: calc(1em / 7) +} + +.pretty.p-default.p-thick .state label:after { + -webkit-transform: scale(.4) !important; + -ms-transform: scale(.4) !important; + transform: scale(.4) !important +} + +.pretty.p-icon .state .icon { + position: absolute; + font-size: 1em; + width: calc(1em + 2px); + height: calc(1em + 2px); + left: 0; + z-index: 1; + text-align: center; + line-height: normal; + top: calc((0% - (100% - 1em)) - 8%); + border: 1px solid transparent; + opacity: 0 +} + +.pretty.p-icon .state .icon:before { + margin: 0; + width: 100%; + height: 100%; + text-align: center; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + line-height: 1 +} + +.pretty.p-icon input:checked ~ .state .icon { + opacity: 1 +} + +.pretty.p-icon input:checked ~ .state label:before { + border-color: #5a656b +} + +.pretty.p-svg .state .svg { + position: absolute; + font-size: 1em; + width: calc(1em + 2px); + height: calc(1em + 2px); + left: 0; + z-index: 1; + text-align: center; + line-height: normal; + top: calc((0% - (100% - 1em)) - 8%); + border: 1px solid transparent; + opacity: 0 +} + +.pretty.p-svg .state svg { + margin: 0; + width: 100%; + height: 100%; + text-align: center; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + line-height: 1 +} + +.pretty.p-svg input:checked ~ .state .svg { + opacity: 1 +} + +.pretty.p-image .state img { + opacity: 0; + position: absolute; + width: calc(1em + 2px); + height: calc(1em + 2px); + top: 0; + top: calc((0% - (100% - 1em)) - 8%); + left: 0; + z-index: 0; + text-align: center; + line-height: normal; + -webkit-transform: scale(.8); + -ms-transform: scale(.8); + transform: scale(.8) +} + +.pretty.p-image input:checked ~ .state img { + opacity: 1 +} + +.pretty.p-switch input { + min-width: 2em +} + +.pretty.p-switch .state { + position: relative +} + +.pretty.p-switch .state:before { + content: ''; + border: 1px solid #bdc3c7; + border-radius: 60px; + width: 2em; + box-sizing: unset; + height: calc(1em + 2px); + position: absolute; + top: 0; + top: calc((0% - (100% - 1em)) - 16%); + z-index: 0; + transition: all .5s ease +} + +.pretty.p-switch .state label { + text-indent: 2.5em +} + +.pretty.p-switch .state label:after, .pretty.p-switch .state label:before { + transition: all .5s ease; + border-radius: 100%; + left: 0; + border-color: transparent; + -webkit-transform: scale(.8); + -ms-transform: scale(.8); + transform: scale(.8) +} + +.pretty.p-switch .state label:after { + background-color: #bdc3c7 !important +} + +.pretty.p-switch input:checked ~ .state:before { + border-color: #5a656b +} + +.pretty.p-switch input:checked ~ .state label:before { + opacity: 0 +} + +.pretty.p-switch input:checked ~ .state label:after { + background-color: #5a656b !important; + left: 1em +} + +.pretty.p-switch.p-fill input:checked ~ .state:before { + border-color: #5a656b; + background-color: #5a656b !important +} + +.pretty.p-switch.p-fill input:checked ~ .state label:before { + opacity: 0 +} + +.pretty.p-switch.p-fill input:checked ~ .state label:after { + background-color: #fff !important; + left: 1em +} + +.pretty.p-switch.p-slim .state:before { + height: .1em; + background: #bdc3c7 !important; + top: calc(50% - .1em) +} + +.pretty.p-switch.p-slim input:checked ~ .state:before { + border-color: #5a656b; + background-color: #5a656b !important +} + +.pretty.p-has-hover input:hover ~ .state:not(.p-is-hover) { + display: none +} + +.pretty.p-has-hover input:hover ~ .state.p-is-hover { + display: block +} + +.pretty.p-has-hover input:hover ~ .state.p-is-hover .icon { + display: block +} + +.pretty.p-has-focus input:focus ~ .state label:before { + box-shadow: 0 0 3px 0 #bdc3c7 +} + +.pretty.p-has-indeterminate input[type=checkbox]:indeterminate ~ .state:not(.p-is-indeterminate) { + display: none +} + +.pretty.p-has-indeterminate input[type=checkbox]:indeterminate ~ .state.p-is-indeterminate { + display: block +} + +.pretty.p-has-indeterminate input[type=checkbox]:indeterminate ~ .state.p-is-indeterminate .icon { + display: block; + opacity: 1 +} + +.pretty.p-toggle .state.p-on { + opacity: 0; + display: none +} + +.pretty.p-toggle .state .icon, .pretty.p-toggle .state .svg, .pretty.p-toggle .state img, .pretty.p-toggle .state.p-off { + opacity: 1; + display: inherit +} + +.pretty.p-toggle .state.p-off .icon { + color: #bdc3c7 +} + +.pretty.p-toggle input:checked ~ .state.p-on { + opacity: 1; + display: inherit +} + +.pretty.p-toggle input:checked ~ .state.p-off { + opacity: 0; + display: none +} + +.pretty.p-plain input:checked ~ .state label:before, .pretty.p-plain.p-toggle .state label:before { + content: none +} + +.pretty.p-plain.p-plain .icon { + -webkit-transform: scale(1.1); + -ms-transform: scale(1.1); + transform: scale(1.1) +} + +.pretty.p-round .state label:after, .pretty.p-round .state label:before { + border-radius: 100% +} + +.pretty.p-round.p-icon .state .icon { + border-radius: 100%; + overflow: hidden +} + +.pretty.p-round.p-icon .state .icon:before { + -webkit-transform: scale(.8); + -ms-transform: scale(.8); + transform: scale(.8) +} + +.pretty.p-curve .state label:after, .pretty.p-curve .state label:before { + border-radius: 20% +} + +.pretty.p-smooth .icon, .pretty.p-smooth .svg, .pretty.p-smooth label:after, .pretty.p-smooth label:before { + transition: all .5s ease +} + +.pretty.p-smooth input:checked + .state label:after { + transition: all .3s ease +} + +.pretty.p-smooth input:checked + .state .icon, .pretty.p-smooth input:checked + .state .svg, .pretty.p-smooth input:checked + .state img { + -webkit-animation: zoom .2s ease; + animation: zoom .2s ease +} + +.pretty.p-smooth.p-default input:checked + .state label:after { + -webkit-animation: zoom .2s ease; + animation: zoom .2s ease +} + +.pretty.p-smooth.p-plain input:checked + .state label:before { + content: ''; + -webkit-transform: scale(0); + -ms-transform: scale(0); + transform: scale(0); + transition: all .5s ease +} + +.pretty.p-tada:not(.p-default) input:checked + .state .icon, .pretty.p-tada:not(.p-default) input:checked + .state .svg, .pretty.p-tada:not(.p-default) input:checked + .state img, .pretty.p-tada:not(.p-default) input:checked + .state label:after, .pretty.p-tada:not(.p-default) input:checked + .state label:before { + -webkit-animation: tada .7s cubic-bezier(.25, .46, .45, .94) 1 alternate; + animation: tada .7s cubic-bezier(.25, .46, .45, .94) 1 alternate; + opacity: 1 +} + +.pretty.p-jelly:not(.p-default) input:checked + .state .icon, .pretty.p-jelly:not(.p-default) input:checked + .state .svg, .pretty.p-jelly:not(.p-default) input:checked + .state img, .pretty.p-jelly:not(.p-default) input:checked + .state label:after, .pretty.p-jelly:not(.p-default) input:checked + .state label:before { + -webkit-animation: jelly .7s cubic-bezier(.25, .46, .45, .94); + animation: jelly .7s cubic-bezier(.25, .46, .45, .94); + opacity: 1 +} + +.pretty.p-jelly:not(.p-default) input:checked + .state label:before { + border-color: transparent +} + +.pretty.p-rotate:not(.p-default) input:checked ~ .state .icon, .pretty.p-rotate:not(.p-default) input:checked ~ .state .svg, .pretty.p-rotate:not(.p-default) input:checked ~ .state img, .pretty.p-rotate:not(.p-default) input:checked ~ .state label:after, .pretty.p-rotate:not(.p-default) input:checked ~ .state label:before { + -webkit-animation: rotate .7s cubic-bezier(.25, .46, .45, .94); + animation: rotate .7s cubic-bezier(.25, .46, .45, .94); + opacity: 1 +} + +.pretty.p-rotate:not(.p-default) input:checked ~ .state label:before { + border-color: transparent +} + +.pretty.p-pulse:not(.p-switch) input:checked ~ .state label:before { + -webkit-animation: pulse 1s; + animation: pulse 1s +} + +.pretty input[disabled] { + cursor: not-allowed; + display: none +} + +.pretty input[disabled] ~ * { + opacity: .5 +} + +.pretty.p-locked input { + display: none; + cursor: not-allowed +} + +.pretty input:checked ~ .state.p-primary label:after, .pretty.p-toggle .state.p-primary label:after { + background-color: #428bca !important +} + +.pretty input:checked ~ .state.p-primary .icon, .pretty input:checked ~ .state.p-primary .svg, .pretty.p-toggle .state.p-primary .icon, .pretty.p-toggle .state.p-primary .svg { + color: #fff; + stroke: #fff +} + +.pretty input:checked ~ .state.p-primary-o label:before, .pretty.p-toggle .state.p-primary-o label:before { + border-color: #428bca +} + +.pretty input:checked ~ .state.p-primary-o label:after, .pretty.p-toggle .state.p-primary-o label:after { + background-color: transparent +} + +.pretty input:checked ~ .state.p-primary-o .icon, .pretty input:checked ~ .state.p-primary-o .svg, .pretty input:checked ~ .state.p-primary-o svg, .pretty.p-toggle .state.p-primary-o .icon, .pretty.p-toggle .state.p-primary-o .svg, .pretty.p-toggle .state.p-primary-o svg { + color: #428bca; + stroke: #428bca +} + +.pretty.p-default:not(.p-fill) input:checked ~ .state.p-primary-o label:after { + background-color: #428bca !important +} + +.pretty.p-switch input:checked ~ .state.p-primary:before { + border-color: #428bca +} + +.pretty.p-switch.p-fill input:checked ~ .state.p-primary:before { + background-color: #428bca !important +} + +.pretty.p-switch.p-slim input:checked ~ .state.p-primary:before { + border-color: #245682; + background-color: #245682 !important +} + +.pretty input:checked ~ .state.p-info label:after, .pretty.p-toggle .state.p-info label:after { + background-color: #5bc0de !important +} + +.pretty input:checked ~ .state.p-info .icon, .pretty input:checked ~ .state.p-info .svg, .pretty.p-toggle .state.p-info .icon, .pretty.p-toggle .state.p-info .svg { + color: #fff; + stroke: #fff +} + +.pretty input:checked ~ .state.p-info-o label:before, .pretty.p-toggle .state.p-info-o label:before { + border-color: #5bc0de +} + +.pretty input:checked ~ .state.p-info-o label:after, .pretty.p-toggle .state.p-info-o label:after { + background-color: transparent +} + +.pretty input:checked ~ .state.p-info-o .icon, .pretty input:checked ~ .state.p-info-o .svg, .pretty input:checked ~ .state.p-info-o svg, .pretty.p-toggle .state.p-info-o .icon, .pretty.p-toggle .state.p-info-o .svg, .pretty.p-toggle .state.p-info-o svg { + color: #5bc0de; + stroke: #5bc0de +} + +.pretty.p-default:not(.p-fill) input:checked ~ .state.p-info-o label:after { + background-color: #5bc0de !important +} + +.pretty.p-switch input:checked ~ .state.p-info:before { + border-color: #5bc0de +} + +.pretty.p-switch.p-fill input:checked ~ .state.p-info:before { + background-color: #5bc0de !important +} + +.pretty.p-switch.p-slim input:checked ~ .state.p-info:before { + border-color: #2390b0; + background-color: #2390b0 !important +} + +.pretty input:checked ~ .state.p-success label:after, .pretty.p-toggle .state.p-success label:after { + background-color: #5cb85c !important +} + +.pretty input:checked ~ .state.p-success .icon, .pretty input:checked ~ .state.p-success .svg, .pretty.p-toggle .state.p-success .icon, .pretty.p-toggle .state.p-success .svg { + color: #fff; + stroke: #fff +} + +.pretty input:checked ~ .state.p-success-o label:before, .pretty.p-toggle .state.p-success-o label:before { + border-color: #5cb85c +} + +.pretty input:checked ~ .state.p-success-o label:after, .pretty.p-toggle .state.p-success-o label:after { + background-color: transparent +} + +.pretty input:checked ~ .state.p-success-o .icon, .pretty input:checked ~ .state.p-success-o .svg, .pretty input:checked ~ .state.p-success-o svg, .pretty.p-toggle .state.p-success-o .icon, .pretty.p-toggle .state.p-success-o .svg, .pretty.p-toggle .state.p-success-o svg { + color: #5cb85c; + stroke: #5cb85c +} + +.pretty.p-default:not(.p-fill) input:checked ~ .state.p-success-o label:after { + background-color: #5cb85c !important +} + +.pretty.p-switch input:checked ~ .state.p-success:before { + border-color: #5cb85c +} + +.pretty.p-switch.p-fill input:checked ~ .state.p-success:before { + background-color: #5cb85c !important +} + +.pretty.p-switch.p-slim input:checked ~ .state.p-success:before { + border-color: #357935; + background-color: #357935 !important +} + +.pretty input:checked ~ .state.p-warning label:after, .pretty.p-toggle .state.p-warning label:after { + background-color: #f0ad4e !important +} + +.pretty input:checked ~ .state.p-warning .icon, .pretty input:checked ~ .state.p-warning .svg, .pretty.p-toggle .state.p-warning .icon, .pretty.p-toggle .state.p-warning .svg { + color: #fff; + stroke: #fff +} + +.pretty input:checked ~ .state.p-warning-o label:before, .pretty.p-toggle .state.p-warning-o label:before { + border-color: #f0ad4e +} + +.pretty input:checked ~ .state.p-warning-o label:after, .pretty.p-toggle .state.p-warning-o label:after { + background-color: transparent +} + +.pretty input:checked ~ .state.p-warning-o .icon, .pretty input:checked ~ .state.p-warning-o .svg, .pretty input:checked ~ .state.p-warning-o svg, .pretty.p-toggle .state.p-warning-o .icon, .pretty.p-toggle .state.p-warning-o .svg, .pretty.p-toggle .state.p-warning-o svg { + color: #f0ad4e; + stroke: #f0ad4e +} + +.pretty.p-default:not(.p-fill) input:checked ~ .state.p-warning-o label:after { + background-color: #f0ad4e !important +} + +.pretty.p-switch input:checked ~ .state.p-warning:before { + border-color: #f0ad4e +} + +.pretty.p-switch.p-fill input:checked ~ .state.p-warning:before { + background-color: #f0ad4e !important +} + +.pretty.p-switch.p-slim input:checked ~ .state.p-warning:before { + border-color: #c77c11; + background-color: #c77c11 !important +} + +.pretty input:checked ~ .state.p-danger label:after, .pretty.p-toggle .state.p-danger label:after { + background-color: #d9534f !important +} + +.pretty input:checked ~ .state.p-danger .icon, .pretty input:checked ~ .state.p-danger .svg, .pretty.p-toggle .state.p-danger .icon, .pretty.p-toggle .state.p-danger .svg { + color: #fff; + stroke: #fff +} + +.pretty input:checked ~ .state.p-danger-o label:before, .pretty.p-toggle .state.p-danger-o label:before { + border-color: #d9534f +} + +.pretty input:checked ~ .state.p-danger-o label:after, .pretty.p-toggle .state.p-danger-o label:after { + background-color: transparent +} + +.pretty input:checked ~ .state.p-danger-o .icon, .pretty input:checked ~ .state.p-danger-o .svg, .pretty input:checked ~ .state.p-danger-o svg, .pretty.p-toggle .state.p-danger-o .icon, .pretty.p-toggle .state.p-danger-o .svg, .pretty.p-toggle .state.p-danger-o svg { + color: #d9534f; + stroke: #d9534f +} + +.pretty.p-default:not(.p-fill) input:checked ~ .state.p-danger-o label:after { + background-color: #d9534f !important +} + +.pretty.p-switch input:checked ~ .state.p-danger:before { + border-color: #d9534f +} + +.pretty.p-switch.p-fill input:checked ~ .state.p-danger:before { + background-color: #d9534f !important +} + +.pretty.p-switch.p-slim input:checked ~ .state.p-danger:before { + border-color: #a02622; + background-color: #a02622 !important +} + +.pretty.p-bigger .icon, .pretty.p-bigger .img, .pretty.p-bigger .svg, .pretty.p-bigger label:after, .pretty.p-bigger label:before { + font-size: 1.2em !important; + top: calc((0% - (100% - 1em)) - 35%) !important +} + +.pretty.p-bigger label { + text-indent: 1.7em +} + +@media print { + .pretty .state .icon, .pretty .state label:after, .pretty .state label:before, .pretty .state:before { + color-adjust: exact; + -webkit-print-color-adjust: exact; + print-color-adjust: exact + } +} \ No newline at end of file diff --git a/src/main/resources/assets/css/style.css b/src/main/resources/assets/css/style.css new file mode 100644 index 0000000..7e19cf6 --- /dev/null +++ b/src/main/resources/assets/css/style.css @@ -0,0 +1,94 @@ +html, body { + background-size: cover; + margin: 0; + padding: 0; + background: #F5F5F5; + min-height: 100vh; +} + +.avatar { + height: 4rem; + width: 4rem; +} + +.content-box { + background: white; + z-index: 1; +} + +.center-vertical { + position: absolute; + top: 1rem; + left: 0; + right: 0; + bottom: 3rem; + height: auto; + margin: auto; +} + +.padding-0 { + padding: 0; +} + +.padding-top-0 { + padding-top: 0; +} + +.padding-top-1 { + padding-top: 1%; +} + +.padding-top-3 { + padding-top: 3%; +} + +.padding-right-0 { + padding-right: 0; +} + +.bottom { + position: relative; + left: 0; + right: 0; + bottom: 4rem; + height: auto; + margin: auto; +} + +.text-justify-all { + text-align: justify; + text-align-last: justify; +} + +.show { + display: block; +} + +.hide { + display: none; +} + +input[type=checkbox] { + border: 1px solid #b4b9be; + background: #fff; + color: #555; + clear: none; + cursor: pointer; + display: inline-block; + line-height: 0; + height: 16px; + margin: -4px 4px 0 0; + outline: 0; + text-align: center; + vertical-align: middle; + box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); + transition: .05s border-color ease-in-out; +} + +.font-1 { + font-size: 1rem; +} + +.width-60-vm { + width: 60vw; +} \ No newline at end of file diff --git a/src/main/resources/assets/fonts/fa-brands-400.eot b/src/main/resources/assets/fonts/fa-brands-400.eot new file mode 100644 index 0000000..d840454 Binary files /dev/null and b/src/main/resources/assets/fonts/fa-brands-400.eot differ diff --git a/src/main/resources/assets/fonts/fa-brands-400.svg b/src/main/resources/assets/fonts/fa-brands-400.svg new file mode 100644 index 0000000..e1e41cc --- /dev/null +++ b/src/main/resources/assets/fonts/fa-brands-400.svg @@ -0,0 +1,1008 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/assets/fonts/fa-brands-400.ttf b/src/main/resources/assets/fonts/fa-brands-400.ttf new file mode 100644 index 0000000..12719a1 Binary files /dev/null and b/src/main/resources/assets/fonts/fa-brands-400.ttf differ diff --git a/src/main/resources/assets/fonts/fa-brands-400.woff b/src/main/resources/assets/fonts/fa-brands-400.woff new file mode 100644 index 0000000..721dbcc Binary files /dev/null and b/src/main/resources/assets/fonts/fa-brands-400.woff differ diff --git a/src/main/resources/assets/fonts/fa-brands-400.woff2 b/src/main/resources/assets/fonts/fa-brands-400.woff2 new file mode 100644 index 0000000..8ae415c Binary files /dev/null and b/src/main/resources/assets/fonts/fa-brands-400.woff2 differ diff --git a/src/main/resources/assets/fonts/fa-regular-400.eot b/src/main/resources/assets/fonts/fa-regular-400.eot new file mode 100644 index 0000000..f35e3cf Binary files /dev/null and b/src/main/resources/assets/fonts/fa-regular-400.eot differ diff --git a/src/main/resources/assets/fonts/fa-regular-400.svg b/src/main/resources/assets/fonts/fa-regular-400.svg new file mode 100644 index 0000000..2a24561 --- /dev/null +++ b/src/main/resources/assets/fonts/fa-regular-400.svg @@ -0,0 +1,366 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/assets/fonts/fa-regular-400.ttf b/src/main/resources/assets/fonts/fa-regular-400.ttf new file mode 100644 index 0000000..8d66ab0 Binary files /dev/null and b/src/main/resources/assets/fonts/fa-regular-400.ttf differ diff --git a/src/main/resources/assets/fonts/fa-regular-400.woff b/src/main/resources/assets/fonts/fa-regular-400.woff new file mode 100644 index 0000000..8449c2a Binary files /dev/null and b/src/main/resources/assets/fonts/fa-regular-400.woff differ diff --git a/src/main/resources/assets/fonts/fa-regular-400.woff2 b/src/main/resources/assets/fonts/fa-regular-400.woff2 new file mode 100644 index 0000000..2fd6764 Binary files /dev/null and b/src/main/resources/assets/fonts/fa-regular-400.woff2 differ diff --git a/src/main/resources/assets/fonts/fa-solid-900.eot b/src/main/resources/assets/fonts/fa-solid-900.eot new file mode 100644 index 0000000..98d4dd5 Binary files /dev/null and b/src/main/resources/assets/fonts/fa-solid-900.eot differ diff --git a/src/main/resources/assets/fonts/fa-solid-900.svg b/src/main/resources/assets/fonts/fa-solid-900.svg new file mode 100644 index 0000000..a7246b4 --- /dev/null +++ b/src/main/resources/assets/fonts/fa-solid-900.svg @@ -0,0 +1,1467 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/assets/fonts/fa-solid-900.ttf b/src/main/resources/assets/fonts/fa-solid-900.ttf new file mode 100644 index 0000000..f786c42 Binary files /dev/null and b/src/main/resources/assets/fonts/fa-solid-900.ttf differ diff --git a/src/main/resources/assets/fonts/fa-solid-900.woff b/src/main/resources/assets/fonts/fa-solid-900.woff new file mode 100644 index 0000000..8cef46c Binary files /dev/null and b/src/main/resources/assets/fonts/fa-solid-900.woff differ diff --git a/src/main/resources/assets/fonts/fa-solid-900.woff2 b/src/main/resources/assets/fonts/fa-solid-900.woff2 new file mode 100644 index 0000000..2cad2c7 Binary files /dev/null and b/src/main/resources/assets/fonts/fa-solid-900.woff2 differ diff --git a/src/main/resources/assets/fonts/glyphicons-halflings-regular.eot b/src/main/resources/assets/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 0000000..b93a495 Binary files /dev/null and b/src/main/resources/assets/fonts/glyphicons-halflings-regular.eot differ diff --git a/src/main/resources/assets/fonts/glyphicons-halflings-regular.svg b/src/main/resources/assets/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 0000000..94fb549 --- /dev/null +++ b/src/main/resources/assets/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/assets/fonts/glyphicons-halflings-regular.ttf b/src/main/resources/assets/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 0000000..1413fc6 Binary files /dev/null and b/src/main/resources/assets/fonts/glyphicons-halflings-regular.ttf differ diff --git a/src/main/resources/assets/fonts/glyphicons-halflings-regular.woff b/src/main/resources/assets/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 0000000..9e61285 Binary files /dev/null and b/src/main/resources/assets/fonts/glyphicons-halflings-regular.woff differ diff --git a/src/main/resources/assets/fonts/glyphicons-halflings-regular.woff2 b/src/main/resources/assets/fonts/glyphicons-halflings-regular.woff2 new file mode 100644 index 0000000..64539b5 Binary files /dev/null and b/src/main/resources/assets/fonts/glyphicons-halflings-regular.woff2 differ diff --git a/src/main/resources/assets/fonts/glyphicons-regular.eot b/src/main/resources/assets/fonts/glyphicons-regular.eot new file mode 100644 index 0000000..c73cdd8 Binary files /dev/null and b/src/main/resources/assets/fonts/glyphicons-regular.eot differ diff --git a/src/main/resources/assets/fonts/glyphicons-regular.svg b/src/main/resources/assets/fonts/glyphicons-regular.svg new file mode 100644 index 0000000..d84cf19 --- /dev/null +++ b/src/main/resources/assets/fonts/glyphicons-regular.svg @@ -0,0 +1,435 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/assets/fonts/glyphicons-regular.ttf b/src/main/resources/assets/fonts/glyphicons-regular.ttf new file mode 100644 index 0000000..42d0591 Binary files /dev/null and b/src/main/resources/assets/fonts/glyphicons-regular.ttf differ diff --git a/src/main/resources/assets/fonts/glyphicons-regular.woff b/src/main/resources/assets/fonts/glyphicons-regular.woff new file mode 100644 index 0000000..6d892ed Binary files /dev/null and b/src/main/resources/assets/fonts/glyphicons-regular.woff differ diff --git a/src/main/resources/assets/fonts/iconfont.css b/src/main/resources/assets/fonts/iconfont.css new file mode 100644 index 0000000..788d4e4 --- /dev/null +++ b/src/main/resources/assets/fonts/iconfont.css @@ -0,0 +1,81 @@ + +@font-face {font-family: "my-icon"; + src: url('iconfont.eot?t=1507649649871'); /* IE9*/ + src: url('iconfont.eot?t=1507649649871#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAACLYAAsAAAAAMiAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFZXRkpUY21hcAAAAYAAAAFoAAAEAG/NFElnbHlmAAAC6AAAHJ4AACdACFxvGWhlYWQAAB+IAAAALwAAADYPJVaKaGhlYQAAH7gAAAAdAAAAJAfeA6dobXR4AAAf2AAAABsAAACIh+8AAGxvY2EAAB/0AAAARgAAAEavZqQ+bWF4cAAAIDwAAAAfAAAAIAE7Ah9uYW1lAAAgXAAAAUkAAAJhOV4a03Bvc3QAACGoAAABMAAAAdaC0WjfeJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2BkYWCcwMDKwMHUyXSGgYGhH0IzvmYwYuRgYGBiYGVmwAoC0lxTGBwYKl7EMzf8b2CIYW5muAEUZgTJAQDdpwwxeJzFkz1OAmEQhp8VxB8QBRURRUFKqayMlTEegAMQbWy8gCH0tEBFLDyGJb2lt3jlFjjDEAzGypj4TZ7NfvPt7M7Pu8AqkDKaRhpWHkjsjuTOvMnMn2Jz5k8nF7a/pGbPlekIpZVVXkWVVFZFVTXUVEttPepJXfU01EjPetVYb3r/6E/up1N7w1dkYRFZ0/lS5ODHyN+sxDL+squFXXOzZLffzCM32WXNaj3ihEMy1qsKOU6tA3W2WbE+7HFAliL7FChxzDp5quywwZZ3Ncn8Mus/WMn/fXp55fySvMx3daMzx1IUgWtM6cC6jrKB9R/lA5sEKgQ2E1QMbDqoFNicUDmwiaFK4DpXNcB9tcCzUz2wyaKzAD9vBJ6zzgObO2oGpgDUCvzfUTswVaDHwPSBngK8pm6A594LTD1oEOD1DQO8plFg2kLPgakMvQZ4b8YBXt9bYBpE74GpkY9+YLpkch+Q+gRHTaMQeJyFeg2YFNWZbp1zqs6p/+ru6qrq7un/nqmCGaZn+h9moAdwEBAEBfnTKAREERD8wQUUZRZioq6ixvhc1Bvjb2RVjLqb+MQEje5jdiHJ8+j1Xvfimk12NY93byR5NLvG3Qvt/U71QDC7e29Pd/31OV+d89X7vd/7nR5BEoTP/4H8kCQEW5giVIRx4QJBQHQAFU2cQYWgXsYDyClIjhc3SVAKCqxULJNZyCvSuFtt1n2PMmohE2VRrVBtBmUcoEa9jUdR1c0glOxJLY/1pWPkHqQmguytnfPwY8jJldJWe7CzcNpYvJq35Z16LJaMxe6UqSTJGIuWia72XEVSVNp5QrJSzg9zU3EO6ckgtXiNke+Jffm2+rZMn6cgNDGB7J68+dRYNBWF956Ua8eSLGLIiZRR6o2jnR9oCVvP+O8L8BJhrlPIZzBXXThfWCYs5zMVmCt4TaHlCwEqo6CMxnCz1RxD1WYLTlvNRr2MKMsiz0SwrcFBFjmeS1l47kEb6Awm4FTyA9erYOGdzgeUovQ776A0pZ0P3kFTMtck4wjFRZFGdAPFELyJeNmL12BwXPcUpQyZxjFykn+SwpdeePBVIiHpjTeQJKHVSzq3RyORKElwY2cbP/mEnjHia2k05Vq5Yq9jx+NoajBl6TK05KIpSzCKx21HXJA0yyRK18aNrEHayf6Pn+RWpc7/eWPrS3MuJ6JI/uCbvyDTBE3whEBogW9gbm3UCqdOurOF0zIiJeoHDTjkznBgAvCdk4VTt9lqQB+MdjxIyIM7utvLb8X41ss33ErIrdKRe18RxR/dfeSeV0Xx9c739j5N0MN3Hdn1gkienkBRcvD66w4ScvC66w+iT8i+9Rv2EbJvw/p9J6eS4oWxA0dE8ciB2IVFdJg8dUuslsTYa8RueQpGzzF8L/k7/LRABCbEhYywGS4WGgWv4BRsE3GgtupBi/msjOrNqhunfX4QPuygBIOHN4DYazVaAfODWqPkt7xW0IIZwg62Tq0BM2UeuCKHYOeyRsmpURYwrwJ/TbQI7erclkMjnR8f8iuVRRWEvv8r20vmcpVcDn0Ht/PrZla31MX6lurMdfk2xgvTdasyTZ3ZlqbVrbqYmgvAKVQ3DeeGNg4X3CAYT5GaWR+U2jPVaU1rhh5ZuP2G7HR0E6qcV632UenBNdo9KD+ch/eWmy6tLu1xEwgl3J6l1UtvuuMpA2V70lsuzW1bme5Jy/5ML3DziRQeGcHJRMEJvJmBBF+s2o6za7dle/KUPQXPH9z4+ZXkbvxzoSyMCjPBfyYq+sEsNOkjCOY4LfqNUqPeClEwCY14GBuuQ+DKGYxUyN0onosj+8W98s0vuNjJOQhFbr1idB4iD+3c+RCeN7rx1gj+Adl18SW7MN51ycW7yPWReDxCxBsPiuL9OyWJRBwnQiRx+5NT/D2Ld32LkG/tWrzHD57YJjFcm+y1G+PdFwuCAmOfQU4QK8RuSVgiXCV8VbhPeER4FFAMsQlBDuHdFOwsaqMhmJKJPCmLmvXucQ75QGEwgUaNM9oACup+kTKvxDluFDlhzAMTtJp+IDX8AFpMfhN3a9UakEUberMW6/YP2OS3jLNi98vS2QEURM+KLFbB+E3kAFH8+s03O7+GeHfOJ7op4Ui4RZpkamIcNkiaQ01RipTQsKxp8jBSLCDALbKqyltIS5diMo6KhHb+FWki9XmTlMowuNCjWDHobTmNX5vB2+uGrCJdXiIrSJP9218SxZdu7267ju5uiSV1fv3W5JjefAs5nZ0yFU3Wz5hkyC5FiknjYNukn4oik2OrlzDVVNv8Bm04kK/cSiUGfJ5kJtG/aYhEjajPwI3FCJ+NaJN14Qyg6ZcR0kwFUaRaMnpO/N4dt79EyEu33/E98dRj5JGd3eHsfIRM4vRH5EkyW5gmjABSBZQlTtzExTLmaafVzCJOzU48i6ttuMTjvI08AtCN8tB3onFaquCP++cOJeZe98BzD1w/xxuYPSU+nGXx0bGhZdv2XL1seHxWXElXnI+TpVK1VEriI9nFqzdUVz9w7TnnXPvA6sq6VYuylhxUrIFbFrS3LRseXratvXDfgFmZolidb5cSaGOitzfReShREgQM432aPENWQs6pwmirTRgHoAvG4doVDpRapYuDLiYqHEGlyllowYKqaXFNW6vourL/SSIef/zx4yJ5cj8/v/8VIn383e9+LJFX0ICmhm3VDmzRT42Ylv/Jnz12XJKOP3bX0TyYKLz/8F9+Ioqf/OXD7wsWJ33yDn5H6IeIrwpN8OY5wnxhkbAU8uIq4RLhcmGTsFUQ+io2eLFUAD+ehr3tehSCIiTVmolOHzZJjZX6al7JC2oe4/vTbNsqcVbtfuxSo9aa/Nh/aNCqBa0aIxMrNPXU8izS2GN8Jvd+RVQMVFkwjIwTkoyGF8KBftHN9vUd4S55oi9xo7xuo7yqEksmexOJg6uUdVco26+Xb9q3ah29Yrt4/S2D3W8yq5V1Fyvb4z9EE6KqXctkcEbHUomd6h0e7vXiqqvxg5QtohtXrlw5sXJl/8oJlICevYm9cNy/svN0d9d/1tWVwiQeXyU/AjzOAU2xBvzFtVPgz0It12sBF4CvWLMFU++ehIkDuAVIg+9dLxQbTtwDv1ZbTWhEv5Brz2IK/GMxPxin0jmWVRw8L53pWXP/WPmTnyftaNGMvn9oYMhO2kmziROFdZZyuZ8mKw2zd+ZMJpFkFB995ZQknXqlu33xI1H86MUXPpKkj95LKtQ9gG07MXJePF/AZFaz18x7djzbWx+M0qkUkZl9zawdKc4aXEK+UTQigz1FmQ3gpeKpV149KUknX33l1MnHT5t74cWPBEC68PlXyc/INUJdaAsLQFGuFrYJu4RbQ1b+a+FvQm0xgLhYoIxvgW2BI7mO8HkWcUrdAPFqXT+4Ndg0KqTqeg4koGYt4OKyBBqkxjiA4Cp4KbxGIMpBiMKl0KAF2hVCn5N7weVRhsK01uCtvYqDzmZljyMRxC7Pc7NQqejXm6OoEUweBH4buTkEyS8LVmuTbWvkpyifXIuqftZFtT6319D73L4qJAy/gq5KFgrJq+if713/X4qJdHUO/gTyyOase5eb24wy3uvZQTg9gDLl7ObHDZUUVB1F+hPVuQidukc10HPb7pfRBzif/dpK9CgqzytjVD63jDorFm3AeMOi8zZivPG8C5mqMqBbZQ7n3TloNt9NCUSimWK9IRo6Eaf4YZs23pDID/WBOImqfcPIjkRsNNynRhGq9A3lE539iTy6+SmCtlyio7HhreiXGXdw0M0szbh79riZmpuBg0G+BeGObubk83EkimYPS4t1pfNX5O6tg4ULLsNILkzrz28vlculregJvHFRd7CLNuL/oVpqDx9JDxyUgOrVEWrKhC5eTIls0hG40NuNp9fJTjIL6pEScBJHylAo0UpRLr+4+Ij2wTOLUjFo0N4gCleIV3GrYwhICMLHQqXA54hyyeVrnztfUc5/bq1Njn5bEp88Sjp3LHx8td/5FyfW+dSXVj1y70FlivKBgSJ6TD6O75KjuoUQ/oE4u0Vpa7bY+dtDp/oR6j91CE09r9pknfc/+paE6MO/QWnEmp3v/9do9ONun8iH3zDBSpRzAuf8veR3gP+oMAQzGJ6BuirzdKVhA4pLDuMsCLwAu0bNqTkFVghaQBDo32786bm16Yir/L/tlhCFh4+ct7wVzAtmvzf2885tPWipOiu1iVyDr1gz5+E173TehxSd4Y1PFdqfP3NRu3zfm2Pz5499valPvNb16YQokAnQR4NQ4X2h6rGHQ413JhCClhOcyUZc4JcCB0gLPjBmGCl8YJQtj2Hh3c4v4L6Fd99FBagnfrGGHN6//zCB7ehVU+ccXVLff/4jb4vi2488+ra44KkrrrhiHnzG19SvWNdcQSagw7tnGehIorrvKUKe/8r+5wkql4aHftwemoXE9x599B1RfPMRMzdenDc+Pq84fvXgTbdM3XGae7vzWidcDQwjoFJYsoGs84tdQddNWgyqFBbwazSe488CmAH0bJdfGL/G4cMnC5+Qa7j2DyEFtnilw6+BPfQF4UZ+djGLaWOWtSAyNTLTGQLkuVe6JWQNubMiUyILLGu2HqMX85i5mMb02Za1EC7PdM9q6LTDhpExLcYudqBgzjid2vp9GO9bv34/xvvXoxP/L2tTrf+ftf94hJ1nwfSZ23Bfamd8qQtJISX0Cn1CA1QBz2sXCSv+qFKOhnK61ZXWFQJocKDUavQFFvKavV4N6ibmgd9h50Cp7FUhx5VqDgWPQ5EYuHEGNSRgv4GF451fQD1bOH4cFQDpvzj+RgdSFCJQ+iqS2Pm3o77v20g49SGifvzZZBRl5hkpfyJfk57f8uCIT869xla/maT+gzheXiOdI9XEymySwz6Z4MbONn5yUEJKaFbq/P6NNzq/l/DPkjMM5udp/aJv53GMBakH44tF5T767PlsRy5/4cp1fy1OF0V/tS0tN3GqfB/rxvbfk+8TH3LbWqiSil2sUa4zAS4+ryjaoejnnypkDM5aWRyukQDOIOn3VWHfakLH5ukeQ2EFwtMVb8QTDPVc+BL/k20flnvkw4lBi0GGwDK7M6LpWe81xZFWqnk/p64SHeV1N6trkTupjApEZbHOSTd6OGEoYgEb5p0R1XvN7pVXaTltpdIbe91TIncaJikQ2Ug8F8mpl4jiJQaNyVn3NSVJb4/09npiGlvydxx4fUe2cFr0ensjt7OE+iMvIzsH5Et0U4u9Zvcbt0dyJE1S0eeh1Hs+moLjXOR2o99+LaaZ+iUyxxVsPj9C7idLoTJLCFkhB5p9DPTlBq7cIci45mmHlTjUVYzX6lBRF0u0GVQ8P1xkMXn15bm8BPMAPKDx6z7ietEHTE1SZ4nVwJ8gOUFPeHEI+Vmo5sRbNXRjcna+7jjTPMVSqSUnEps3WT2OfotEEz558up/XD3QjlhixBrrrzT98325saU2snYQfa1zuJVsnJM3Ruff0Nj59ZkTmqRW81WNZC7rKReqULO96TGCMQuMPoMyzMRYD7FKRb0HI3RebXjZlMtn1LfXh+YnUrqMEJJoJHbB1rGJ/97ZsENnpdZALDbQQrK7sCeFNiULy+ZTHoR8HWOCvAExGAjDwgzQ4NeDnvRDfvY5l3EmC33ixBkFaHGnVEMB2UYh2jzHZyCqA7faMlGLWoQOYQBYMyS0ZqvShETJvQmFqwe45aZ4AVLlz6DVdD23jXxyZD9ZP5IvoMbSOspmZq8nNzOmvlAZSK7evDo5UHlBZR2EcsM5NH36aG/bmbWj7bb/Z7M8pdTfqE+dWq0e2I7wium54vz5fcXmMjw60vnfrRtbxYWiJkP1h2f2DG0eZiaVGvsOH1q3J2XKWtBoBLpiZPauOfQXe2SHZBWp0mpVJCWLXfxpIpdLEFk5+VWUz1Cayd+gUJEQRHjRmZx4/tDibWDDgndi25JDFz04F+9OJs1lc3i1W5ujucghkmWczssT5D2yXyhAHhSQb4Ye/EPxFZbxZ/J0GU1Wa6EWxV+VDZGxA89i6bOXX/5Mws8eYEw02IMvYwnpx44hXcIvd/5BVmMxlfRiZhl42q/uf/kzUfzs5ft/NQ0bFsND//ro0c7vgN6Mo49+1nlYk9FRFV6dlqydzmdPAwevFGqQzw7Bs4e4mAVPjz+eWageNJoQJOFT8yGDeYAFZ/JxcuLIoAJUalKokDkiZvGwgucPPcdQw2+2qtAD0h7HTcXNEQAQaF1ICbyzVDjTC8hoSJzsVYJuzSrcgzleuARk4XC9FwDEecohzzyuG5Tm7ZgTxUq68U+NmIWnjU7MlkBXunqgxa8t+KIweCr9s3Tq2pgtkWk6prpGxcam9tQowhgxnWo6kyOGNiiTdX0D+LenaoXsQdtlMSYBZKSIRhFS4gNHBzyKqWZoCo7mwfee83i2d/cy6hgjTCk3lEJ8PGVfosyFmseZFvXUW7UlMXc0bU+Zrg05+vzYmD973rSF1/nZzJA5X83HZ0m4NchE2RxVVelL2pUsfaGdGpcZFSVDjWdShuFNNxyaoA1n/oV+YXpkudbnjitpVXcTPaIWG7dU6Vp1KDk8nG3iqyXVGtejIlbqg5oszvf6tAsjM4pnaZSqcAjZgLpW4IdB+Yd3qQiVYCn0a3djocpZKOzjaaL75kKEZ48zb45d6nHqLIWxDDzJi5RmjYaMGa5buV3+7O54miHhiqff5V2+qBXmoMkFUZ7IhhCCBM81UBad1RlQABmcp7As5oTRJl3qOdOT92VhKgyKPhgugWFGeTHcHMNNqK8cBhQe8DtzDUUFUkQ9mqanDZNmcyQSYYauRCKuG4kQxzFNxdDj9hTDML58LTqdxtHOUzcCj+i6b5iBZbJIPmv0GkaPpmNDT1jW8LkFFVHdmyKlerCpU4YAM7qGJBZhIlIqHjVMJWFJKG5pKduMqtTucRKR5GCCyjiTLvpJixkRjDA8C1EVI1nLdQfaro6kn3Q+kdDGVUpyXi1dS2AsQ7Gji2AoVzYkn5Foyuip9dgpNxGTMIuYBSOuyrauRtyIWbJZTMRmDGMsRhOIqLokKSJNkISCKbKyPQoixYWtjX+CpM4npA9FGKUGlTBAH6iOvzAcUCISUWKqyxRl/jt/elrdfOXvGpJEKY2AhiaEQEtMJCIiIilMtlsDRgTIMYatlJWU4SpWMdZVS8ISyUSjMFRJgazl9TglVZ3tRaxCPms6aQWr0tCWQv6CIJpXsKRpKU2SsUwxYfGtjZ6TX5eQ+ZMb3pjrzNzhKRKYNG0jwbAsVTcGypxz9ZQD7st5kUIha4g01rc8ztIxM2NoWFRkakqiocRggqYrgWQhIlUZQgRpkhW3iJjPSeJg3Z7z1gQ4vZsXJ+sXD/0S/QuOfVGPkrPkqF0MlRTXEiGS/VapHYK86cKG0VZAeQlQRiWO5RJXXq5XLONSGF9cjgGOXVYrVUo+Vx+EYx70G5QDPFKgST2MGh5/2OWmsxiOnTCkWKsJ8cFlCuMCsNkNPuZDfBcpz8G+iZkpFmnYvgVixeXx4vMRey4PizC6UBPilu+4pSbcmJclPA5DzuXUMAYdGsDk4TBAXpokbmKX80ejSyDUpwGfZ8kFVYlpnK9C+aVwUA2unCazXTf0YWZtDMOgZRE8Qfk1DBwVFDnDuJTHNenq2yCkLMzjF3vci1A1BnwQfOzgYo+63NdtKANqXiMAhggJDdxWataq8C0/hZkGTU4AJY9ntjGejyj/XZFzY6vilfEYqock0RbDkbqgd/ygwivOXwLi8+8e71acx491s2mYfaXOP1+lxxWZqIpkQuhZEih9VbQIcSiVNQVIXR1Bpos0hUCwEiTJEgAOa2YsossykAMWqc6oA+obQYKCSNUUizDfZA6DHjKgFYkQtVgSZS1KRUaJiogiM5nqMralSCDGeLwiojGsiCLoQwpmVJxwVIOYRAXe1ZlEmAQ3gjA1RJDzIkGiJqrRrO2pmiOrsqnDyFQF4owi0Aw6RAdTmMqoosUUSoHwEI9BDStEEuGUmcxWRUXnqRHgCsZ53OvgA84D0QhSYUiSKCUSBINQBQLCBMsi39MYY1RjKnSRVDAmyoqqMk3mPxQYsqlZ4DMQvggTRaWERS2MFUW0FOiKgAkkVbWKq6pIVmQUjUZlyUJR8CQwDNRYkNEByaos6wwBeUg4khIzlq5DJ4oHypk0NGNaLBOjhmHOvW4QLeGMSzjhga4DBwLjSdRQgBQI5qNligx7WZRMXemuHhzvrh4cfxcVTvZ3NZgBKPjdsWOdf8YnIW6BObE8FZOYYjDuaYWCEMcwH6T2qVSVACqaCEUTmMUS0zHcEEhRFBUJkxyMQwGJSXUpFhUJU1QR8Z+nEaGySh1oJSEKXsGMUD2qSiqTRHAMcVT+hEko+kEuihjUBIlCQpIhEQErY8hdFFzPJBgfjABROaqJSpSawO7FONwK0BenkoiQKCMpCtRMwIwEjQGVDCpjbMpYhZwAw+XPUoMRwIxkzTLUiBkFlBEJy2GakCM6PGuMI6oMoAPWhbHAGAD3MBhmyq5ZhJLIMiQmxxRb0w3NNMLEBw+NACCwKEumw0xQrpQZhooVxwXNDdRdsnUJGFvRVFnrn+NQGI8CSAYxpSCODgTDBodQTWa6qEI0YWggg8ckTGVRMwm0DuNIxAakDGP20J9DuAE4M3mYJfiKkPeoYckRFqO8IahAmShEFfOF6Q1Rmfy9+kxOEOYKS/7zjNCaiZyuvj+zzhWWUG4N5P3kr5j8OKiW+s5ebv93SxPHOr8FwEUBZ1HA2W9XJq8aH1mEyN7LLttL0KKR8auSQD1y6Y7ly+8oQQRroog7f798C8bbl/Ltln+3JnHSP8scwPa3P4CqZnx9PLNlfP0ejPesH9+edNbOppIpSwvXI7R+oQQh+mOybelFWxDedsHSbaEfbiTHJ/0wKMz7z3+3t4t+qVCk/Nd7v9HsbdVcEVr0FaqeWwNVNvlDb6jzoCrHwpa7Mb57S3d7wWaMN18Qbl9Lj5U675bG0n0LMm0NZe8buQ9lNXpbp4j83kK2Z+koVn9/08hNv1dRXLOnT88W0NfIga1bDhByYMvWA+h73BB3B5g7ORfAosX8Dd94BuNnvrGzNh/iZmD0hjswvuOG/l5JB+UvCAzm+Ax5nSwDNW0J/TDLitCA+liwo4UoCaCeDmpeq8Y8VOAPtsZ/Fyg4tWicskKj5BT4knHQ8KTa5EImfNrok++Oj6/Yvn7ZyOiyDdtRprNi2ijqQ30ZdMdbfvrUE334T6eNoLce7Pzju+ELL+qY6JPO7o0bn7hvxYr7nsAH0OjAzTen/bdOvZT20aLtaGQa3kweeuhbfat7V/eFteYPyaukKewV7uG/QpukWO4uA0PON0mp6AelsKI3EeTcSSFeDv8TAbJtWPKFP17W28TtrnuUQXSTWnWy6Ic5ViEHT6rxSZFPuyso8CdV27hexkUTx7OY/ABTw/Zi8XQ2q3qyAdkimSnokN2QWptb18R4oljogQzINMk1/FjcMQwtBjxDIpnBwYyiyXokasmZdkLTCH/RTCmv6ibRhubWQSMrRj6bAl7AulI+Jy27sumK2GLZ/lKGSWpnwcDi6YXC9MUrF0/P56cvRoKeL6Z4JlS1xKy0bUPtJ5ssOzCQ0TTVtEGRR9zpCRO4n0FSShdKuiPp9blVjbiJXDqhYaIrnl7I92gRasQNFlFSQV9GjsiGo8sxMT2rLBrAgUarJxojamNORQPu35AfWbRy0Yx8fgbsRvKn6+13yf8ivUJGGBXmfzFyeCDQYtClDDjg/wcSrr10j7st3fjkvwG5XussfkEnRxcivHfdur0YLZj5tacxChA59sADxwgc4KcjyN5/WEQ+Jndur8xEeMcq2z73Sxh/6VwbvbX0SoyvXMq3V6EdPVfPX3cLIbesO/fqdPHte+Lxg8dE8djBePze/zYFxxE5vN+2b/xuoWftzFU7+E3AxLxLMbJ1vGnpkk0Yb1qydJMg0HCyr+JO+P8xGsSQDVwh2BAHrYJTYH+0n71796f4tlO7Pv2jPT61a9eut3fv3n0EPtwkCu2WcFaICkIfXwmtN6u8+qy6cRB96PNk7ITqpNUPP9TSjnoi9n70NyocfPghv/ibaLc//iZ+XIiAMXCsXx8DRRoaceP4z05op1trJ2JoxQkNDkJb2gl7+Av35/09N06LQ6fNNKt4gN8cOvB7nrArWP/i3YWz7g9nKOzEBz5pyEfrY18cwQTM56wxQBEi/F8z/lSDAAB4nGNgZGBgAGLXe5c64vltvjJwszCAwDVmzkIE/V+DhYG5GcjlYGACiQIADHEIjwB4nGNgZGBgbvjfwBDDwsIABECCkQEVKAEAR4wCjwAAAHicY2FgYGB+ycDAwkACZgJiFhL1EMAAnlEBdwAAAAAAAHYBBAFiAgQCYgNYA7AD/ASgBSoGSAaqBvIHXggECJgJMAnGCnYKxguwDVIQXBDgEVIRrBJ+EvITHhM+E14TfhOgAAB4nGNgZGBgUGISZuBnAAEmIOYCQgaG/2A+AwAPSAFdAHicXZE9TsNAEIWf8ysciQIEFcVKSBRBcX7KiC5S0qdwR5E46/zI9lrrTaSUnIcTcAJOQEvDKeh4cQYkbGvH37x9MzvyArjCJzycnxuuM3toMztzjXwnXCffCzfIXeEmOhgKt6g/Cft4xES4g2tk7OA1Lph18SLs4RKvwjXym3Cd/C7cIH8IN3GLL+EW9W9hH6HXFu7gwXv2J1YvnF6p5VFtI5PFJnN+euydeK7X+2RhJZNPqG2xNZkaBgNRZjrT9rdHcViPnItVbE2qpmymk8So3JqdjlywcS4f9/ux6EFkUo40gYXGAo5xBYUljoxbRDD8GXEZHX0p9d6fPqd7jT0SVtrK3v8spNOiYH7KFC8hwKDimdGTlb7qHAUOPGdE1XEWxWVZkZKmMpnmDAlZIS/3dlQi6gE2ZVWOMfp844o/KM9OfwDtPGpBAAAAeJxtT2tvgkAQZKyg+Cz2bd/2cxOVEvsH+kNOOLlr8A6BjeKv71EraZNuLtm53dnZHathHaJj/R8TNHCCJmw4aKENFx100UMfAwxxCg8jnOEcF7jEFa5xgzFucYd7POART3jGBC8Wdu1C004yFY/DbF3QUjItiC2Z8oMVLbnyFwt/mAuu4pISaZ6hNinn2WBt4IrLXMhSq9gpSIaCujLUKufFdB74Nfan7+1QGNJeSPcIZk5EKmWyfyy8hjot6/Zbzyw1ORQyIVWX517IZMRUwhSTqawE6l5gs4RlazvVW57Z1Y25u9VZEm1lxO0gYhtyj25nTnW0IC9i2pz9sQt5UnmPuXPYaG9I7WmUC005acMSZqoy1Po080Zm+JO/5UpN3u9/XrH/MPakLesLACmLNA==') format('woff'), + url('iconfont.ttf?t=1507649649871') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ + url('iconfont.svg?t=1507649649871#my-icon') format('svg'); /* iOS 4.1- */ +} + +.my-icon { + font-family:"my-icon" !important; + font-size:16px; + font-style:normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-touxiang:before { content: "\e62c"; } + +.icon-crmtubiaohuaban35fuben3773:before { content: "\e85f"; } + +.icon-shengyuliuliang:before { content: "\e650"; } + +.icon-user:before { content: "\e61a"; } + +.icon-mianfeishiyong:before { content: "\e618"; } + +.icon-tuichu:before { content: "\e60b"; } + +.icon-iconset0253:before { content: "\e698"; } + +.icon-iconset0308:before { content: "\e6cf"; } + +.icon-chongzhi:before { content: "\e600"; } + +.icon-chongzhi1:before { content: "\e676"; } + +.icon-dunpai:before { content: "\e620"; } + +.icon-chongzhi-copy:before { content: "\e78e"; } + +.icon-chongzhi4:before { content: "\e6b4"; } + +.icon-shezhichilun:before { content: "\e62b"; } + +.icon-chongzhi2:before { content: "\e6c7"; } + +.icon-caidanlanaipinpai:before { content: "\e68f"; } + +.icon-chongzhi5:before { content: "\e67a"; } + +.icon-alarm:before { content: "\e604"; } + +.icon-power:before { content: "\e65c"; } + +.icon-users:before { content: "\e685"; } + +.icon-worldwide:before { content: "\e690"; } + +.icon-5daqu:before { content: "\e669"; } + +.icon-touxiang1:before { content: "\e60e"; } + +.icon-yonghu:before { content: "\e616"; } + +.icon-daochuExcelbiaoge:before { content: "\e61d"; } + +.icon-chilun:before { content: "\e611"; } + +.icon-qunzu:before { content: "\e696"; } + +.icon-shousuodaohangicon:before { content: "\e6bc"; } + +.icon-jiantou:before { content: "\e612"; } + +.icon-jiantouxiangyou:before { content: "\e621"; } + +.icon-jiantouxiangshang:before { content: "\e622"; } + +.icon-jiantouxiangzuo:before { content: "\e623"; } + diff --git a/src/main/resources/assets/fonts/iconfont.eot b/src/main/resources/assets/fonts/iconfont.eot new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/assets/fonts/iconfont.js b/src/main/resources/assets/fonts/iconfont.js new file mode 100644 index 0000000..ea8edc1 --- /dev/null +++ b/src/main/resources/assets/fonts/iconfont.js @@ -0,0 +1 @@ +(function(window){var svgSprite='';var script=function(){var scripts=document.getElementsByTagName("script");return scripts[scripts.length-1]}();var shouldInjectCss=script.getAttribute("data-injectcss");var ready=function(fn){if(document.addEventListener){if(~["complete","loaded","interactive"].indexOf(document.readyState)){setTimeout(fn,0)}else{var loadFn=function(){document.removeEventListener("DOMContentLoaded",loadFn,false);fn()};document.addEventListener("DOMContentLoaded",loadFn,false)}}else if(document.attachEvent){IEContentLoaded(window,fn)}function IEContentLoaded(w,fn){var d=w.document,done=false,init=function(){if(!done){done=true;fn()}};var polling=function(){try{d.documentElement.doScroll("left")}catch(e){setTimeout(polling,50);return}init()};polling();d.onreadystatechange=function(){if(d.readyState=="complete"){d.onreadystatechange=null;init()}}}};var before=function(el,target){target.parentNode.insertBefore(el,target)};var prepend=function(el,target){if(target.firstChild){before(el,target.firstChild)}else{target.appendChild(el)}};function appendSvg(){var div,svg;div=document.createElement("div");div.innerHTML=svgSprite;svgSprite=null;svg=div.getElementsByTagName("svg")[0];if(svg){svg.setAttribute("aria-hidden","true");svg.style.position="absolute";svg.style.width=0;svg.style.height=0;svg.style.overflow="hidden";prepend(svg,document.body)}}if(shouldInjectCss&&!window.__iconfont__svg__cssinject__){window.__iconfont__svg__cssinject__=true;try{document.write("")}catch(e){console&&console.log(e)}}ready(appendSvg)})(window) \ No newline at end of file diff --git a/src/main/resources/assets/fonts/iconfont.svg b/src/main/resources/assets/fonts/iconfont.svg new file mode 100644 index 0000000..d388e4f --- /dev/null +++ b/src/main/resources/assets/fonts/iconfont.svg @@ -0,0 +1,129 @@ + + + + + +Created by iconfont + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/assets/fonts/iconfont.ttf b/src/main/resources/assets/fonts/iconfont.ttf new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/assets/fonts/iconfont.woff b/src/main/resources/assets/fonts/iconfont.woff new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/assets/img/default-user.jpg b/src/main/resources/assets/img/default-user.jpg new file mode 100644 index 0000000..fb39e86 Binary files /dev/null and b/src/main/resources/assets/img/default-user.jpg differ diff --git a/src/main/resources/assets/img/file-favicon.png b/src/main/resources/assets/img/file-favicon.png new file mode 100644 index 0000000..dcc30a3 Binary files /dev/null and b/src/main/resources/assets/img/file-favicon.png differ diff --git a/src/main/resources/assets/img/loading-sm.gif b/src/main/resources/assets/img/loading-sm.gif new file mode 100644 index 0000000..664e1f8 Binary files /dev/null and b/src/main/resources/assets/img/loading-sm.gif differ diff --git a/src/main/resources/assets/img/loading.gif b/src/main/resources/assets/img/loading.gif new file mode 100644 index 0000000..664e1f8 Binary files /dev/null and b/src/main/resources/assets/img/loading.gif differ diff --git a/src/main/resources/assets/img/mini.png b/src/main/resources/assets/img/mini.png new file mode 100644 index 0000000..ecdf3fa Binary files /dev/null and b/src/main/resources/assets/img/mini.png differ diff --git a/src/main/resources/assets/js/admin.js b/src/main/resources/assets/js/admin.js new file mode 100644 index 0000000..695d795 --- /dev/null +++ b/src/main/resources/assets/js/admin.js @@ -0,0 +1,764 @@ +var app = new Vue({ + el: "#container", + data: { + categories: [], + downloaded: [], + uploaded: [], + files: [], + serverFiles: [], + selectedServerFiles: [], + auths: [], + users: [] + } +}); + +var authFileSearch = ""; + +var authSearchOffset = 0; + +var authModal = new Vue({ + el: "#authAddedModal", methods: { + searchFileInAuth: function () { + var file = $("#auth-file-search").val(); + if (isEmpty(file)) { + alerts("内容不能为空"); + } else { + if (file === authFileSearch) { + authSearchOffset += 1; + } else { + authSearchOffset = 0; + authFileSearch = file; + } + layer.load(1); + $.get("/file/basic/all", { + user: "", + file: file, + category: "", + offset: authSearchOffset + }, function (data) { + layer.closeAll(); + var json = JSON.parse(data); + if (json.length < 1) { + alerts("糟糕,没有数据了"); + } else { + var ele = $("#auth-file-list-group"); + $(ele).empty(); + $.each(json, function (i, n) { + var li = "
  • " + n.localUrl + "
  • "; + $(ele).append(li); + }); + $('[data-toggle="tooltip"]').tooltip(); + } + }); + } + }, + "searchUserInAuth": function () { + var user = $("#auth-user-search").val(); + if (isEmpty(user)) { + alerts("内容不能为空"); + } else { + layer.load(1); + $.get("/user/all", { + user: user, + offset: 0 + }, function (data) { + layer.closeAll(); + var json = JSON.parse(data); + if (json.length < 1) { + alerts("糟糕,没有数据了"); + } else { + var ele = $("#auth-user-list-group"); + $(ele).empty(); + $.each(json, function (i, n) { + var li = "
  • " + n.username + "
  • "; + $(ele).append(li); + }); + $('[data-toggle="tooltip"]').tooltip(); + } + }); + } + } + } +}); + +var serverFileSearchHistory = []; + +Vue.component('file-filter-item', { + template: '
    ' + + '
    ' + + '
    ' + + '
    ' + + ' ' + + '
    ' + + '
    ' + + ' ' + + '
    ' + + '
    ' + + ' ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' +}); + +var offset = 0; + +var enableSearch = false; + +function getTabInfo(tabId) { + offset = 0; + enableSearch = false; + changeTabInfo(tabId); +} + +function changeTabInfo(tabId) { + if (tabId.indexOf("#") === 0) { + tabId = tabId.substr(1); + } + if (tabId === "upload-manager") { + getUploaded(); + } else if (tabId === "download-manager") { + getDownloaded(); + } else if (tabId === "file-manager") { + getFile(); + } else if (tabId === "auth-manager") { + getAuth(); + } else if (tabId === "category-manager") { + setCategoryToDefault(); + getCategory(); + } else if (tabId === "user-manager") { + getUser(); + } else if (tabId === "admin-manager") { + + } else if (tabId === "system-setting") { + getConfig(); + } else { + alerts("没有找到可执行的方法"); + } + setTimeout(function () { + $('[data-toggle="tooltip"]').tooltip(); + setCSS(); + }, 1000); +} + +function getConfig() { + layer.load(1); + $.get("/config/all", function (data) { + layer.closeAll(); + $("#json-editor").children("textarea").val(JSON.stringify(JSON.parse(data), undefined, 4)); + }); +} + +function saveConfig() { + layer.load(1); + $.ajax({ + url: "/config", + type: "PUT", + data: {config: $("#json-editor").children("textarea").val()}, + success: function (data) { + layer.closeAll(); + alerts(JSON.parse(data).message); + } + }); +} + +// var editor = new JSONEditor(document.getElementById("json-editor"), { +// theme: 'foundation5' +// }); + +function getUser() { + layer.load(1); + $.get("/user/all", getFileFilterParameters(), function (data) { + layer.closeAll(); + var json = JSON.parse(data); + if (json.length < 1) { + alerts("糟糕,没有数据了"); + } else if (offset < 1) { + app.users = json; + } else { + app.users = app.users.concat(json); + } + }); +} + +function getIconYesOrNo(flag) { + return ""; +} + +function getAuth() { + layer.load(1); + $.get("/auth/all", getFileFilterParameters(), function (data) { + layer.closeAll(); + var json = JSON.parse(data); + if (json.length < 1) { + alerts("糟糕,没有数据了"); + } else if (offset < 1) { + app.auths = json; + } else { + app.auths = app.auths.concat(json); + } + }); +} + +var fileFilterDivParent; + +function getFile() { + layer.load(1); + $.get("/file/basic/all", getFileFilterParameters(), function (data) { + layer.closeAll(); + var json = JSON.parse(data); + if (json.length < 1) { + alerts("糟糕,没有数据了"); + } else if (offset < 1) { + app.files = json; + } else { + app.files = app.files.concat(json); + } + }); +} + +function getFileFilterParameters() { + var res = { + user: enableSearch ? $(fileFilterDivParent).find(".user-filter").val() : "", + file: enableSearch ? $(fileFilterDivParent).find(".file-name-filter").val() : "", + category: enableSearch ? $(fileFilterDivParent).find(".category-filter").val() : "", + offset: offset + }; + enableSearch = false; + return res; +} + +function fileFilter() { + /** @namespace window.event.keyCode */ + if (window.event.keyCode === 13) { + offset = 0; + enableSearch = true; + fileFilterDivParent = $(event.srcElement).parent().parent(); + changeTabInfo(window.location.hash); + } +} + +function getUploaded() { + layer.load(1); + $.get("/uploaded/all", getFileFilterParameters(), function (data) { + layer.closeAll(); + var json = JSON.parse(data); + if (json.length < 1) { + alerts("糟糕,没有数据了"); + } else if (offset < 1) { + app.uploaded = json; + } else { + app.uploaded = app.uploaded.concat(json); + } + }); +} + +function getDownloaded() { + layer.load(1); + $.get("/downloaded/all", getFileFilterParameters(), function (data) { + layer.closeAll(); + var json = JSON.parse(data); + if (json.length < 1) { + alerts("糟糕,没有数据了"); + } else if (offset < 1) { + app.downloaded = json; + } else { + app.downloaded = app.downloaded.concat(json); + } + }); +} + +function getCategory() { + $.get("/category/all", function (data) { + app.categories = JSON.parse(data); + }); +} + +function deleteCategory() { + var srcTr = $(event.srcElement).parents("tr"); + var categoryId = $(srcTr).children("td.category-id").text(); + $.ajax({ + url: "/category/" + categoryId, type: "DELETE", success: function (data) { + var json = JSON.parse(data); + if (json.status === "success") { + app.categories.splice($(srcTr).children("td.hide").text(), 1); + alerts("删除成功"); + } else { + alerts("删除失败,请稍后重新尝试"); + } + }, error: function () { + alerts("删除失败,该分类已被多个文件引用,无法删除(可尝试修改文件分类或删除文件)。"); + } + }); +} + +function showFileShareModal() { + if (isEmpty($("#select-url").val())) { + getServerFileByPath(false); + } + var modal = $("#fileAddedModal"); + var ele = $(modal).find(".modal-content"); + if (isMobile()) { + $(ele).removeClass("width-60-vm"); + } else { + $(ele).addClass("width-60-vm"); + } + $(modal).modal("show"); +} + +var selectedRows = []; + +var rowIndex; + +/** + * 演示同类型的模态框 + * @param table tableId + * @param modal 模态框 + */ +function showFileModal(table, modal) { + rowIndex = 0; + selectedRows = getSelectedRows($(table).children("tbody")); + if (selectedRows.length < 1) { + alerts("请至少选择一行"); + } else { + if (modal === "#fileModifiedModal") { + setModifyFile(); + } else if (modal === "#fileAuthModal") { + setFileAuth(); + } else if (modal === "#authEditModal") { + setAuth(); + } else if (modal === "#userFileAuthModal") { + setUserFileAuth(); + } else if (modal === "#userPasswordModal") { + setUsername(modal, ''); + } else if (modal === "#userAuthModal") { + setUsername(modal, "auth"); + } + $(modal).modal("show"); + } +} + +function setFileAuth() { + checkRowIndex(); + var file = app.files[$(selectedRows[rowIndex]).children(".file-index").attr("data-key")]; + /** @namespace file.localUrl */ + $("#file-local-url").val(file.localUrl); + $("#file-auth-id").val(file.id); + layer.load(1); + $.get("/file/" + file.id + "/auth", function (data) { + layer.closeAll(); + var json = JSON.parse(data); + /** @namespace json.isDownloadable */ + $("#file-downloadable-auth").val(json.isDownloadable); + /** @namespace json.isDeletable */ + $("#file-deletable-auth").val(json.isDeletable); + /** @namespace json.isUpdatable */ + $("#file-updatable-auth").val(json.isUpdatable); + /** @namespace json.isVisible */ + $("#file-visible-auth").val(json.isVisible); + }); +} + +function setUsername(id, auth) { + checkRowIndex(); + var user = app.users[$(selectedRows[rowIndex]).children(".user-index").attr("data-key")]; + var parent = $(id); + $(parent).find(".username").val(user.username); + if (!isEmpty(auth)) { + $(parent).find(".user-permission").val(user.permission); + } +} + +function setUserFileAuth() { + checkRowIndex(); + var user = app.users[$(selectedRows[rowIndex]).children(".user-index").attr("data-key")]; + var parent = $("#userFileAuthModal"); + $(parent).find(".username").val(user.username); + $(parent).find(".user-downloadable").val(user.isDownloadable); + /** @namespace user.isUploadable */ + $(parent).find(".user-uploadable").val(user.isUploadable); + $(parent).find(".user-deletable").val(user.isDeletable); + $(parent).find(".user-updatable").val(user.isUpdatable); +} + +function setAuth() { + checkRowIndex(); + var auth = app.auths[$(selectedRows[rowIndex]).children(".auth-index").attr("data-key")]; + $("#auth-username").val(auth.username); + $("#auth-file-local-url").val(auth.localUrl); + $("#auth-id").val(auth.id); + $("#auth-downloadable").val(auth.isDownloadable); + $("#auth-deletable").val(auth.isDeletable); + $("#auth-updatable").val(auth.isUpdatable); + $("#auth-visible").val(auth.isVisible); +} + +function checkRowIndex() { + if (rowIndex < 0) { + rowIndex = selectedRows.length - 1; + } else if (rowIndex >= selectedRows.length) { + rowIndex = 0; + } +} + +function toggleCheckBoxStatus(tab) { + setCheckboxesStatus($("#" + tab + "-manager-table").children("tbody"), document.getElementById(tab + "-toggle-box").checked); +} + +function setModifyFile() { + checkRowIndex(); + var file = app.files[$(selectedRows[rowIndex]).children(".file-index").attr("data-key")]; + /** @namespace file.localUrl */ + $("#old-file-local-url").val(file.localUrl); + $("#old-file-visit-url").val(file.visitUrl); + $("#file-id").val(file.id); +} + +function getServerFileByPath(addTo) { + var path = $("#select-url").val(); + serverFileSearchHistory = serverFileSearchHistory.concat(path); + layer.load(1); + $.get("/file/server", {path: path}, function (data) { + layer.closeAll(); + app.serverFiles = JSON.parse(data); + var ele = $("#server-file-list-group"); + $(ele).empty(); + $.each(app.serverFiles, function (i, json) { + var li = "
  • " + json.name + "
  • "; + $(ele).append(li); + if (addTo && json.isFile) { + addToSelectedServerFile(json); + } + }); + $('[data-toggle="tooltip"]').tooltip(); + }); +} + +function editCategory() { + $("#category-title").text("编辑分类"); + var srcTr = $(event.srcElement).parents("tr"); + var categoryId = $(srcTr).children("td.category-id").text(); + $("#category-id").val(categoryId); + $("#category-key").val($(srcTr).children("td.hide").text()); + $("#category-name").val($(srcTr).children("td.category-name").text()); +} + +function saveCategory() { + var name = $("#category-name").val(); + if (isEmpty(name)) { + alerts("分类名不能为空"); + } else { + var id = $("#category-id").val(); + layer.load(1); + if (id > 0) { + $.ajax({ + url: "/category/" + id, type: "PUT", data: {name: name}, success: function (data) { + if (data.indexOf("success") > 0) { + app.categories[$("#category-key").val()].name = name; + } + responseTip(data); + } + }); + } else { + $.post("/category/" + name, function (data) { + if (data.indexOf("success") > 0) { + getCategory(); + } + responseTip(data); + }); + } + } + setCategoryToDefault(); +} + +function toggleRowSelectedStatus(ele) { + if (event.srcElement.toString() === "[object HTMLTableCellElement]") { + var cb = $(ele).find("input[type='checkbox']")[0]; + cb.checked = !cb.checked; + } +} + +function setCategoryToDefault() { + $("#category-id").val(0); + $("#category-key").val(0); + $("#category-name").val(""); + $("#category-title").text("添加新分类"); +} + +function addToSelectedServerFile(json) { + app.selectedServerFiles = app.selectedServerFiles.concat(json); + var li = "
  • " + json.name + "
  • "; + $("#selected-file-list-group").append(li); + $('[data-toggle="tooltip"]').tooltip(); +} + +function selectServerFile() { + var json = app.serverFiles[$(event.srcElement).parent().attr("data-key")]; + if (json.isDirectory) { + /** @namespace json.absolutePath */ + $("#select-url").val(json.absolutePath); + getServerFileByPath(document.getElementById("share-all-file").checked); + } else { + addToSelectedServerFile(json); + } +} + +function removeSelectedServerFile() { + var liEle = $(event.srcElement).parent(); + var key = $(liEle).attr("data-key"); + app.selectedServerFiles.splice(key, 1); + $(liEle).remove(); +} + +function toOneOrZero(val) { + return val > 0 ? 1 : 0; +} + +function updateAuth(url, down, uplo, dele, upda, visi, key) { + var auth = toOneOrZero(down) + "," + toOneOrZero(uplo) + "," + toOneOrZero(dele) + "," + toOneOrZero(upda) + "," + toOneOrZero(visi); + layer.load(1); + $.ajax({ + url: url, type: "PUT", data: {auth: auth}, success: function (data) { + layer.closeAll(); + var result = data.indexOf("success") > 0; + alerts("更新" + boolToChinese(result)); + if (result) { + if (url.indexOf("auth") === 1) { + app.auths[key].isDownloadable = down; + app.auths[key].isDeletable = dele; + app.auths[key].isUpdatable = upda; + app.auths[key].isVisible = visi; + } else if (url.indexOf("user") === 1) { + app.users[key].isDownloadable = down; + app.users[key].isUploadable = uplo; + app.users[key].isDeletable = dele; + app.users[key].isUpdatable = upda; + } + } + } + }); +} + +function doDelete(table, json, index, url) { + selectedRows = getSelectedRows($(table).children("tbody")); + if (selectedRows.length < 1) { + alerts("请至少选中一行"); + } else { + layer.confirm('是否确定删除选中的所有行', { + btn: ['确定', '取消'] + }, function () { + var ids = ""; + selectedRows.forEach(function (tr) { + ids += json[$(tr).children(index).attr("data-key")].id + ","; + }); + ids = ids.substr(0, ids.length - 1); + layer.load(1); + $.ajax({ + url: url + ids, type: "DELETE", success: function (data) { + layer.closeAll(); + var json = JSON.parse(data); + if (json.status === "success") { + selectedRows.forEach(function (tr) { + var key = $(tr).children(index).attr("data-key"); + if (table.indexOf("file") > 0) { + app.files.splice(key, 1); + } else if (table.indexOf("auth") > 0) { + app.auths.splice(key, 1); + } + }); + $("[data-toggle='tooltip']").tooltip(); + layer.msg("删除成功"); + } else { + alerts("删除失败"); + } + } + }) + }, function () { + layer.msg("取消了操作"); + }); + } +} + +$(document).ready(function () { + $("#auth-add-button").click(function () { + var fileList = $("#auth-file-list-group").find("a"); + var files = ""; + for (var i = 0; i < fileList.length; i++) { + files += $(fileList[i]).attr("data-key") + ","; + } + var userList = $("#auth-user-list-group").find("a"); + var users = ""; + for (var i = 0; i < userList.length; i++) { + users += $(userList[i]).attr("data-key") + ","; + } + if (isEmpty(files) || isEmpty(users)) { + alerts("内容不能为空"); + } else { + files = files.substr(0, files.length - 1); + users = users.substr(0, users.length - 1); + var down = $("#auth-downloadable-new").val(); + var dele = $("#auth-deletable-new").val(); + var upda = $("#auth-updatable-new").val(); + var visi = $("#auth-visible-new").val(); + var auths = toOneOrZero(down) + ",1," + toOneOrZero(dele) + "," + toOneOrZero(upda) + "," + toOneOrZero(visi); + $.post("/auth", {files: files, users: users, auths: auths}, function (data) { + layer.closeAll(); + getTabInfo("#auth-manager"); + $("#authAddedModal").modal("hide"); + alerts("添加" + boolToChinese(data.indexOf("success") > 0)); + }) + } + }); + $("#user-auth-update-button").click(function () { + var key = $(selectedRows[rowIndex]).children(".user-index").attr("data-key"); + var permission = $("#userAuthModal").find(".user-permission").val(); + layer.load(1); + $.ajax({ + url: "/user/" + app.users[key].id + "/" + permission + "/", + type: "PUT", + success: function (data) { + layer.closeAll(); + alerts(JSON.parse(data).message); + if (data.indexOf("成功")) { + app.users[key].permission = permission; + } + } + }); + }); + $("#user-password-update-button").click(function () { + var password = $("#userPasswordModal").find(".user-password").val(); + if (isEmpty(password)) { + alerts("密码不能为空"); + } else { + var user = app.users[$(selectedRows[rowIndex]).children(".user-index").attr("data-key")]; + layer.load(1); + $.ajax({ + url: "/user/reset/" + user.id + "/" + password + "/", + type: "PUT", + success: function (data) { + layer.closeAll(); + alerts("更新" + boolToChinese(data.indexOf("success") > 0)); + } + }); + } + }); + $(".auth-delete").click(function () { + doDelete("#auth-manager-table", app.auths, ".auth-index", "/auth/batch/"); + }); + $("#auth-update-button").click(function () { + var down = $("#auth-downloadable").val(); + var dele = $("#auth-deletable").val(); + var upda = $("#auth-updatable").val(); + var visi = $("#auth-visible").val(); + var key = $(selectedRows[rowIndex]).children(".auth-index").attr("data-key"); + var id = app.auths[key].id; + updateAuth("/auth/" + id, down, 1, dele, upda, visi, key); + }); + $("#user-file-auth-update-button").click(function () { + var key = $(selectedRows[rowIndex]).children(".user-index").attr("data-key"); + var parent = $("#userFileAuthModal"); + var down = $(parent).find(".user-downloadable").val(); + var uplo = $(parent).find(".user-uploadable").val(); + var dele = $(parent).find(".user-deletable").val(); + var upda = $(parent).find(".user-updatable").val(); + updateAuth("/user/" + app.users[key].id + "/auth", down, uplo, dele, upda, 1, key); + }); + $("#file-auth-update-button").click(function () { + var down = $("#file-downloadable-auth").val(); + var dele = $("#file-deletable-auth").val(); + var upda = $("#file-updatable-auth").val(); + var visi = $("#file-visible-auth").val(); + var id = app.files[$(selectedRows[rowIndex]).children(".file-index").attr("data-key")].id; + //无须传递KEY + updateAuth("/file/" + id + "/auth", down, 1, dele, upda, visi, 0); + }); + setTimeout(function () { + if (isEmpty(location.hash)) { + location.hash = "#upload-manager"; + getTabInfo(location.hash); + } else { + $("a[href='" + location.hash + "']").click(); + } + }, 1000); + $("#server-file-share-button").click(function () { + if (app.selectedServerFiles.length > 0) { + var files = ""; + app.selectedServerFiles.forEach(function (json) { + files += json.absolutePath + ","; + }); + layer.load(1); + $.post("/file/server/share", { + prefix: $("#link-prefix").val(), + files: files.substr(0, files.length - 1) + }, function (data) { + layer.closeAll(); + var json = JSON.parse(data); + if (json.status === "success") { + getTabInfo(window.location.hash); + $("#fileAddedModal").modal("hide"); + layer.msg("共享文件成功"); + } else { + alerts("共享失败(可能是本地路径或访问链接已经存在导致)"); + } + }); + } else { + alerts("还没有选择任何文件"); + } + }); + $("#select-url").keyup(function () { + if (window.event.keyCode === 13) { + getServerFileByPath(false); + } + }); + $(".server-path-return").click(function () { + var len = serverFileSearchHistory.length; + if (len > 1) { + serverFileSearchHistory.splice(len - 1, 1); + $("#select-url").val(serverFileSearchHistory[len - 2]); + serverFileSearchHistory.splice(len - 2, 1); + getServerFileByPath(false); + } + }); + $(".to-upload-button").click(function () { + layer.prompt({title: '请输入文件前缀,支持规则', formType: 0}, function (prefix, index) { + layer.close(index); + window.open("upload?prefix=" + encodeURI(prefix)); + }); + }); + $("#file-modify-button").click(function () { + var localUrl = $("#new-file-local-url").val(); + var visitUrl = $("#new-file-visit-url").val(); + if (isEmpty(localUrl) && isEmpty(visitUrl)) { + alerts("内容为空,无法更新"); + } else { + layer.load(1); + $.ajax({ + url: "/file/" + $("#file-id").val() + "/url", + data: {oldLocalUrl: $("#old-file-local-url").val(), localUrl: localUrl, visitUrl: visitUrl}, + type: "PUT", + success: function (data) { + layer.closeAll(); + var json = JSON.parse(data); + var key = $(selectedRows[rowIndex]).children(".file-index").attr("data-key"); + if (json.status.localUrl) { + app.files[key].localUrl = localUrl; + $("[data-toggle='tooltip']").tooltip(); + } + if (json.status.visitUrl) { + app.files[key].visitUrl = visitUrl; + } + alerts((isEmpty(localUrl) ? "" : "更新本地路径" + boolToChinese(json.status.localUrl)) + (isEmpty(visitUrl) ? "" : (isEmpty(localUrl) ? "" : ",") + "更新访问链接" + boolToChinese(json.status.visitUrl))); + }, + error: function () { + alerts("连接到服务器异常"); + } + }); + } + }); + $(".file-delete").click(function () { + doDelete("#file-manager-table", app.files, ".file-index", "/file/batch/"); + }); +}); \ No newline at end of file diff --git a/src/main/resources/assets/js/angular-filemanager.min.js b/src/main/resources/assets/js/angular-filemanager.min.js new file mode 100644 index 0000000..d83ad5f --- /dev/null +++ b/src/main/resources/assets/js/angular-filemanager.min.js @@ -0,0 +1,2240 @@ +!function (e, r) { + "use strict"; + r.module("FileManagerApp", ["pascalprecht.translate", "ngFileUpload"]), r.element(e.document).on("shown.bs.modal", ".modal", function () { + e.setTimeout(function () { + r.element("[autofocus]", this).focus() + }.bind(this), 100) + }), r.element(e.document).on("click", function () { + r.element("#context-menu").hide() + }), r.element(e.document).on("contextmenu", '.main-navigation .table-files tr.item-list:has("td"), .item-list', function (n) { + var i = r.element("#context-menu"); + n.pageX >= e.innerWidth - i.width() && (n.pageX -= i.width()), n.pageY >= e.innerHeight - i.height() && (n.pageY -= i.height()), i.hide().css({ + left: n.pageX, + top: n.pageY + }).appendTo("body").show(), n.preventDefault() + }), Array.prototype.find || (Array.prototype.find = function (e) { + if (null == this) throw new TypeError("Array.prototype.find called on null or undefined"); + if ("function" != typeof e) throw new TypeError("predicate must be a function"); + for (var r, n = Object(this), i = n.length >>> 0, a = arguments[1], t = 0; t < i; t++) if (r = n[t], e.call(a, r, t, n)) return r + }) +}(window, angular), function (e) { + "use strict"; + e.module("FileManagerApp").controller("FileManagerCtrl", ["$scope", "$rootScope", "$window", "$translate", "fileManagerConfig", "item", "fileNavigator", "apiMiddleware", function (r, n, i, a, t, o, s, l) { + var d = i.localStorage; + r.config = t, r.reverse = !1, r.predicate = ["model.type", "model.name"], r.order = function (e) { + r.reverse = r.predicate[1] === e && !r.reverse, r.predicate[1] = e + }, r.query = "", r.fileNavigator = new s, r.apiMiddleware = new l, r.uploadFileList = [], r.viewTemplate = d.getItem("viewTemplate") || "main-icons.html", r.fileList = [], r.temps = [], r.$watch("temps", function () { + r.singleSelection() ? r.temp = r.singleSelection() : (r.temp = new o({rights: 644}), r.temp.multiple = !0), r.temp.revert() + }), r.fileNavigator.onRefresh = function () { + r.temps = [], r.query = "", n.selectedModalPath = r.fileNavigator.currentPath + }, r.setTemplate = function (e) { + d.setItem("viewTemplate", e), r.viewTemplate = e + }, r.changeLanguage = function (e) { + if (e) return d.setItem("language", e), a.use(e); + a.use(d.getItem("language") || t.defaultLang) + }, r.isSelected = function (e) { + return -1 !== r.temps.indexOf(e) + }, r.selectOrUnselect = function (e, n) { + var i = r.temps.indexOf(e), a = n && 3 == n.which; + if (n && n.target.hasAttribute("prevent")) r.temps = []; else if (!(!e || a && r.isSelected(e))) { + if (n && n.shiftKey && !a) { + var t = r.fileList, o = t.indexOf(e), s = r.temps[0], l = t.indexOf(s), d = void 0; + if (s && t.indexOf(s) < o) { + for (r.temps = []; l <= o;) d = t[l], !r.isSelected(d) && r.temps.push(d), l++; + return + } + if (s && t.indexOf(s) > o) { + for (r.temps = []; l >= o;) d = t[l], !r.isSelected(d) && r.temps.push(d), l--; + return + } + } + !n || a || !n.ctrlKey && !n.metaKey ? r.temps = [e] : r.isSelected(e) ? r.temps.splice(i, 1) : r.temps.push(e) + } + }, r.singleSelection = function () { + return 1 === r.temps.length && r.temps[0] + }, r.totalSelecteds = function () { + return {total: r.temps.length} + }, r.selectionHas = function (e) { + return r.temps.find(function (r) { + return r && r.model.type === e + }) + }, r.prepareNewFolder = function () { + var e = new o(null, r.fileNavigator.currentPath); + return r.temps = [e], e + }, r.smartClick = function (e) { + var n = r.config.allowedActions.pickFiles; + if (e.isFolder()) return r.fileNavigator.folderClick(e); + if ("function" == typeof r.config.pickCallback && n) { + if (!0 === r.config.pickCallback(e.model)) return + } + return e.isImage() ? r.config.previewImagesInModal ? r.openImagePreview(e) : r.apiMiddleware.download(e, !0) : e.isEditable() ? r.openEditItem(e) : void 0 + }, r.openImagePreview = function () { + var e = r.singleSelection(); + r.apiMiddleware.apiHandler.inprocess = !0, r.modal("imagepreview", null, !0).find("#imagepreview-target").attr("src", r.getUrl(e)).unbind("load error").on("load error", function () { + r.apiMiddleware.apiHandler.inprocess = !1, r.$apply() + }) + }, r.openEditItem = function () { + var e = r.singleSelection(); + r.apiMiddleware.getContent(e).then(function (r) { + e.tempModel.content = e.model.content = r.result + }), r.modal("edit") + }, r.modal = function (n, i, a) { + var t = e.element("#" + n); + return t.modal(i ? "hide" : "show"), r.apiMiddleware.apiHandler.error = "", r.apiMiddleware.apiHandler.asyncSuccess = !1, !a || t + }, r.modalWithPathSelector = function (e) { + return n.selectedModalPath = r.fileNavigator.currentPath, r.modal(e) + }, r.isInThisPath = function (e) { + return -1 !== (r.fileNavigator.currentPath.join("/") + "/").indexOf(e + "/") + }, r.edit = function () { + r.apiMiddleware.edit(r.singleSelection()).then(function () { + r.modal("edit", !0) + }) + }, r.changePermissions = function () { + r.apiMiddleware.changePermissions(r.temps, r.temp).then(function () { + r.fileNavigator.refresh(), r.modal("changepermissions", !0) + }) + }, r.download = function () { + var e = r.singleSelection(); + if (!r.selectionHas("dir")) return e ? r.apiMiddleware.download(e) : r.apiMiddleware.downloadMultiple(r.temps) + }, r.copy = function () { + var e = r.singleSelection(); + if (e) { + var i = e.tempModel.name.trim(); + if (r.fileNavigator.fileNameExists(i) && c(e)) return r.apiMiddleware.apiHandler.error = a.instant("error_invalid_filename"), !1; + if (!i) return r.apiMiddleware.apiHandler.error = a.instant("error_invalid_filename"), !1 + } + r.apiMiddleware.copy(r.temps, n.selectedModalPath).then(function () { + r.fileNavigator.refresh(), r.modal("copy", !0) + }) + }, r.compress = function () { + var e = r.temp.tempModel.name.trim(); + return r.fileNavigator.fileNameExists(e) && c(r.temp) ? (r.apiMiddleware.apiHandler.error = a.instant("error_invalid_filename"), !1) : e ? void r.apiMiddleware.compress(r.temps, e, n.selectedModalPath).then(function () { + if (r.fileNavigator.refresh(), !r.config.compressAsync) return r.modal("compress", !0); + r.apiMiddleware.apiHandler.asyncSuccess = !0 + }, function () { + r.apiMiddleware.apiHandler.asyncSuccess = !1 + }) : (r.apiMiddleware.apiHandler.error = a.instant("error_invalid_filename"), !1) + }, r.extract = function () { + var e = r.temp, i = r.temp.tempModel.name.trim(); + return r.fileNavigator.fileNameExists(i) && c(r.temp) ? (r.apiMiddleware.apiHandler.error = a.instant("error_invalid_filename"), !1) : i ? void r.apiMiddleware.extract(e, i, n.selectedModalPath).then(function () { + if (r.fileNavigator.refresh(), !r.config.extractAsync) return r.modal("extract", !0); + r.apiMiddleware.apiHandler.asyncSuccess = !0 + }, function () { + r.apiMiddleware.apiHandler.asyncSuccess = !1 + }) : (r.apiMiddleware.apiHandler.error = a.instant("error_invalid_filename"), !1) + }, r.remove = function () { + r.apiMiddleware.remove(r.temps).then(function () { + r.fileNavigator.refresh(), r.modal("remove", !0) + }) + }, r.move = function () { + var e = r.singleSelection() || r.temps[0]; + if (e && c(e)) return r.apiMiddleware.apiHandler.error = a.instant("error_cannot_move_same_path"), !1; + r.apiMiddleware.move(r.temps, n.selectedModalPath).then(function () { + r.fileNavigator.refresh(), r.modal("move", !0) + }) + }, r.rename = function () { + var e = r.singleSelection(), n = e.tempModel.name, i = e.tempModel.path.join("") === e.model.path.join(""); + if (!n || i && r.fileNavigator.fileNameExists(n)) return r.apiMiddleware.apiHandler.error = a.instant("error_invalid_filename"), !1; + r.apiMiddleware.rename(e).then(function () { + r.fileNavigator.refresh(), r.modal("rename", !0) + }) + }, r.createFolder = function () { + var e = r.singleSelection(), n = e.tempModel.name; + if (!n || r.fileNavigator.fileNameExists(n)) return r.apiMiddleware.apiHandler.error = a.instant("error_invalid_filename"); + r.apiMiddleware.createFolder(e).then(function () { + r.fileNavigator.refresh(), r.modal("newfolder", !0) + }) + }, r.addForUpload = function (e) { + r.uploadFileList = r.uploadFileList.concat(e), r.modal("uploadfile") + }, r.removeFromUpload = function (e) { + r.uploadFileList.splice(e, 1) + }, r.uploadFiles = function () { + r.apiMiddleware.upload(r.uploadFileList, r.fileNavigator.currentPath).then(function () { + r.fileNavigator.refresh(), r.uploadFileList = [], r.modal("uploadfile", !0) + }, function (e) { + var n = e.result && e.result.error || a.instant("error_uploading_files"); + r.apiMiddleware.apiHandler.error = n + }) + }, r.getUrl = function (e) { + return r.apiMiddleware.getUrl(e) + }; + var c = function (e) { + var r = n.selectedModalPath.join(""); + return (e && e.model.path.join("")) === r + }, p = function (e) { + var r = i.location.search.substr(1).split("&").filter(function (r) { + return e === r.split("=")[0] + }); + return r[0] && r[0].split("=")[1] || void 0 + }; + r.changeLanguage(p("lang")), r.isWindows = "Windows" === p("server"), r.fileNavigator.refresh() + }]) +}(angular), function (e) { + "use strict"; + angular.module("FileManagerApp").controller("ModalFileManagerCtrl", ["$scope", "$rootScope", "fileNavigator", function (e, r, n) { + e.reverse = !1, e.predicate = ["model.type", "model.name"], e.fileNavigator = new n, r.selectedModalPath = [], e.order = function (r) { + e.reverse = e.predicate[1] === r && !e.reverse, e.predicate[1] = r + }, e.select = function (n) { + r.selectedModalPath = n.model.fullPath().split("/").filter(Boolean), e.modal("selector", !0) + }, e.selectCurrent = function () { + r.selectedModalPath = e.fileNavigator.currentPath, e.modal("selector", !0) + }, e.selectedFilesAreChildOfPath = function (r) { + var n = r.model.fullPath(); + return e.temps.find(function (e) { + var r = e.model.fullPath(); + if (n == r) return !0 + }) + }, r.openNavigator = function (r) { + e.fileNavigator.currentPath = r, e.fileNavigator.refresh(), e.modal("selector") + }, r.getSelectedPath = function () { + var n = "/" + r.selectedModalPath.filter(Boolean).join("/"); + return e.singleSelection() && !e.singleSelection().isFolder() && (n += "/" + e.singleSelection().tempModel.name), n.replace(/\/\//, "/") + } + }]) +}(), function (e) { + "use strict"; + var r = angular.module("FileManagerApp"); + r.directive("angularFilemanager", ["$parse", "fileManagerConfig", function (e, r) { + return {restrict: "EA", templateUrl: r.tplPath + "/main.html"} + }]), r.directive("ngFile", ["$parse", function (e) { + return { + restrict: "A", link: function (r, n, i) { + var a = e(i.ngFile).assign; + n.bind("change", function () { + r.$apply(function () { + a(r, n[0].files) + }) + }) + } + } + }]), r.directive("ngRightClick", ["$parse", function (e) { + return function (r, n, i) { + var a = e(i.ngRightClick); + n.bind("contextmenu", function (e) { + r.$apply(function () { + e.preventDefault(), a(r, {$event: e}) + }) + }) + } + }]) +}(), function (e) { + "use strict"; + angular.module("FileManagerApp").service("chmod", function () { + var e = function (e) { + if (this.owner = this.getRwxObj(), this.group = this.getRwxObj(), this.others = this.getRwxObj(), e) { + var r = isNaN(e) ? this.convertfromCode(e) : this.convertfromOctal(e); + if (!r) throw new Error("Invalid chmod input data (%s)".replace("%s", e)); + this.owner = r.owner, this.group = r.group, this.others = r.others + } + }; + return e.prototype.toOctal = function (e, r) { + var n = []; + return ["owner", "group", "others"].forEach(function (e, r) { + n[r] = this[e].read && this.octalValues.read || 0, n[r] += this[e].write && this.octalValues.write || 0, n[r] += this[e].exec && this.octalValues.exec || 0 + }.bind(this)), (e || "") + n.join("") + (r || "") + }, e.prototype.toCode = function (e, r) { + var n = []; + return ["owner", "group", "others"].forEach(function (e, r) { + n[r] = this[e].read && this.codeValues.read || "-", n[r] += this[e].write && this.codeValues.write || "-", n[r] += this[e].exec && this.codeValues.exec || "-" + }.bind(this)), (e || "") + n.join("") + (r || "") + }, e.prototype.getRwxObj = function () { + return {read: !1, write: !1, exec: !1} + }, e.prototype.octalValues = {read: 4, write: 2, exec: 1}, e.prototype.codeValues = { + read: "r", + write: "w", + exec: "x" + }, e.prototype.convertfromCode = function (e) { + if (e = ("" + e).replace(/\s/g, ""), e = 10 === e.length ? e.substr(1) : e, /^[-rwxts]{9}$/.test(e)) { + var r = [], n = e.match(/.{1,3}/g); + for (var i in n) { + var a = this.getRwxObj(); + a.read = /r/.test(n[i]), a.write = /w/.test(n[i]), a.exec = /x|t/.test(n[i]), r.push(a) + } + return {owner: r[0], group: r[1], others: r[2]} + } + }, e.prototype.convertfromOctal = function (e) { + if (e = ("" + e).replace(/\s/g, ""), e = 4 === e.length ? e.substr(1) : e, /^[0-7]{3}$/.test(e)) { + var r = [], n = e.match(/.{1}/g); + for (var i in n) { + var a = this.getRwxObj(); + a.read = /[4567]/.test(n[i]), a.write = /[2367]/.test(n[i]), a.exec = /[1357]/.test(n[i]), r.push(a) + } + return {owner: r[0], group: r[1], others: r[2]} + } + }, e + }) +}(), function (e) { + "use strict"; + e.module("FileManagerApp").factory("item", ["fileManagerConfig", "chmod", function (r, n) { + var i = function (r, i) { + var a = { + name: r && r.name || "", + path: i || [], + type: r && r.type || "file", + size: r && parseInt(r.size || 0), + date: function (e) { + var r = (e || "").toString().split(/[- :]/); + return new Date(r[0], r[1] - 1, r[2], r[3], r[4], r[5]) + }(r && r.date), + perms: new n(r && r.rights), + content: r && r.content || "", + recursive: !1, + fullPath: function () { + return ("/" + this.path.filter(Boolean).join("/") + "/" + this.name).replace(/\/\//, "/") + } + }; + this.error = "", this.processing = !1, this.model = e.copy(a), this.tempModel = e.copy(a) + }; + return i.prototype.update = function () { + e.extend(this.model, e.copy(this.tempModel)) + }, i.prototype.revert = function () { + e.extend(this.tempModel, e.copy(this.model)), this.error = "" + }, i.prototype.isFolder = function () { + return "dir" === this.model.type + }, i.prototype.isEditable = function () { + return !this.isFolder() && r.isEditableFilePattern.test(this.model.name) + }, i.prototype.isImage = function () { + return r.isImageFilePattern.test(this.model.name) + }, i.prototype.isCompressible = function () { + return this.isFolder() + }, i.prototype.isExtractable = function () { + return !this.isFolder() && r.isExtractableFilePattern.test(this.model.name) + }, i.prototype.isSelectable = function () { + return this.isFolder() && r.allowedActions.pickFolders || !this.isFolder() && r.allowedActions.pickFiles + }, i + }]) +}(angular), function (e) { + "use strict"; + var r = angular.module("FileManagerApp"); + r.filter("strLimit", ["$filter", function (e) { + return function (r, n, i) { + return r.length <= n ? r : e("limitTo")(r, n) + (i || "...") + } + }]), r.filter("fileExtension", ["$filter", function (e) { + return function (r) { + return /\./.test(r) && e("strLimit")(r.split(".").pop(), 3, "..") || "" + } + }]), r.filter("formatDate", ["$filter", function () { + return function (e) { + return e instanceof Date ? e.toISOString().substring(0, 19).replace("T", " ") : (e.toLocaleString || e.toString).apply(e) + } + }]), r.filter("humanReadableFileSize", ["$filter", "fileManagerConfig", function (e, r) { + var n = [" kB", " MB", " GB", " TB", "PB", "EB", "ZB", "YB"], + i = ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"]; + return function (e) { + var a = -1, t = e; + do { + t /= 1024, a++ + } while (t > 1024); + var o = r.useBinarySizePrefixes ? i[a] : n[a]; + return Math.max(t, .1).toFixed(1) + " " + o + } + }]) +}(), function (e) { + "use strict"; + e.module("FileManagerApp").provider("fileManagerConfig", function () { + var r = { + appName: "angular-filemanager v1.5", + defaultLang: "zh_cn", + listUrl: "/filemanager/list", + uploadUrl: "/filemanager/upload", + renameUrl: "/filemanager/rename", + copyUrl: "/filemanager/copy", + moveUrl: "/filemanager/move", + removeUrl: "/filemanager/remove", + editUrl: "/filemanager/edit", + getContentUrl: "/filemanager/content", + createFolderUrl: "/filemanager/folder", + downloadFileUrl: "/filemanager/download", + downloadMultipleUrl: "/filemanager/multidownload", + compressUrl: "/filemanager/compress", + extractUrl: "/filemanager/extract", + permissionsUrl: "/filemanager/permission", + basePath: "/", + searchForm: !0, + sidebar: !0, + breadcrumb: !0, + allowedActions: { + upload: !0, + rename: !0, + move: !0, + copy: !0, + edit: !0, + changePermissions: !0, + compress: !0, + compressChooseName: !0, + extract: !0, + download: !0, + downloadMultiple: !0, + preview: !0, + remove: !0, + createFolder: !0, + pickFiles: !1, + pickFolders: !1 + }, + multipleDownloadFileName: "efo-download.zip", + filterFileExtensions: [], + showExtensionIcons: !0, + showSizeForDirectories: !0, + useBinarySizePrefixes: !1, + downloadFilesByAjax: !0, + previewImagesInModal: !0, + enablePermissionsRecursive: !0, + compressAsync: !1, + extractAsync: !1, + pickCallback: null, + isEditableFilePattern: /\.(txt|diff?|patch|svg|asc|cnf|cfg|conf|html?|.html|cfm|cgi|aspx?|ini|pl|py|md|css|cs|js|jsp|log|htaccess|htpasswd|gitignore|gitattributes|env|json|atom|eml|rss|markdown|sql|xml|xslt?|sh|rb|as|bat|cmd|cob|for|ftn|frm|frx|inc|lisp|scm|coffee|php[3-6]?|java|c|cbl|go|h|scala|vb|tmpl|lock|go|yml|yaml|tsv|lst)$/i, + isImageFilePattern: /\.(jpe?g|gif|bmp|png|svg|tiff?)$/i, + isExtractableFilePattern: /\.(gz|tar|rar|g?zip)$/i, + tplPath: "src/templates" + }; + return { + $get: function () { + return r + }, set: function (n) { + e.extend(r, n) + } + } + }) +}(angular), function (e) { + "use strict"; + angular.module("FileManagerApp").config(["$translateProvider", function (e) { + e.useSanitizeValueStrategy(null), e.translations("en", { + filemanager: "File Manager", + language: "Language", + english: "English", + spanish: "Spanish", + portuguese: "Portuguese", + french: "French", + german: "German", + hebrew: "Hebrew", + italian: "Italian", + slovak: "Slovak", + chinese_tw: "Traditional Chinese", + chinese_cn: "Simple Chinese", + russian: "Russian", + ukrainian: "Ukrainian", + turkish: "Turkish", + persian: "Persian", + polish: "Polish", + dutch: "Dutch", + confirm: "Confirm", + cancel: "Cancel", + close: "Close", + upload_files: "Upload files", + files_will_uploaded_to: "Files will be uploaded to", + select_files: "Select files", + uploading: "Uploading", + permissions: "Permissions", + select_destination_folder: "Select the destination folder", + source: "Source", + destination: "Destination", + copy_file: "Copy file", + sure_to_delete: "Are you sure to delete", + change_name_move: "Change name / move", + enter_new_name_for: "Enter new name for", + extract_item: "Extract item", + extraction_started: "Extraction started in a background process", + compression_started: "Compression started in a background process", + enter_folder_name_for_extraction: "Enter the folder name for the extraction of", + enter_file_name_for_compression: "Enter the file name for the compression of", + toggle_fullscreen: "Toggle fullscreen", + edit_file: "Edit file", + file_content: "File content", + loading: "Loading", + search: "Search", + create_folder: "Create folder", + create: "Create", + folder_name: "Folder name", + upload: "Upload", + change_permissions: "Change permissions", + change: "Change", + details: "Details", + icons: "Icons", + list: "List", + name: "Name", + size: "Size", + actions: "Actions", + date: "Date", + selection: "Selection", + no_files_in_folder: "No files in this folder", + no_folders_in_folder: "This folder not contains children folders", + select_this: "Select this", + go_back: "Go back", + wait: "Wait", + move: "Move", + download: "Download", + view_item: "View item", + remove: "Delete", + edit: "Edit", + copy: "Copy", + rename: "Rename", + extract: "Extract", + compress: "Compress", + error_invalid_filename: "Invalid filename or already exists, specify another name", + error_modifying: "An error occurred modifying the file", + error_deleting: "An error occurred deleting the file or folder", + error_renaming: "An error occurred renaming the file", + error_copying: "An error occurred copying the file", + error_compressing: "An error occurred compressing the file or folder", + error_extracting: "An error occurred extracting the file", + error_creating_folder: "An error occurred creating the folder", + error_getting_content: "An error occurred getting the content of the file", + error_changing_perms: "An error occurred changing the permissions of the file", + error_uploading_files: "An error occurred uploading files", + sure_to_start_compression_with: "Are you sure to compress", + owner: "Owner", + group: "Group", + others: "Others", + read: "Read", + write: "Write", + exec: "Exec", + original: "Original", + changes: "Changes", + recursive: "Recursive", + preview: "Item preview", + open: "Open", + these_elements: "these {{total}} elements", + new_folder: "New folder", + download_as_zip: "Download as ZIP" + }), e.translations("nl", { + filemanager: "Bestand beheerder", + language: "Taal", + english: "Engels", + spanish: "Spaans", + portuguese: "Portugees", + french: "Frans", + german: "Duits", + hebrew: "Hebrews", + slovak: "Slowakije", + chinese: "Chinees", + russian: "Russisch", + ukrainian: "Oekraïens", + turkish: "Turks", + persian: "Perzisch", + confirm: "Bevestigen", + cancel: "Annuleren", + close: "Sluiten", + upload_files: "Bestanden uploaden", + files_will_uploaded_to: "Bestanden worden geüpload naar", + select_files: "Selecteer bestanden", + uploading: "Uploaden", + permissions: "Rechten", + select_destination_folder: "Selecteer de map van bestemming", + source: "Bron", + destination: "Doel", + copy_file: "Kopieer bestand", + sure_to_delete: "Weet je zeker dat je wilt verwijderen", + change_name_move: "Hernoemen / verplaatsen", + enter_new_name_for: "Typ een nieuwe naam voor", + extract_item: "Uitpakken", + extraction_started: "Uitpakken gestart als achtergrond proces", + compression_started: "Inpakken gestart als achtergrond proces", + enter_folder_name_for_extraction: "Typ een map naar voor het uitpakken van", + enter_file_name_for_compression: "Typ een bestandsnaam voor het inpakken van", + toggle_fullscreen: "Volledigscherm", + edit_file: "Bewerk bestand", + file_content: "Bestandsinhoud", + loading: "Laden", + search: "Zoeken", + create_folder: "Maak map", + create: "Maak", + folder_name: "Map naam", + upload: "Uploaden", + change_permissions: "Rechten aanpassen", + change: "Aanpassen", + details: "Details", + icons: "Iconen", + list: "Lijst", + name: "Naam", + size: "Grootte", + actions: "Acties", + date: "Datum", + no_files_in_folder: "Geen bestanden in deze map", + no_folders_in_folder: "Deze map bevat geen submappen", + select_this: "Selecteer dit", + go_back: "Ga terug", + wait: "Wacht", + move: "Verplaats", + download: "Download", + view_item: "Bekijk item", + remove: "Verwijderen", + edit: "Bewerken", + copy: "Kopiëren", + rename: "Hernoemen", + extract: "Uitpakken", + compress: "Inpakken", + error_invalid_filename: "Ongeldige bestandsnaam of bestand al aanwezig, kies een andere naam", + error_modifying: "Er is een fout opgetreden met het bewerken van het bestand", + error_deleting: "Er is een fout opgetreden tijdens het verwijderen van de bestand of map", + error_renaming: "Er is een fout opgetreden tijdens het hernoemen van het bestand", + error_copying: "Er is een fout opgetreden tijdens het kopiëren van het bestand", + error_compressing: "Er is een fout opgetreden tijdens het inpakken van het bestand of map", + error_extracting: "Er is een fout opgetreden tijdens het uitpakken van het bestand", + error_creating_folder: "Er is een fout opgetreden tijdens het maken van de map", + error_getting_content: "Er is een fout opgetreden tijdens het ophalen van de inhoud van het bestand", + error_changing_perms: "Er is een fout opgetreden tijdens het aanpassen van de rechten van het bestand", + error_uploading_files: "Er is een fout opgetreden tijdens het uploaden van de bestanden", + sure_to_start_compression_with: "Weet je zeker dat je dit wilt inpakken", + owner: "Eigenaar", + group: "Groep", + others: "Andere", + read: "Lees", + write: "Schrijf", + exec: "Uitvoeren", + original: "Origineel", + changes: "Aanpassingen", + recursive: "Recursieve", + preview: "Item bekijken", + open: "Openen", + these_elements: "Deze {{total}} elementen", + new_folder: "Nieuwe map", + download_as_zip: "Download als ZIP" + }), e.translations("he", { + filemanager: "מנהל קבצים", + language: "שפה", + english: "אנגלית", + spanish: "ספרדית", + portuguese: "פורטוגזית", + french: "צרפתית", + german: "גרמנית", + hebrew: "עברי", + italian: "איטלקי", + slovak: "סלובקי", + chinese_tw: "סינית מסורתית", + chinese_cn: "סינית פשוטה", + russian: "רוּסִי", + ukrainian: "אוקראיני", + turkish: "טורקי", + persian: "פַּרסִית", + polish: "פולני", + confirm: "אשר", + cancel: "בטל", + close: "סגור", + upload_files: "העלה קבצים", + files_will_uploaded_to: "הקבצים יעלו ל", + select_files: "בחר קבצים", + uploading: "מעלה", + permissions: "הרשאות", + select_destination_folder: "בחר תיקיית יעד", + source: "מקור", + destination: "יעד", + copy_file: "העתק קובץ", + sure_to_delete: "האם אתה בטוח שברצונך למחוק", + change_name_move: "שנה שם / הזז", + enter_new_name_for: "הקלד שם חדש עבור", + extract_item: "חלץ פריט", + extraction_started: "תהליך החילוץ מתבצע ברקע", + compression_started: "תהליך הכיווץ מתבצע ברקע", + enter_folder_name_for_extraction: "הקלד שם תיקייה לחילוץ עבור", + enter_file_name_for_compression: "הזן את שם הקובץ עבור הדחיסה של", + toggle_fullscreen: "הפעל/בטל מסך מלא", + edit_file: "ערוך קובץ", + file_content: "תוכן הקובץ", + loading: "טוען", + search: "חפש", + create_folder: "צור תיקייה", + create: "צור", + folder_name: "שם תיקייה", + upload: "העלה", + change_permissions: "שנה הרשאות", + change: "שנה", + details: "פרטים", + icons: "סמלים", + list: "רשימה", + name: "שם", + size: "גודל", + actions: "פעולות", + date: "תאריך", + selection: "בְּחִירָה", + no_files_in_folder: "אין קבצים בתיקייה זו", + no_folders_in_folder: "התיקייה הזו אינה כוללת תתי תיקיות", + select_this: "בחר את זה", + go_back: "חזור אחורה", + wait: "חכה", + move: "הזז", + download: "הורד", + view_item: "הצג פריט", + remove: "מחק", + edit: "ערוך", + copy: "העתק", + rename: "שנה שם", + extract: "חלץ", + compress: "כווץ", + error_invalid_filename: "שם קובץ אינו תקין או קיים, ציין שם קובץ אחר", + error_modifying: "התרחשה שגיאה בעת שינוי הקובץ", + error_deleting: "התרחשה שגיאה בעת מחיקת הקובץ או התיקייה", + error_renaming: "התרחשה שגיאה בעת שינוי שם הקובץ", + error_copying: "התרחשה שגיאה בעת העתקת הקובץ", + error_compressing: "התרחשה שגיאה בעת כיווץ הקובץ או התיקייה", + error_extracting: "התרחשה שגיאה בעת חילוץ הקובץ או התיקייה", + error_creating_folder: "התרחשה שגיאה בעת יצירת התיקייה", + error_getting_content: "התרחשה שגיאה בעת בקשת תוכן הקובץ", + error_changing_perms: "התרחשה שגיאה בעת שינוי הרשאות הקובץ", + error_uploading_files: "התרחשה שגיאה בעת העלאת הקבצים", + sure_to_start_compression_with: "האם אתה בטוח שברצונך לכווץ", + owner: "בעלים", + group: "קבוצה", + others: "אחרים", + read: "קריאה", + write: "כתיבה", + exec: "הרצה", + original: "מקורי", + changes: "שינויים", + recursive: "רקורסיה", + preview: "הצגת פריט", + open: "פתח", + new_folder: "תיקיה חדשה", + download_as_zip: "להוריד כמו" + }), e.translations("pt", { + filemanager: "Gerenciador de arquivos", + language: "Língua", + english: "Inglês", + spanish: "Espanhol", + portuguese: "Portugues", + french: "Francês", + german: "Alemão", + hebrew: "Hebraico", + italian: "Italiano", + slovak: "Eslovaco", + chinese_tw: "Tradicional Chinesa", + chinese_cn: "Chinês Simplificado", + russian: "Russo", + ukrainian: "Ucraniano", + turkish: "Turco", + persian: "Persa", + polish: "Polonês", + confirm: "Confirmar", + cancel: "Cancelar", + close: "Fechar", + upload_files: "Carregar arquivos", + files_will_uploaded_to: "Os arquivos serão enviados para", + select_files: "Selecione os arquivos", + uploading: "Carregar", + permissions: "Autorizações", + select_destination_folder: "Selecione a pasta de destino", + source: "Origem", + destination: "Destino", + copy_file: "Copiar arquivo", + sure_to_delete: "Tem certeza de que deseja apagar", + change_name_move: "Renomear / mudança", + enter_new_name_for: "Digite o novo nome para", + extract_item: "Extrair arquivo", + extraction_started: "A extração começou em um processo em segundo plano", + compression_started: "A compressão começou em um processo em segundo plano", + enter_folder_name_for_extraction: "Digite o nome da pasta para a extração de", + enter_file_name_for_compression: "Digite o nome do arquivo para a compressão de", + toggle_fullscreen: "Ativar/desativar tela cheia", + edit_file: "Editar arquivo", + file_content: "Conteúdo do arquivo", + loading: "Carregando", + search: "Localizar", + create_folder: "Criar Pasta", + create: "Criar", + folder_name: "Nome da pasta", + upload: "Fazer", + change_permissions: "Alterar permissões", + change: "Alterar", + details: "Detalhes", + icons: "Icones", + list: "Lista", + name: "Nome", + size: "Tamanho", + actions: "Ações", + date: "Data", + selection: "Seleção", + no_files_in_folder: "Não há arquivos nesta pasta", + no_folders_in_folder: "Esta pasta não contém subpastas", + select_this: "Selecione esta", + go_back: "Voltar", + wait: "Espere", + move: "Mover", + download: "Baixar", + view_item: "Veja o arquivo", + remove: "Excluir", + edit: "Editar", + copy: "Copiar", + rename: "Renomear", + extract: "Extrair", + compress: "Comprimir", + error_invalid_filename: "Nome do arquivo inválido ou nome de arquivo já existe, especifique outro nome", + error_modifying: "Ocorreu um erro ao modificar o arquivo", + error_deleting: "Ocorreu um erro ao excluir o arquivo ou pasta", + error_renaming: "Ocorreu um erro ao mudar o nome do arquivo", + error_copying: "Ocorreu um erro ao copiar o arquivo", + error_compressing: "Ocorreu um erro ao comprimir o arquivo ou pasta", + error_extracting: "Ocorreu um erro ao extrair o arquivo", + error_creating_folder: "Ocorreu um erro ao criar a pasta", + error_getting_content: "Ocorreu um erro ao obter o conteúdo do arquivo", + error_changing_perms: "Ocorreu um erro ao alterar as permissões do arquivo", + error_uploading_files: "Ocorreu um erro upload de arquivos", + sure_to_start_compression_with: "Tem certeza que deseja comprimir", + owner: "Proprietário", + group: "Grupo", + others: "Outros", + read: "Leitura", + write: "Escrita ", + exec: "Execução", + original: "Original", + changes: "Mudanças", + recursive: "Recursiva", + preview: "Visualização", + open: "Abrir", + these_elements: "estes {{total}} elements", + new_folder: "Nova pasta", + download_as_zip: "Download como ZIP" + }), e.translations("es", { + filemanager: "Administrador de archivos", + language: "Idioma", + english: "Ingles", + spanish: "Español", + portuguese: "Portugues", + french: "Francés", + german: "Alemán", + hebrew: "Hebreo", + italian: "Italiano", + slovak: "Eslovaco", + chinese_tw: "Tradicional China", + chinese_cn: "Chino Simplificado", + russian: "Ruso", + ukrainian: "Ucraniano", + turkish: "Turco", + persian: "Persa", + polish: "Polaco", + confirm: "Confirmar", + cancel: "Cancelar", + close: "Cerrar", + upload_files: "Subir archivos", + files_will_uploaded_to: "Los archivos seran subidos a", + select_files: "Seleccione los archivos", + uploading: "Subiendo", + permissions: "Permisos", + select_destination_folder: "Seleccione la carpeta de destino", + source: "Origen", + destination: "Destino", + copy_file: "Copiar archivo", + sure_to_delete: "Esta seguro que desea eliminar", + change_name_move: "Renombrar / mover", + enter_new_name_for: "Ingrese el nuevo nombre para", + extract_item: "Extraer archivo", + extraction_started: "La extraccion ha comenzado en un proceso de segundo plano", + compression_started: "La compresion ha comenzado en un proceso de segundo plano", + enter_folder_name_for_extraction: "Ingrese el nombre de la carpeta para la extraccion de", + enter_file_name_for_compression: "Ingrese el nombre del archivo para la compresion de", + toggle_fullscreen: "Activar/Desactivar pantalla completa", + edit_file: "Editar archivo", + file_content: "Contenido del archivo", + loading: "Cargando", + search: "Buscar", + create_folder: "Crear carpeta", + create: "Crear", + folder_name: "Nombre de la carpeta", + upload: "Subir", + change_permissions: "Cambiar permisos", + change: "Cambiar", + details: "Detalles", + icons: "Iconos", + list: "Lista", + name: "Nombre", + size: "Tamaño", + actions: "Acciones", + date: "Fecha", + selection: "Selección", + no_files_in_folder: "No hay archivos en esta carpeta", + no_folders_in_folder: "Esta carpeta no contiene sub-carpetas", + select_this: "Seleccionar esta", + go_back: "Volver", + wait: "Espere", + move: "Mover", + download: "Descargar", + view_item: "Ver archivo", + remove: "Eliminar", + edit: "Editar", + copy: "Copiar", + rename: "Renombrar", + extract: "Extraer", + compress: "Comprimir", + error_invalid_filename: "El nombre del archivo es invalido o ya existe", + error_modifying: "Ocurrio un error al intentar modificar el archivo", + error_deleting: "Ocurrio un error al intentar eliminar el archivo", + error_renaming: "Ocurrio un error al intentar renombrar el archivo", + error_copying: "Ocurrio un error al intentar copiar el archivo", + error_compressing: "Ocurrio un error al intentar comprimir el archivo", + error_extracting: "Ocurrio un error al intentar extraer el archivo", + error_creating_folder: "Ocurrio un error al intentar crear la carpeta", + error_getting_content: "Ocurrio un error al obtener el contenido del archivo", + error_changing_perms: "Ocurrio un error al cambiar los permisos del archivo", + error_uploading_files: "Ocurrio un error al subir archivos", + sure_to_start_compression_with: "Esta seguro que desea comprimir", + owner: "Propietario", + group: "Grupo", + others: "Otros", + read: "Lectura", + write: "Escritura", + exec: "Ejecucion", + original: "Original", + changes: "Cambios", + recursive: "Recursivo", + preview: "Vista previa", + open: "Abrir", + these_elements: "estos {{total}} elementos", + new_folder: "Nueva carpeta", + download_as_zip: "Descargar como ZIP" + }), e.translations("fr", { + filemanager: "Gestionnaire de fichier", + language: "Langue", + english: "Anglais", + spanish: "Espagnol", + portuguese: "Portugais", + french: "Français", + german: "Allemand", + hebrew: "Hébreu", + italian: "Italien", + slovak: "Slovaque", + chinese_tw: "Traditionnelle Chinoise", + chinese_cn: "Chinois Simplifié", + russian: "Russe", + ukrainian: "Ukrainien", + turkish: "Turc", + persian: "Persan", + polish: "Polonais", + confirm: "Confirmer", + cancel: "Annuler", + close: "Fermer", + upload_files: "Télécharger des fichiers", + files_will_uploaded_to: "Les fichiers seront uploadé dans", + select_files: "Sélectionnez les fichiers", + uploading: "Upload en cours", + permissions: "Permissions", + select_destination_folder: "Sélectionné le dossier de destination", + source: "Source", + destination: "Destination", + copy_file: "Copier le fichier", + sure_to_delete: "Êtes-vous sûr de vouloir supprimer", + change_name_move: "Renommer / Déplacer", + enter_new_name_for: "Entrer le nouveau nom pour", + extract_item: "Extraires les éléments", + extraction_started: "L'extraction a démarré en tâche de fond", + compression_started: "La compression a démarré en tâche de fond", + enter_folder_name_for_extraction: "Entrer le nom du dossier pour l'extraction de", + enter_file_name_for_compression: "Entrez le nom de fichier pour la compression de", + toggle_fullscreen: "Basculer en plein écran", + edit_file: "Éditer le fichier", + file_content: "Contenu du fichier", + loading: "Chargement en cours", + search: "Recherche", + create_folder: "Créer un dossier", + create: "Créer", + folder_name: "Nom du dossier", + upload: "Upload", + change_permissions: "Changer les permissions", + change: "Changer", + details: "Details", + icons: "Icons", + list: "Liste", + name: "Nom", + size: "Taille", + actions: "Actions", + date: "Date", + selection: "Sélection", + no_files_in_folder: "Aucun fichier dans ce dossier", + no_folders_in_folder: "Ce dossier ne contiens pas de dossier", + select_this: "Sélectionner", + go_back: "Retour", + wait: "Patienter", + move: "Déplacer", + download: "Télécharger", + view_item: "Voir l'élément", + remove: "Supprimer", + edit: "Éditer", + copy: "Copier", + rename: "Renommer", + extract: "Extraire", + compress: "Compresser", + error_invalid_filename: "Nom de fichier invalide ou déjà existant, merci de spécifier un autre nom", + error_modifying: "Une erreur est survenue pendant la modification du fichier", + error_deleting: "Une erreur est survenue pendant la suppression du fichier ou du dossier", + error_renaming: "Une erreur est survenue pendant le renommage du fichier", + error_copying: "Une erreur est survenue pendant la copie du fichier", + error_compressing: "Une erreur est survenue pendant la compression du fichier ou du dossier", + error_extracting: "Une erreur est survenue pendant l'extraction du fichier", + error_creating_folder: "Une erreur est survenue pendant la création du dossier", + error_getting_content: "Une erreur est survenue pendant la récupération du contenu du fichier", + error_changing_perms: "Une erreur est survenue pendant le changement des permissions du fichier", + error_uploading_files: "Une erreur est survenue pendant l'upload des fichiers", + sure_to_start_compression_with: "Êtes-vous sûre de vouloir compresser", + owner: "Propriétaire", + group: "Groupe", + others: "Autres", + read: "Lecture", + write: "Écriture", + exec: "Éxécution", + original: "Original", + changes: "Modifications", + recursive: "Récursif", + preview: "Aperçu", + open: "Ouvrir", + these_elements: "ces {{total}} éléments", + new_folder: "Nouveau dossier", + download_as_zip: "Télécharger comme ZIP" + }), e.translations("de", { + filemanager: "Dateimanager", + language: "Sprache", + english: "Englisch", + spanish: "Spanisch", + portuguese: "Portugiesisch", + french: "Französisch", + german: "Deutsch", + hebrew: "Hebräisch", + italian: "Italienisch", + slovak: "Slowakisch", + chinese_tw: "Traditionelles Chinesisch", + chinese_cn: "Vereinfachtes Chinesisch", + russian: "Russisch", + ukrainian: "Ukrainisch", + turkish: "Türkisch", + persian: "Persisch", + polish: "Polnisch", + confirm: "Bestätigen", + cancel: "Abbrechen", + close: "Schließen", + upload_files: "Hochladen von Dateien", + files_will_uploaded_to: "Dateien werden hochgeladen nach", + select_files: "Wählen Sie die Dateien", + uploading: "Lade hoch", + permissions: "Berechtigungen", + select_destination_folder: "Wählen Sie einen Zielordner", + source: "Quelle", + destination: "Ziel", + copy_file: "Datei kopieren", + sure_to_delete: "Sind Sie sicher, dass Sie die Datei löschen möchten?", + change_name_move: "Namen ändern / verschieben", + enter_new_name_for: "Geben Sie den neuen Namen ein für", + extract_item: "Archiv entpacken", + extraction_started: "Entpacken hat im Hintergrund begonnen", + compression_started: "Komprimierung hat im Hintergrund begonnen", + enter_folder_name_for_extraction: "Geben Sie den Verzeichnisnamen für die Entpackung an von", + enter_file_name_for_compression: "Geben Sie den Dateinamen für die Kompression an von", + toggle_fullscreen: "Vollbild umschalten", + edit_file: "Datei bearbeiten", + file_content: "Dateiinhalt", + loading: "Lade", + search: "Suche", + create_folder: "Ordner erstellen", + create: "Erstellen", + folder_name: "Verzeichnisname", + upload: "Hochladen", + change_permissions: "Berechtigungen ändern", + change: "Ändern", + details: "Details", + icons: "Symbolansicht", + list: "Listenansicht", + name: "Name", + size: "Größe", + actions: "Aktionen", + date: "Datum", + selection: "Auswahl", + no_files_in_folder: "Keine Dateien in diesem Ordner", + no_folders_in_folder: "Dieser Ordner enthält keine Unterordner", + select_this: "Auswählen", + go_back: "Zurück", + wait: "Warte", + move: "Verschieben", + download: "Herunterladen", + view_item: "Datei ansehen", + remove: "Löschen", + edit: "Bearbeiten", + copy: "Kopieren", + rename: "Umbenennen", + extract: "Entpacken", + compress: "Komprimieren", + error_invalid_filename: "Ungültiger Dateiname oder existiert bereits", + error_modifying: "Beim Bearbeiten der Datei ist ein Fehler aufgetreten", + error_deleting: "Beim Löschen der Datei oder des Ordners ist ein Fehler aufgetreten", + error_renaming: "Beim Umbennenen der Datei ist ein Fehler aufgetreten", + error_copying: "Beim Kopieren der Datei ist ein Fehler aufgetreten", + error_compressing: "Beim Komprimieren der Datei oder des Ordners ist ein Fehler aufgetreten", + error_extracting: "Beim Entpacken der Datei ist ein Fehler aufgetreten", + error_creating_folder: "Beim Erstellen des Ordners ist ein Fehler aufgetreten", + error_getting_content: "Beim Laden des Dateiinhalts ist ein Fehler aufgetreten", + error_changing_perms: "Beim Ändern der Dateiberechtigungen ist ein Fehler aufgetreten", + error_uploading_files: "Beim Hochladen der Dateien ist ein Fehler aufgetreten", + sure_to_start_compression_with: "Möchten Sie die Datei wirklich komprimieren?", + owner: "Besitzer", + group: "Gruppe", + others: "Andere", + read: "Lesen", + write: "Schreiben", + exec: "Ausführen", + original: "Original", + changes: "Änderungen", + recursive: "Rekursiv", + preview: "Dateivorschau", + open: "Öffnen", + these_elements: "diese {{total}} elemente", + new_folder: "Neuer ordner", + download_as_zip: "Download als ZIP" + }), e.translations("sk", { + filemanager: "Správca súborov", + language: "Jazyk", + english: "Angličtina", + spanish: "Španielčina", + portuguese: "Portugalčina", + french: "Francúzština", + german: "Nemčina", + hebrew: "Hebrejčina", + italian: "Italština", + slovak: "Slovenčina", + chinese_tw: "Tradičná Čínska", + chinese_cn: "Zjednodušená Čínština", + russian: "Ruský", + ukrainian: "Ukrajinský", + turkish: "Turecký", + persian: "Perzský", + polish: "Poľský", + confirm: "Potvrdiť", + cancel: "Zrušiť", + close: "Zavrieť", + upload_files: "Nahrávať súbory", + files_will_uploaded_to: "Súbory budú nahrané do", + select_files: "Vybrať súbory", + uploading: "Nahrávanie", + permissions: "Oprávnenia", + select_destination_folder: "Vyberte cieľový príečinok", + source: "Zdroj", + destination: "Cieľ", + copy_file: "Kopírovať súbor", + sure_to_delete: "Ste si istý, že chcete vymazať", + change_name_move: "Premenovať / Premiestniť", + enter_new_name_for: "Zadajte nové meno pre", + extract_item: "Rozbaliť položku", + extraction_started: "Rozbaľovanie začalo v procese na pozadí", + compression_started: "Kompresia začala v procese na pzoadí", + enter_folder_name_for_extraction: "Zadajte názov priečinka na rozbalenie", + enter_file_name_for_compression: "Zadajte názov súboru pre kompresiu", + toggle_fullscreen: "Prepnúť režim na celú obrazovku", + edit_file: "Upraviť súbor", + file_content: "Obsah súboru", + loading: "Načítavanie", + search: "Hľadať", + create_folder: "Vytvoriť priečinok", + create: "Vytvoriť", + folder_name: "Názov priećinka", + upload: "Nahrať", + change_permissions: "Zmeniť oprávnenia", + change: "Zmeniť", + details: "Podrobnosti", + icons: "Ikony", + list: "Zoznam", + name: "Meno", + size: "Veľkosť", + actions: "Akcie", + date: "Dátum", + selection: "Výber", + no_files_in_folder: "V tom to priečinku nie sú žiadne súbory", + no_folders_in_folder: "Tento priečinok neobsahuje žiadne ďalšie priećinky", + select_this: "Vybrať tento", + go_back: "Ísť späť", + wait: "Počkajte", + move: "Presunúť", + download: "Stiahnuť", + view_item: "Zobraziť položku", + remove: "Vymazať", + edit: "Upraviť", + copy: "Kopírovať", + rename: "Premenovať", + extract: "Rozbaliť", + compress: "Komprimovať", + error_invalid_filename: "Neplatné alebo duplicitné meno súboru, vyberte iné meno", + error_modifying: "Vyskytla sa chyba pri upravovaní súboru", + error_deleting: "Vyskytla sa chyba pri mazaní súboru alebo priečinku", + error_renaming: "Vyskytla sa chyba pri premenovaní súboru", + error_copying: "Vyskytla sa chyba pri kopírovaní súboru", + error_compressing: "Vyskytla sa chyba pri komprimovaní súboru alebo priečinka", + error_extracting: "Vyskytla sa chyba pri rozbaľovaní súboru", + error_creating_folder: "Vyskytla sa chyba pri vytváraní priečinku", + error_getting_content: "Vyskytla sa chyba pri získavaní obsahu súboru", + error_changing_perms: "Vyskytla sa chyba pri zmene oprávnení súboru", + error_uploading_files: "Vyskytla sa chyba pri nahrávaní súborov", + sure_to_start_compression_with: "Ste si istý, že chcete komprimovať", + owner: "Vlastník", + group: "Skupina", + others: "Ostatní", + read: "Čítanie", + write: "Zapisovanie", + exec: "Spúštanie", + original: "Originál", + changes: "Zmeny", + recursive: "Rekurzívne", + preview: "Náhľad položky", + open: "Otvoriť", + these_elements: "týchto {{total}} prvkov", + new_folder: "Nový priečinok", + download_as_zip: "Stiahnuť ako ZIP" + }), e.translations("zh_cn", { + filemanager: "文档管理器", + language: "语言", + english: "英语", + spanish: "西班牙语", + portuguese: "葡萄牙语", + french: "法语", + german: "德语", + hebrew: "希伯来语", + italian: "意大利", + slovak: "斯洛伐克语", + chinese_tw: "正体中文", + chinese_cn: "简体中文", + russian: "俄語", + ukrainian: "烏克蘭", + turkish: "土耳其", + persian: "波斯語", + polish: "波兰语", + confirm: "确定", + cancel: "取消", + close: "关闭", + upload_files: "上传文件", + files_will_uploaded_to: "文件将上传到", + select_files: "选择文件", + uploading: "上传中", + permissions: "权限", + select_destination_folder: "选择目标文件", + source: "源自", + destination: "目的地", + copy_file: "复制文件", + sure_to_delete: "确定要删除?", + change_name_move: "改名或移动?", + enter_new_name_for: "输入新的名称", + extract_item: "解压", + extraction_started: "解压已经在后台开始", + compression_started: "压缩已经在后台开始", + enter_folder_name_for_extraction: "输入解压的目标文件夹", + enter_file_name_for_compression: "输入要压缩的文件名", + toggle_fullscreen: "切换全屏", + edit_file: "编辑文件", + file_content: "文件内容", + loading: "加载中", + search: "搜索", + create_folder: "创建文件夹", + create: "创建", + folder_name: "文件夹名称", + upload: "上传", + change_permissions: "修改权限", + change: "修改", + details: "详细信息", + icons: "图标", + list: "列表", + name: "名称", + size: "尺寸", + actions: "操作", + date: "日期", + selection: "选择", + no_files_in_folder: "此文件夹没有文件", + no_folders_in_folder: "此文件夹不包含子文件夹", + select_this: "选择此文件", + go_back: "后退", + wait: "等待", + move: "移动", + download: "下载", + view_item: "查看子项", + remove: "删除", + edit: "编辑", + copy: "复制", + rename: "重命名", + extract: "解压", + compress: "压缩", + error_invalid_filename: "非法文件名或文件已经存在, 请指定其它名称", + error_modifying: "修改文件出错", + error_deleting: "删除文件或文件夹出错", + error_renaming: "重命名文件出错", + error_copying: "复制文件出错", + error_compressing: "压缩文件或文件夹出错", + error_extracting: "解压文件出错", + error_creating_folder: "创建文件夹出错", + error_getting_content: "获取文件内容出错", + error_changing_perms: "修改文件权限出错", + error_uploading_files: "上传文件出错", + sure_to_start_compression_with: "确定要压缩?", + owner: "拥有者", + group: "群组", + others: "其他", + read: "读取", + write: "写入", + exec: "执行", + original: "原始", + changes: "变化", + recursive: "递归", + preview: "成员预览", + open: "打开", + these_elements: "共 {{total}} 个", + new_folder: "新文件夹", + download_as_zip: "下载的ZIP" + }), e.translations("zh_tw", { + filemanager: "檔案管理員", + language: "語言", + english: "英語", + spanish: "西班牙語", + portuguese: "葡萄牙語", + french: "法語", + german: "德語", + hebrew: "希伯來語", + italian: "意大利", + slovak: "斯洛伐克語", + chinese_tw: "正體中文", + chinese_cn: "簡體中文", + russian: "俄語", + ukrainian: "烏克蘭", + turkish: "土耳其", + persian: "波斯語", + polish: "波蘭語", + confirm: "確定", + cancel: "取消", + close: "關閉", + upload_files: "上傳檔案", + files_will_uploaded_to: "檔案將上傳到", + select_files: "選擇檔案", + uploading: "上傳中", + permissions: "權限", + select_destination_folder: "選擇目標檔案", + source: "來自", + destination: "目的地", + copy_file: "複製檔案", + sure_to_delete: "確定要刪除?", + change_name_move: "更名或移動?", + enter_new_name_for: "輸入新的名稱", + extract_item: "解壓", + extraction_started: "解壓已經在後台開始", + compression_started: "壓縮已經在後台開始", + enter_folder_name_for_extraction: "輸入解壓的目標資料匣", + enter_file_name_for_compression: "輸入要壓縮的檔名", + toggle_fullscreen: "切換全螢幕", + edit_file: "編輯檔案", + file_content: "檔案內容", + loading: "載入中", + search: "尋找", + create_folder: "建立資料匣", + create: "建立", + folder_name: "資料匣名稱", + upload: "上傳", + change_permissions: "修改權限", + change: "修改", + details: "詳細內容", + icons: "圖示", + list: "列表", + name: "名稱", + size: "大小", + actions: "操作", + date: "日期", + selection: "選擇", + no_files_in_folder: "此資料匣沒有文件", + no_folders_in_folder: "此資料匣不包含子資料匣", + select_this: "選擇此資料匣", + go_back: "後退", + wait: "等待", + move: "移動", + download: "下載", + view_item: "檢視", + remove: "刪除", + edit: "存檔", + copy: "複製", + rename: "更改名稱", + extract: "解壓", + compress: "壓縮", + error_invalid_filename: "非法檔名或檔案已經存在, 請指定其它檔名", + error_modifying: "修改檔案出錯", + error_deleting: "刪除檔案或資料夾出錯", + error_renaming: "更改名稱發生出錯", + error_copying: "複製檔案出錯", + error_compressing: "壓縮檔案或資料匣出錯", + error_extracting: "解壓檔案出錯", + error_creating_folder: "建立資料匣出錯", + error_getting_content: "獲取檔案內容出錯", + error_changing_perms: "修改檔案權限出錯", + error_uploading_files: "上傳檔案出錯", + sure_to_start_compression_with: "確定要壓縮?", + owner: "擁有者", + group: "群組", + others: "其他", + read: "讀取", + write: "寫入", + exec: "執行", + original: "現行", + changes: "變更為", + recursive: "包含所有子資料匣", + preview: "預覽", + open: "開啟", + these_elements: "共 {{total}} 個", + new_folder: "新資料匣", + download_as_zip: "以ZIP下載" + }), e.translations("ru", { + filemanager: "Файловый менеджер", + language: "Язык", + english: "Английский", + spanish: "Испанский", + portuguese: "Португальский", + french: "Французкий", + german: "Немецкий", + hebrew: "Хинди", + italian: "итальянский", + slovak: "Словацкий", + chinese_tw: "Традиционный Китайский", + chinese_cn: "Упрощенный Китайский", + russian: "русский", + ukrainian: "украинец", + turkish: "турецкий", + persian: "персидский", + polish: "Польский", + confirm: "Подьвердить", + cancel: "Отменить", + close: "Закрыть", + upload_files: "Загрузка файлов", + files_will_uploaded_to: "Файлы будут загружены в: ", + select_files: "Выберите файлы", + uploading: "Загрузка", + permissions: "Разрешения", + select_destination_folder: "Выберите папку назначения", + source: "Источкик", + destination: "Цель", + copy_file: "Скопировать файл", + sure_to_delete: "Действительно удалить?", + change_name_move: "Переименовать / переместить", + enter_new_name_for: "Новое имя для", + extract_item: "Извлечь", + extraction_started: "Извлечение начато", + compression_started: "Сжатие начато", + enter_folder_name_for_extraction: "Извлечь в укананную папку", + enter_file_name_for_compression: "Введите имя архива", + toggle_fullscreen: "На весь экран", + edit_file: "Редактировать", + file_content: "Содержимое файла", + loading: "Загрузка", + search: "Поиск", + create_folder: "Создать папку", + create: "Создать", + folder_name: "Имя папки", + upload: "Загрузить", + change_permissions: "Изменить разрешения", + change: "Изменить", + details: "Свойства", + icons: "Иконки", + list: "Список", + name: "Имя", + size: "Размер", + actions: "Действия", + date: "Дата", + selection: "выбор", + no_files_in_folder: "Пустая папка", + no_folders_in_folder: "Пустая папка", + select_this: "Выбрать", + go_back: "Назад", + wait: "Подождите", + move: "Переместить", + download: "Скачать", + view_item: "Отобразить содержимое", + remove: "Удалить", + edit: "Редактировать", + copy: "Скопировать", + rename: "Переименовать", + extract: "Извлечь", + compress: "Сжать", + error_invalid_filename: "Имя неверное или уже существует, выберите другое", + error_modifying: "Произошла ошибка при модифицировании файла", + error_deleting: "Произошла ошибка при удалении", + error_renaming: "Произошла ошибка при переименовании файла", + error_copying: "Произошла ошибка при копировании файла", + error_compressing: "Произошла ошибка при сжатии", + error_extracting: "Произошла ошибка при извлечении", + error_creating_folder: "Произошла ошибка при создании папки", + error_getting_content: "Произошла ошибка при получении содержимого", + error_changing_perms: "Произошла ошибка при изменении разрешений", + error_uploading_files: "Произошла ошибка при загрузке", + sure_to_start_compression_with: "Действительно сжать", + owner: "Владелец", + group: "Группа", + others: "Другие", + read: "Чтение", + write: "Запись", + exec: "Выполнение", + original: "По-умолчанию", + changes: "Изменения", + recursive: "Рекурсивно", + preview: "Просмотр", + open: "Открыть", + these_elements: "всего {{total}} елементов", + new_folder: "Новая папка", + download_as_zip: "Download as ZIP" + }), e.translations("ua", { + filemanager: "Файловий менеджер", + language: "Мова", + english: "Англійська", + spanish: "Іспанська", + portuguese: "Португальська", + french: "Французька", + german: "Німецька", + hebrew: "Хінді", + italian: "італійський", + slovak: "Словацька", + chinese_tw: "традиційний Китайський", + chinese_cn: "Cпрощена Китайська", + russian: "російський", + ukrainian: "український", + turkish: "турецька", + persian: "перський", + polish: "Польська", + confirm: "Підтвердити", + cancel: "Відмінити", + close: "Закрити", + upload_files: "Завантаження файлів", + files_will_uploaded_to: "Файли будуть завантажені у: ", + select_files: "Виберіть файли", + uploading: "Завантаження", + permissions: "Дозволи", + select_destination_folder: "Виберіть папку призначення", + source: "Джерело", + destination: "Ціль", + copy_file: "Скопіювати файл", + sure_to_delete: "Дійсно удалить?", + change_name_move: "Перейменувати / перемістити", + enter_new_name_for: "Нове ім'я для", + extract_item: "Извлечь", + extraction_started: "Извлечение начато", + compression_started: "Архівацію почато", + enter_folder_name_for_extraction: "Извлечь в укананную папку", + enter_file_name_for_compression: "Введите имя архива", + toggle_fullscreen: "На весь экран", + edit_file: "Редагувати", + file_content: "Вміст файлу", + loading: "Завантаження", + search: "Пошук", + create_folder: "Створити папку", + create: "Створити", + folder_name: "Ім'я папки", + upload: "Завантижити", + change_permissions: "Змінити дозволи", + change: "Редагувати", + details: "Властивості", + icons: "Іконки", + list: "Список", + name: "Ім'я", + size: "Розмір", + actions: "Дії", + date: "Дата", + selection: "вибір", + no_files_in_folder: "Пуста папка", + no_folders_in_folder: "Пуста папка", + select_this: "Выбрати", + go_back: "Назад", + wait: "Зачекайте", + move: "Перемістити", + download: "Скачати", + view_item: "Показати вміст", + remove: "Видалити", + edit: "Редагувати", + copy: "Копіювати", + rename: "Переіменувати", + extract: "Розархівувати", + compress: "Архівувати", + error_invalid_filename: "Ім'я певірне або вже існує, виберіть інше", + error_modifying: "Виникла помилка при редагуванні файлу", + error_deleting: "Виникла помилка при видаленні", + error_renaming: "Виникла помилка при зміні імені файлу", + error_copying: "Виникла помилка при коміюванні файлу", + error_compressing: "Виникла помилка при стисненні", + error_extracting: "Виникла помилка при розархівації", + error_creating_folder: "Виникла помилка при створенні папки", + error_getting_content: "Виникла помилка при отриманні вмісту", + error_changing_perms: "Виникла помилка при зміні дозволів", + error_uploading_files: "Виникла помилка при завантаженні", + sure_to_start_compression_with: "Дійсно стиснути", + owner: "Власник", + group: "Група", + others: "Інші", + read: "Читання", + write: "Запис", + exec: "Виконання", + original: "За замовчуванням", + changes: "Зміни", + recursive: "Рекурсивно", + preview: "Перегляд", + open: "Відкрити", + these_elements: "усього {{total}} елементів", + new_folder: "Нова папка", + download_as_zip: "Download as ZIP" + }), e.translations("tr", { + filemanager: "Dosya Yöneticisi", + language: "Dil", + english: "İngilizce", + spanish: "İspanyolca", + portuguese: "Portekizce", + french: "Fransızca", + german: "Almanca", + hebrew: "İbranice", + italian: "İtalyanca", + slovak: "Slovakça", + chinese_tw: "Geleneksel Çin", + chinese_cn: "Basitleştirilmiş Çince", + russian: "Rusça", + ukrainian: "Ukraynaca", + turkish: "Türkçe", + persian: "Farsça", + polish: "Lehçe", + confirm: "Onayla", + cancel: "İptal Et", + close: "Kapat", + upload_files: "Dosya yükle", + files_will_uploaded_to: "Dosyalar yüklenecektir.", + select_files: "Dosya Seç", + uploading: "Yükleniyor", + permissions: "İzinler", + select_destination_folder: "Hedef klasör seçin", + source: "Kaynak", + destination: "Hedef", + copy_file: "Dosyayı kopyala", + sure_to_delete: "Silmek istediğinden emin misin", + change_name_move: "İsmini değiştir / taşı", + enter_new_name_for: "Yeni ad girin", + extract_item: "Dosya çıkar", + extraction_started: "Çıkarma işlemi arkaplanda devam ediyor", + compression_started: "Sıkıştırma işlemi arkaplanda başladı", + enter_folder_name_for_extraction: "Çıkarılması için klasör adı girin", + enter_file_name_for_compression: "Sıkıştırılması için dosya adı girin", + toggle_fullscreen: "Tam ekran moduna geç", + edit_file: "Dosyayı düzenle", + file_content: "Dosya içeriği", + loading: "Yükleniyor", + search: "Ara", + create_folder: "Klasör oluştur", + create: "Oluştur", + folder_name: "Klasör adı", + upload: "Yükle", + change_permissions: "İzinleri değiştir", + change: "Değiştir", + details: "Detaylar", + icons: "simgeler", + list: "Liste", + name: "Adı", + size: "Boyutu", + actions: "İşlemler", + date: "Tarih", + selection: "Seçim", + no_files_in_folder: "Klasörde hiç dosya yok", + no_folders_in_folder: "Bu klasör alt klasör içermez", + select_this: "Bunu seç", + go_back: "Geri git", + wait: "Bekle", + move: "Taşı", + download: "İndir", + view_item: "Dosyayı görüntüle", + remove: "Sil", + edit: "Düzenle", + copy: "Kopyala", + rename: "Yeniden Adlandır", + extract: "Çıkart", + compress: "Sıkıştır", + error_invalid_filename: "Geçersiz dosya adı, bu dosya adına sahip dosya mevcut", + error_modifying: "Dosya düzenlenirken bir hata oluştu", + error_deleting: "Klasör veya dosya silinirken bir hata oluştu", + error_renaming: "Dosya yeniden adlandırılırken bir hata oluştu", + error_copying: "Dosya kopyalanırken bir hata oluştu", + error_compressing: "Dosya veya klasör sıkıştırılırken bir hata oluştu", + error_extracting: "Çıkartılırken bir hata oluştu", + error_creating_folder: "Klasör oluşturulurken bir hata oluştu", + error_getting_content: "Dosya detayları alınırken bir hata oluştu", + error_changing_perms: "Dosyanın izini değiştirilirken bir hata oluştu", + error_uploading_files: "Dosyalar yüklenirken bir hata oluştu", + sure_to_start_compression_with: "Sıkıştırmak istediğinden emin misin", + owner: "Sahip", + group: "Grup", + others: "Diğerleri", + read: "Okuma", + write: "Yazma", + exec: "Gerçekleştir", + original: "Orjinal", + changes: "Değişiklikler", + recursive: "Yinemeli", + preview: "Dosyayı önizle", + open: "Aç", + these_elements: "{{total}} eleman", + new_folder: "Yeni Klasör", + download_as_zip: "ZIP olarak indir" + }), e.translations("fa", { + filemanager: "مدیریت فایل ها", + language: "زبان", + english: "انگلیسی", + spanish: "اسپانیایی", + portuguese: "پرتغالی", + french: "فرانسه", + german: "آلمانی", + hebrew: "عبری", + italian: "ایتالیایی", + slovak: "اسلواک", + chinese_tw: "چینی سنتی", + chinese_cn: "چینی ساده شده", + russian: "روسی", + ukrainian: "اوکراینی", + turkish: "ترکی", + persian: "فارسی", + polish: "لهستانی", + confirm: "تایید", + cancel: "رد", + close: "بستن", + upload_files: "آپلود فایل", + files_will_uploaded_to: "فایل ها آپلود می شوند به", + select_files: "انتخاب فایل ها", + uploading: "در حال آپلود", + permissions: "مجوز ها", + select_destination_folder: "پوشه مقصد را انتخاب کنید", + source: "مبدا", + destination: "مقصد", + copy_file: "کپی فایل", + sure_to_delete: "مطمين هستید می خواهید حذف کنید؟", + change_name_move: "تغییر نام و جابجایی", + enter_new_name_for: "نام جدیدی وارد کنید برای", + extract_item: "خارج کردن از حالت فشرده", + extraction_started: "یک پروسه در پس زمینه شروع به خارج کردن از حالت فشرده کرد", + compression_started: "یک پروسه در پس زمینه شروع به فشرده سازی کرد", + enter_folder_name_for_extraction: "نام پوشه مقصد برای خارج کردن از حالت فشرده را وارد کنید", + enter_file_name_for_compression: "نام پوشه مقصد برای فشرده سازی را وارد کنید", + toggle_fullscreen: "تعویض حالت تمام صفحه", + edit_file: "ویرایش", + file_content: "محتویات", + loading: "در حال بارگذاری", + search: "جستجو", + create_folder: "پوشه جدید", + create: "ساختن", + folder_name: "نام پوشه", + upload: "آپلود", + change_permissions: "تغییر مجوز ها", + change: "تغییر", + details: "جزییات", + icons: "آیکون ها", + list: "لیست", + name: "نام", + size: "سایز", + actions: "اعمال", + date: "تاریخ", + selection: "انتخاب", + no_files_in_folder: "هیچ فایلی در این پوشه نیست", + no_folders_in_folder: "هیچ پوشه ای داخل این پوشه قرار ندارد", + select_this: "انتخاب", + go_back: "بازگشت", + wait: "منتظر بمانید", + move: "جابجایی", + download: "دانلود", + view_item: "مشاهده این مورد", + remove: "حذف", + edit: "ویرایش", + copy: "کپی", + rename: "تغییر نام", + extract: "خروج از حالت فشرده", + compress: "فشرده سازی", + error_invalid_filename: "نام فایل مورد درست نیست و یا قبلا استفاده شده است، لطفا نام دیگری وارد کنید", + error_modifying: "در هنگام تغییر فایل خطایی پیش آمد", + error_deleting: "در هنگام حذف فایل خطایی پیش آمد", + error_renaming: "در هنگام تغییر نام فایل خطایی پیش آمد", + error_copying: "در هنگام کپی کردن فایل خطایی پیش آمد", + error_compressing: "در هنگام فشرده سازی فایل خطایی پیش آمد", + error_extracting: "در هنگام خارک کردن فایل از حالت فشرده خطایی پیش آمد", + error_creating_folder: "در هنگام ساخت پوشه خطایی پیش امد", + error_getting_content: "در هنگام بارگذاری محتویات فایل خطایی رخ داد", + error_changing_perms: "در هنگام تغییر مجوز های فایل خطایی رخ داد", + error_uploading_files: "در آپلود فایل خطایی رخ داد", + sure_to_start_compression_with: "مطمئن هستید فشرده سازی انجام شد؟", + owner: "مالک فایل", + group: "گروه", + others: "دیگران", + read: "خواندن", + write: "نوشتن", + exec: "اجرا کردن", + original: "اصلی", + changes: "تغییرات", + recursive: "بازگشتی", + preview: "پیش نمایش", + open: "باز کردن", + these_elements: "تعداد {{total}} مورد", + new_folder: "پوشه جدید", + download_as_zip: "به عنوان فایل فشرده دانلود شود" + }), e.translations("pl", { + filemanager: "Menadżer plików", + language: "Język", + english: "Angielski", + spanish: "Hiszpański", + portuguese: "Portugalski", + french: "Francuski", + german: "Niemiecki", + hebrew: "Hebrajski", + italian: "Włoski", + slovak: "Słowacki", + chinese_tw: "Tradycyjny Chiński", + chinese_cn: "Chiński Uproszczony", + russian: "Rosyjski", + ukrainian: "Ukraiński", + turkish: "Turecki", + persian: "Perski", + polish: "Polski", + confirm: "Potwierdź", + cancel: "Anuluj", + close: "Zamknij", + upload_files: "Wgraj pliki", + files_will_uploaded_to: "Pliki będą umieszczone w katalogu", + select_files: "Wybierz pliki", + uploading: "Ładowanie", + permissions: "Uprawnienia", + select_destination_folder: "Wybierz folder docelowy", + source: "Źródło", + destination: "Cel", + copy_file: "Kopiuj plik", + sure_to_delete: "Jesteś pewien, że chcesz skasować", + change_name_move: "Zmień nazwę / przenieś", + enter_new_name_for: "Wpisz nową nazwę dla", + extract_item: "Rozpakuj element", + extraction_started: "Rozpakowywanie rozpoczęło się w tle", + compression_started: "Kompresowanie rozpoczęło się w tle", + enter_folder_name_for_extraction: "Wpisz nazwę folderu do rozpakowania", + enter_file_name_for_compression: "Wpisz nazwę folderu do skompresowania", + toggle_fullscreen: "Tryb pełnoekranowy", + edit_file: "Edytuj plik", + file_content: "Zawartość pliku", + loading: "Ładowanie", + search: "Szukaj", + create_folder: "Stwórz folder", + create: "Utwórz", + folder_name: "Nazwa folderu", + upload: "Wgraj", + change_permissions: "Zmień uprawnienia", + change: "Zmień", + details: "Szczegóły", + icons: "Ikony", + list: "Lista", + name: "Nazwa", + size: "Rozmiar", + actions: "Akcje", + date: "Data", + selection: "Zaznaczone", + no_files_in_folder: "Brak plików w tym folderze", + no_folders_in_folder: "Ten folder nie zawiera podfolderów", + select_this: "Wybierz ten", + go_back: "W górę", + wait: "Wait", + move: "Przenieś", + download: "Pobierz", + view_item: "Wyświetl", + remove: "Usuń", + edit: "Edycja", + copy: "Kopiuj", + rename: "Zmień nazwę", + extract: "Rozpakuj", + compress: "Skompresuj", + error_invalid_filename: "Błędna nazwa pliku lub plik o takiej nazwie już istnieje, proszę użyć innej nazwy", + error_modifying: "Wystąpił błąd podczas modyfikowania pliku", + error_deleting: "Wystąpił błąd podczas usuwania pliku lub folderu", + error_renaming: "Wystąpił błąd podczas zmiany nazwy pliku", + error_copying: "Wystąpił błąd podczas kopiowania pliku", + error_compressing: "Wystąpił błąd podczas kompresowania pliku lub folderu", + error_extracting: "Wystąpił błąd podczas rozpakowywania pliku", + error_creating_folder: "Wystąpił błąd podczas tworzenia nowego folderu", + error_getting_content: "Wystąpił błąd podczas pobierania zawartości pliku", + error_changing_perms: "Wystąpił błąd podczas zmiany uprawnień pliku", + error_uploading_files: "Wystąpił błąd podczas wgrywania plików", + sure_to_start_compression_with: "Jesteś pewien, że chcesz skompresować", + owner: "Właściciel", + group: "Grupa", + others: "Inni", + read: "Odczyt", + write: "Zapis", + exec: "Wykonywanie", + original: "Oryginał", + changes: "Zmiany", + recursive: "Rekursywnie", + preview: "Podgląd elementu", + open: "Otwórz", + these_elements: "te {{total}} elementy?", + new_folder: "Nowy folder", + download_as_zip: "Pobierz jako ZIP" + }), e.translations("it", { + filemanager: "Gestore File", + language: "Lingua", + english: "Inglese", + spanish: "Spagnolo", + portuguese: "Portoghese", + french: "Francese", + german: "Tedesco", + hebrew: "Ebraico", + slovak: "Slovacco", + chinese_tw: "Cinese Tradizionale", + chinese_cn: "Cinese", + russian: "Russo", + ukrainian: "Ucraino", + turkish: "Turco", + persian: "Persiano", + polish: "Polacco", + confirm: "Conferma", + cancel: "Annulla", + close: "Chiudi", + upload_files: "Carica files", + files_will_uploaded_to: "I files saranno caricati in", + select_files: "Seleziona i files", + uploading: "Trasferimento", + permissions: "Permessi", + select_destination_folder: "Select carterlla di destinazione", + source: "Sorgente", + destination: "Destinazione", + copy_file: "Copia file", + sure_to_delete: "Sicuro di voler eliminare", + change_name_move: "Rinomina / sposta", + enter_new_name_for: "Inserisci nuovo nome per", + extract_item: "Estrai elemento", + extraction_started: "Decompressione avviata da un processo in background", + compression_started: "Compressione avviata da un processo in background", + enter_folder_name_for_extraction: "Inserisci nome cartella per l'estrazione di", + enter_file_name_for_compression: "Inserisci nome file per la compressione di", + toggle_fullscreen: "Passa a schermo intero", + edit_file: "Modifica file", + file_content: "Contenuto del file", + loading: "Caricamento", + search: "Cerca", + create_folder: "Crea cartella", + create: "Crea", + folder_name: "Nome cartella", + upload: "Upload", + change_permissions: "Modifica permessi", + change: "Modifica", + details: "Dettagli", + icons: "Icone", + list: "Lista", + name: "Nome", + size: "Dimensione", + actions: "Azioni", + date: "Data", + selection: "Selezione", + no_files_in_folder: "Nessun file nella cartella", + no_folders_in_folder: "Questa cartella non contiene altre cartelle", + select_this: "Seleziona questo", + go_back: "Indietro", + wait: "Attendere", + move: "Sposta", + download: "Scarica", + view_item: "Visualizza elemento", + remove: "Elimina", + edit: "Modifica", + copy: "Copia", + rename: "Rinomina", + extract: "Estrai", + compress: "Comprimi", + error_invalid_filename: "Nome file non valido o già esistente, specificarne un'altro", + error_modifying: "Errore durante la modifica del file", + error_deleting: "Errore durante l'eliminazione del file o della cartella", + error_renaming: "Errore durante la rinomina del file", + error_copying: "Errore durante la copia del file", + error_compressing: "Errore durante la compressione del file o della cartella", + error_extracting: "Errore durante l'estrazione del file", + error_creating_folder: "Errore nella creazione della cartella", + error_getting_content: "Errore nel recupero del contenuto del file", + error_changing_perms: "Errore durante la modifica dei permessi del file", + error_uploading_files: "Errore durante il trasferimento dei files", + sure_to_start_compression_with: "Sicuro di voler comprimere", + owner: "Proprietario", + group: "Gruppo", + others: "Altri", + read: "Lettura", + write: "Scrittura", + exec: "Esecuzione", + original: "Originario", + changes: "Cambiamenti", + recursive: "Ricorsivo", + preview: "Anteprima", + open: "Apri", + these_elements: "questi {{total}} elementi", + new_folder: "Nuova cartella", + download_as_zip: "Scarica come file ZIP" + }) + }]) +}(), function (e) { + "use strict"; + angular.module("FileManagerApp").service("apiHandler", ["$http", "$q", "$window", "$translate", "$httpParamSerializer", "Upload", function (e, r, n, i, a, t) { + e.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest"; + var o = function () { + this.inprocess = !1, this.asyncSuccess = !1, this.error = "" + }; + return o.prototype.deferredHandler = function (e, r, n, i) { + return e && "object" == typeof e || (this.error = "Error %s - Bridge response error, please check the API docs or this ajax response.".replace("%s", n)), 404 == n && (this.error = "Error 404 - Backend bridge is not working, please check the ajax response."), e.result && e.result.error && (this.error = e.result.error), !this.error && e.error && (this.error = e.error.message), !this.error && i && (this.error = i), this.error ? r.reject(e) : r.resolve(e) + }, o.prototype.list = function (n, i, a, t) { + var o = this, s = a || o.deferredHandler, l = r.defer(), + d = {action: "list", path: i, fileExtensions: t && t.length ? t : void 0}; + return o.inprocess = !0, o.error = "", e.post(n, d).then(function (e) { + s(e.data, l, e.status) + }, function (e) { + s(e.data, l, e.status, "Unknown error listing, check the response") + }).finally(function () { + o.inprocess = !1 + }), l.promise + }, o.prototype.copy = function (n, a, t, o) { + var s = this, l = r.defer(), d = {action: "copy", items: a, newPath: t}; + return o && 1 === a.length && (d.singleFilename = o), s.inprocess = !0, s.error = "", e.post(n, d).then(function (e) { + s.deferredHandler(e.data, l, e.status) + }, function (e) { + s.deferredHandler(e.data, l, e.status, i.instant("error_copying")) + }).finally(function () { + s.inprocess = !1 + }), l.promise + }, o.prototype.move = function (n, a, t) { + var o = this, s = r.defer(), l = {action: "move", items: a, newPath: t}; + return o.inprocess = !0, o.error = "", e.post(n, l).then(function (e) { + o.deferredHandler(e.data, s, e.status) + }, function (e) { + o.deferredHandler(e.data, s, e.status, i.instant("error_moving")) + }).finally(function () { + o.inprocess = !1 + }), s.promise + }, o.prototype.remove = function (n, a) { + var t = this, o = r.defer(), s = {action: "remove", items: a}; + return t.inprocess = !0, t.error = "", e.post(n, s).then(function (e) { + t.deferredHandler(e.data, o, e.status) + }, function (e) { + t.deferredHandler(e.data, o, e.status, i.instant("error_deleting")) + }).finally(function () { + t.inprocess = !1 + }), o.promise + }, o.prototype.upload = function (e, n, i) { + var a = this, o = r.defer(); + a.inprocess = !0, a.progress = 0, a.error = ""; + for (var s = {destination: n}, l = 0; l < i.length; l++) s["file-" + l] = i[l]; + return i && i.length && t.upload({url: e, data: s}).then(function (e) { + a.deferredHandler(e.data, o, e.status) + }, function (e) { + a.deferredHandler(e.data, o, e.status, "Unknown error uploading files") + }, function (e) { + a.progress = Math.min(100, parseInt(100 * e.loaded / e.total)) - 1 + }).finally(function () { + a.inprocess = !1, a.progress = 0 + }), o.promise + }, o.prototype.getContent = function (n, a) { + var t = this, o = r.defer(), s = {action: "getContent", item: a}; + return t.inprocess = !0, t.error = "", e.post(n, s).then(function (e) { + t.deferredHandler(e.data, o, e.status) + }, function (e) { + t.deferredHandler(e.data, o, e.status, i.instant("error_getting_content")) + }).finally(function () { + t.inprocess = !1 + }), o.promise + }, o.prototype.edit = function (n, a, t) { + var o = this, s = r.defer(), l = {action: "edit", item: a, content: t}; + return o.inprocess = !0, o.error = "", e.post(n, l).then(function (e) { + o.deferredHandler(e.data, s, e.status) + }, function (e) { + o.deferredHandler(e.data, s, e.status, i.instant("error_modifying")) + }).finally(function () { + o.inprocess = !1 + }), s.promise + }, o.prototype.rename = function (n, a, t) { + var o = this, s = r.defer(), l = {action: "rename", item: a, newItemPath: t}; + return o.inprocess = !0, o.error = "", e.post(n, l).then(function (e) { + o.deferredHandler(e.data, s, e.status) + }, function (e) { + o.deferredHandler(e.data, s, e.status, i.instant("error_renaming")) + }).finally(function () { + o.inprocess = !1 + }), s.promise + }, o.prototype.getUrl = function (e, r) { + return r && [e, a({action: "download", path: r})].join("?") + }, o.prototype.download = function (a, t, o, s, l) { + var d = this, c = this.getUrl(a, t); + if (!s || l || !n.saveAs) return !n.saveAs && n.console.log("Your browser dont support ajax download, downloading by default"), !!n.open(c, "_blank", ""); + var p = r.defer(); + return d.inprocess = !0, e.get(c).then(function (e) { + var r = new n.Blob([e.data]); + p.resolve(e.data), n.saveAs(r, o) + }, function (e) { + d.deferredHandler(e.data, p, e.status, i.instant("error_downloading")) + }).finally(function () { + d.inprocess = !1 + }), p.promise + }, o.prototype.downloadMultiple = function (t, o, s, l, d) { + var c = this, p = r.defer(), m = [t, a({action: "downloadMultiple", items: o, toFilename: s})].join("?"); + return l && !d && n.saveAs ? (c.inprocess = !0, e.get(t).then(function (e) { + var r = new n.Blob([e.data]); + p.resolve(e.data), n.saveAs(r, s) + }, function (e) { + c.deferredHandler(e.data, p, e.status, i.instant("error_downloading")) + }).finally(function () { + c.inprocess = !1 + }), p.promise) : (!n.saveAs && n.console.log("Your browser dont support ajax download, downloading by default"), !!n.open(m, "_blank", "")) + }, o.prototype.compress = function (n, a, t, o) { + var s = this, l = r.defer(), d = {action: "compress", items: a, destination: o, compressedFilename: t}; + return s.inprocess = !0, s.error = "", e.post(n, d).then(function (e) { + s.deferredHandler(e.data, l, e.status) + }, function (e) { + s.deferredHandler(e.data, l, e.status, i.instant("error_compressing")) + }).finally(function () { + s.inprocess = !1 + }), l.promise + }, o.prototype.extract = function (n, a, t, o) { + var s = this, l = r.defer(), d = {action: "extract", item: a, destination: o, folderName: t}; + return s.inprocess = !0, s.error = "", e.post(n, d).then(function (e) { + s.deferredHandler(e.data, l, e.status) + }, function (e) { + s.deferredHandler(e.data, l, e.status, i.instant("error_extracting")) + }).finally(function () { + s.inprocess = !1 + }), l.promise + }, o.prototype.changePermissions = function (n, a, t, o, s) { + var l = this, d = r.defer(), + c = {action: "changePermissions", items: a, perms: t, permsCode: o, recursive: !!s}; + return l.inprocess = !0, l.error = "", e.post(n, c).then(function (e) { + l.deferredHandler(e.data, d, e.status) + }, function (e) { + l.deferredHandler(e.data, d, e.status, i.instant("error_changing_perms")) + }).finally(function () { + l.inprocess = !1 + }), d.promise + }, o.prototype.createFolder = function (n, a) { + var t = this, o = r.defer(), s = {action: "createFolder", newPath: a}; + return t.inprocess = !0, t.error = "", e.post(n, s).then(function (e) { + t.deferredHandler(e.data, o, e.status) + }, function (e) { + t.deferredHandler(e.data, o, e.status, i.instant("error_creating_folder")) + }).finally(function () { + t.inprocess = !1 + }), o.promise + }, o + }]) +}(), function (e) { + "use strict"; + angular.module("FileManagerApp").service("apiMiddleware", ["$window", "fileManagerConfig", "apiHandler", function (e, r, n) { + var i = function () { + this.apiHandler = new n + }; + return i.prototype.getPath = function (e) { + return "/" + e.join("/") + }, i.prototype.getFileList = function (e) { + return (e || []).map(function (e) { + return e && e.model.fullPath() + }) + }, i.prototype.getFilePath = function (e) { + return e && e.model.fullPath() + }, i.prototype.list = function (e, n) { + return this.apiHandler.list(r.listUrl, this.getPath(e), n) + }, i.prototype.copy = function (e, n) { + var i = this.getFileList(e), a = 1 === i.length ? e[0].tempModel.name : void 0; + return this.apiHandler.copy(r.copyUrl, i, this.getPath(n), a) + }, i.prototype.move = function (e, n) { + var i = this.getFileList(e); + return this.apiHandler.move(r.moveUrl, i, this.getPath(n)) + }, i.prototype.remove = function (e) { + var n = this.getFileList(e); + return this.apiHandler.remove(r.removeUrl, n) + }, i.prototype.upload = function (n, i) { + if (!e.FormData) throw new Error("Unsupported browser version"); + var a = this.getPath(i); + return this.apiHandler.upload(r.uploadUrl, a, n) + }, i.prototype.getContent = function (e) { + var n = this.getFilePath(e); + return this.apiHandler.getContent(r.getContentUrl, n) + }, i.prototype.edit = function (e) { + var n = this.getFilePath(e); + return this.apiHandler.edit(r.editUrl, n, e.tempModel.content) + }, i.prototype.rename = function (e) { + var n = this.getFilePath(e), i = e.tempModel.fullPath(); + return this.apiHandler.rename(r.renameUrl, n, i) + }, i.prototype.getUrl = function (e) { + var n = this.getFilePath(e); + return this.apiHandler.getUrl(r.downloadFileUrl, n) + }, i.prototype.download = function (e, n) { + var i = this.getFilePath(e), a = e.model.name; + if (!e.isFolder()) return this.apiHandler.download(r.downloadFileUrl, i, a, r.downloadFilesByAjax, n) + }, i.prototype.downloadMultiple = function (e, n) { + var i = this.getFileList(e), + a = (new Date).getTime().toString().substr(8, 13) + "-" + r.multipleDownloadFileName; + return this.apiHandler.downloadMultiple(r.downloadMultipleUrl, i, a, r.downloadFilesByAjax, n) + }, i.prototype.compress = function (e, n, i) { + var a = this.getFileList(e); + return this.apiHandler.compress(r.compressUrl, a, n, this.getPath(i)) + }, i.prototype.extract = function (e, n, i) { + var a = this.getFilePath(e); + return this.apiHandler.extract(r.extractUrl, a, n, this.getPath(i)) + }, i.prototype.changePermissions = function (e, n) { + var i = this.getFileList(e), a = n.tempModel.perms.toCode(), t = n.tempModel.perms.toOctal(), + o = !!n.tempModel.recursive; + return this.apiHandler.changePermissions(r.permissionsUrl, i, a, t, o) + }, i.prototype.createFolder = function (e) { + var n = e.tempModel.fullPath(); + return this.apiHandler.createFolder(r.createFolderUrl, n) + }, i + }]) +}(), function (e) { + "use strict"; + angular.module("FileManagerApp").service("fileNavigator", ["apiMiddleware", "fileManagerConfig", "item", function (e, r, n) { + var i = function () { + this.apiMiddleware = new e, this.requesting = !1, this.fileList = [], this.currentPath = this.getBasePath(), this.history = [], this.error = "", this.onRefresh = function () { + } + }; + return i.prototype.getBasePath = function () { + var e = (r.basePath || "").replace(/^\//, ""); + return e.trim() ? e.split("/") : [] + }, i.prototype.deferredHandler = function (e, r, n, i) { + return e && "object" == typeof e || (this.error = "Error %s - Bridge response error, please check the API docs or this ajax response.".replace("%s", n)), 404 == n && (this.error = "Error 404 - Backend bridge is not working, please check the ajax response."), 200 == n && (this.error = null), !this.error && e.result && e.result.error && (this.error = e.result.error), !this.error && e.error && (this.error = e.error.message), !this.error && i && (this.error = i), this.error ? r.reject(e) : r.resolve(e) + }, i.prototype.list = function () { + return this.apiMiddleware.list(this.currentPath, this.deferredHandler.bind(this)) + }, i.prototype.refresh = function () { + var e = this; + e.currentPath.length || (e.currentPath = this.getBasePath()); + var r = e.currentPath.join("/"); + return e.requesting = !0, e.fileList = [], e.list().then(function (i) { + e.fileList = (i.result || []).map(function (r) { + return new n(r, e.currentPath) + }), e.buildTree(r), e.onRefresh() + }).finally(function () { + e.requesting = !1 + }) + }, i.prototype.buildTree = function (e) { + function r(e, n, i) { + var a = i ? i + "/" + n.model.name : n.model.name; + if (e.name && e.name.trim() && 0 !== i.trim().indexOf(e.name) && (e.nodes = []), e.name !== i) e.nodes.forEach(function (e) { + r(e, n, i) + }); else { + for (var t in e.nodes) if (e.nodes[t].name === a) return; + e.nodes.push({item: n, name: a, nodes: []}) + } + e.nodes = e.nodes.sort(function (e, r) { + return e.name.toLowerCase() < r.name.toLowerCase() ? -1 : e.name.toLowerCase() === r.name.toLowerCase() ? 0 : 1 + }) + } + + function i(e, r) { + r.push(e); + for (var n in e.nodes) i(e.nodes[n], r) + } + + var a = [], t = {}; + !this.history.length && this.history.push({ + name: this.getBasePath()[0] || "", + nodes: [] + }), i(this.history[0], a), (t = function (e, r) { + return e.filter(function (e) { + return e.name === r + })[0] + }(a, e)) && (t.nodes = []); + for (var o in this.fileList) { + var s = this.fileList[o]; + s instanceof n && s.isFolder() && r(this.history[0], s, e) + } + }, i.prototype.folderClick = function (e) { + this.currentPath = [], e && e.isFolder() && (this.currentPath = e.model.fullPath().split("/").splice(1)), this.refresh() + }, i.prototype.upDir = function () { + this.currentPath[0] && (this.currentPath = this.currentPath.slice(0, -1), this.refresh()) + }, i.prototype.goTo = function (e) { + this.currentPath = this.currentPath.slice(0, e + 1), this.refresh() + }, i.prototype.fileNameExists = function (e) { + return this.fileList.find(function (r) { + return e && r.model.name.trim() === e.trim() + }) + }, i.prototype.listHasFolders = function () { + return this.fileList.find(function (e) { + return "dir" === e.model.type + }) + }, i.prototype.getCurrentFolderName = function () { + return this.currentPath.slice(-1)[0] || "/" + }, i + }]) +}(), angular.module("FileManagerApp").run(["$templateCache", function (e) { + e.put("src/templates/current-folder-breadcrumb.html", ''), e.put("src/templates/item-context-menu.html", ''), e.put("src/templates/main-icons.html", '
    \r\n \r\n\r\n
    \r\n
    \r\n
    \r\n\r\n
    \r\n {{"no_files_in_folder" | translate}}...\r\n
    \r\n \r\n
    \r\n {{ fileNavigator.error }}\r\n
    \r\n
    '), e.put("src/templates/main-table-modal.html", '\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
    \r\n \r\n {{"name" | translate}}\r\n \r\n \r\n
    \r\n
    \r\n
    \r\n {{"no_folders_in_folder" | translate}}...\r\n \r\n \r\n
    \r\n {{ fileNavigator.error }}\r\n
    \r\n \r\n \r\n {{item.model.name | strLimit : 32}}\r\n \r\n \r\n \r\n
    '), e.put("src/templates/main-table.html", '\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
    \r\n \r\n {{"name" | translate}}\r\n \r\n \r\n
    \r\n
    \r\n
    \r\n {{"no_files_in_folder" | translate}}...\r\n
    \r\n {{ fileNavigator.error }}\r\n
    \r\n \r\n \r\n \r\n {{item.model.name | strLimit : 64}}\r\n \r\n
    \r\n'), e.put("src/templates/main.html", '
    \r\n
    \r\n\r\n
    \r\n
    \r\n\r\n \r\n\r\n
    \r\n \r\n
    \r\n
    \r\n
    \r\n\r\n
    \r\n
    \r\n
    \r\n'), e.put("src/templates/modals.html", '\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n + + + + + + + + + 管理员 + + + +
    +
    + +
    + +

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + +
    编号用户名邮箱文件路径分类名链接上传时间
    {{key+1}}{{upload.username}}{{upload.email}}查看{{upload.categoryName}}访问{{new Date(upload.createTime).format('yyyy-MM-dd hh:mm:ss')}}
    +
    +
    + +
    +
    + +
    + +

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    编号用户名邮箱文件名分类链接下载时间
    {{key+1}}{{download.username}}{{download.email}}{{download.fileName}}{{download.categoryName}}访问{{new Date(download.createTime).format('yyyy-MM-dd hh:mm:ss')}}
    +
    +
    + +
    +
    + +
    + +

    +
    +
    +
    + + + + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + +
    + +
    +
    +
    编号文件编号用户名本地路径分类链接下载次数上传时间
    +
    + +
    + +
    +
    +
    {{key+1}}{{file.id}}{{file.username}}查看{{file.categoryName}}访问{{file.downloadTimes}}{{new Date(file.createTime).format('yyyy-MM-dd hh:mm:ss')}}
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    +

    +
    +
    +
    + + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + +
    + +
    +
    +
    编号用户名文件名本地路径下载权限删除权限修改权限可见权限创建时间
    +
    + +
    + +
    +
    +
    {{key+1}}{{auth.username}}{{auth.fileName}}查看{{new Date(auth.createTime).format('yyyy-MM-dd hh:mm:ss')}}
    +
    +
    + +
    +
    + +
    +
    +
    +
    +

    添加新分类


    + + + +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + +
    编号名称创建时间动作
    {{key}}{{category.id}}{{category.name}}{{new Date(category.createTime).format('yyyy-MM-dd hh:mm:ss')}} + 编辑 +  删除 +
    +
    +
    +
    + +
    +
    +
    +
    +
    + +
    +
    +
    +
    +

    +
    +
    +
    + + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + +
    + +
    +
    +
    编号头像用户名真实名邮箱权限最近登录时间注册时间
    +
    + +
    + +
    +
    +
    {{key+1}}{{user.username}}{{user.realName}}{{user.email}}{{user.permission<1?"禁止登录":(user.permission<2?"普通用户":"管理员")}}{{new Date(user.lastLoginTime).format('yyyy-MM-dd hh:mm:ss')}}{{new Date(user.createTime).format('yyyy-MM-dd hh:mm:ss')}}
    +
    +
    + +
    +
    + + +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/static/error.html b/src/main/resources/static/error.html new file mode 100644 index 0000000..bdd02dd --- /dev/null +++ b/src/main/resources/static/error.html @@ -0,0 +1,12 @@ + + + + + + + 错误 + + + + + \ No newline at end of file diff --git a/src/main/resources/static/filemanager.html b/src/main/resources/static/filemanager.html new file mode 100644 index 0000000..c2f5b51 --- /dev/null +++ b/src/main/resources/static/filemanager.html @@ -0,0 +1,40 @@ + + + + + + 远程文件管理 + + + + + + + + + + + + + + + + diff --git a/src/main/resources/static/home.html b/src/main/resources/static/home.html new file mode 100644 index 0000000..7dac764 --- /dev/null +++ b/src/main/resources/static/home.html @@ -0,0 +1,118 @@ + + + + + + + + + Websketch - {{ directory }} + + + + + + + + +
    + + + + + +
    +
    + +
    + + + +
    + +

    + +

    +
    +
    + + {% if is_subdirectory %} +
    + + + Back + +
    + {% endif %} + + +
    + + + + + + + + + + + + {% for file in files %} + + + + + + + + {% endfor %} + +
    NameSizeLast Modified
    + {% if file.is_dir %} + + {% else %} + + {% endif %} + + {{ file.name }}{% if file.is_dir %}/{% endif %} + + {{ file.size }} + + {{ file.last_modified }} + + {% if not file.is_dir %} + View in browser + {% endif %} +
    +
    + +
    +

    + Websketch V22.03 +

    +
    + +
    + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html new file mode 100644 index 0000000..1117230 --- /dev/null +++ b/src/main/resources/static/index.html @@ -0,0 +1,360 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WebSketch + + + +


    +
    +
    +
    + +
    +
    + + +
    +
    + +
    + +
    + +
    +
    + + +
    + + + + + + + + + + + +
    NameSizeLast Modified
    +
    +
    +
    + + + +
    +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    +
    + +

    +
    + +
    +
    + +

    +
    +
    +
    + +

    +
    + +
    + +
    +
    +
    +
    +
    +
    + 权限: +
    +
    +
    + 管理员 +
    +
    + 普通用户 +
    +
    +
    +
    +
    +
    + 登陆时间: +
    +
    + {{loginTime}} +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + +
    +
    +

    {{username}}

    +
    +
    +
    +
    +
    + 真实姓名: +
    +
    + +
    +
    +
    +
    +
    + 邮箱: +
    +
    + + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + 旧密码: +
    +
    + +
    +
    +
    +
    +
    + 新密码: +
    +
    + + +
    +
    +
    +
    + 确认新密码: +
    +
    + + +
    +
    +
    +
    +
    + +
    + 忘记密码 +
    +
    +
    +
    +
    +

    +
    +
    +
    + +
    + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/static/signin.html b/src/main/resources/static/signin.html new file mode 100644 index 0000000..46c4538 --- /dev/null +++ b/src/main/resources/static/signin.html @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + 登录 + + +
    +
    +
    +
    +
    +

    欢迎回来


    + +
    + +
    +
    +  记住我 +
    +
    + +
    +
    +
    + +
    +
    + +
    +
    +
    + + +
    +
    + +
    + + + + + \ No newline at end of file diff --git a/src/main/resources/static/upload.html b/src/main/resources/static/upload.html new file mode 100644 index 0000000..142d327 --- /dev/null +++ b/src/main/resources/static/upload.html @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + 上传 + + +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    +

    +
    +
    +
    +
    +
    +
    + +
    +
    + + + + + \ No newline at end of file diff --git a/src/main/resources/style.scss b/src/main/resources/style.scss new file mode 100644 index 0000000..99890d0 --- /dev/null +++ b/src/main/resources/style.scss @@ -0,0 +1,358 @@ +@import "assets/sketch/css/font"; +// -------- Variables ---------- +$fontLight: 300; +$fontBold: 600; +$desktop: 1024px; +$tablet: 768px; +$mobile: 576px; +$primary-color: #03A2DC; +$secondary-color: #fff6bb; +$light-color: #ffffff; +$dark-color: #555555; +$alt-bg-color: #f9f9f9; +$slider-bg-color: #ccc; +$tableHoverColor: #f9f9f9; + + +// --------Mixins ------------ +@mixin mQ($size) { + @media (max-width: $size) { + @content; + } +} +// -------- Normalize ---------- +* { + margin: 0; + padding: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +ul { + margin: 0; +} + +button, +input[type='button'] { + cursor: pointer; +} + +button:focus, +input:focus, +textarea:focus { + outline: none; +} + +input, textarea { + border: none; +} + +button { + border: none; + background: none; +} + +img { + max-width: 100%; + height: auto; +} + +p { + margin: 0; +} + +.align_item_center { + align-items: center; +} + +a, +a:hover, +a:active, +a:visited { + text-decoration: none; +} +// ------- Typo ------ + +body { + font-family: "Poppins", arial, sans-serif; + font-weight: 300; + line-height: 1.625; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + color: $dark-color; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + color: $dark-color; + font-weight: 600; + margin: 0; + line-height: 1.2; +} + +h1 { + font-size: 36px; +} + +h2 { + font-size: 30px; +} + +h3 { + font-size: 24px; +} + +h4 { + font-size: 18px; +} + +h5 { + font-size: 15px; +} + +h6 { + font-size: 13px; +} + +// ------- Styling ------ +.body_bg { + width: 100%; + height: 100%; + position: absolute; + z-index: -1; + background-color: $alt-bg-color; + clip-path: polygon(100% 100%, 0% 100%, 100% 0); +} + +.conn { + padding: 20px 0; +} + +// ----- header ---- +#header { + margin: 0px 0; + margin-left: 20px; + padding-bottom: 20px; + border-bottom: 5px solid $alt-bg-color; + + @include mQ($tablet) { + justify-content: center; + } + + @include mQ($mobile) { + margin-left: 10px; + flex-direction: column; + } + + // -- logo P -- + .logo_p { + //background-color: $primary-color; + display: flex; + justify-content: center; + align-items: center; + + img{ + width: 65px; + } + } + // --- Heading Title -- + .heading_title_p { + margin-left: 20px; + + @include mQ($mobile) { + margin-left: 0; + margin-top: 20px; + } + + h2 { + font-size: 22px; + + @include mQ($mobile) { + text-align: center; + } + } + } +} + +// --- Upload +.inputUploadP { + margin: 5px 0px 0px 20px; + + @include mQ($mobile) { + margin: 20px 10px; + } + + .uploadForm { + display: flex; + align-items: center; + + .uploadFile { + width: 0.1px; + height: 0.1px; + opacity: 0; + overflow: hidden; + position: absolute; + z-index: -1; + } + + svg{ + fill: $light-color; + } + + .uploadFile + label { + font-size: 1em; + font-weight: 700; + color: $light-color; + background-color: $primary-color; + display: inline-block; + transition: .2s ease; + padding: 8px 20px; + border-radius: 3px; + cursor: pointer; + } + + .uploadFile:focus + label, + .uploadFile + label:hover { + background-color: $dark-color; + } + + .uploadFile:focus + label { + outline: 1px dotted $dark-color; + outline: -webkit-focus-ring-color auto 5px; + } + + .uploadFile + label * { + pointer-events: none; + } + + .js .uploadFile { + width: 0.1px; + height: 0.1px; + opacity: 0; + overflow: hidden; + position: absolute; + z-index: -1; + } + + .no-js .uploadFile + label { + display: none; + } + + .uploadBtn_P { + margin-top: -7px; + margin-left: 10px; + + .uploadBtn { + transition: .2s ease; + + &:hover { + background-color: $secondary-color; + color: var(--dark-color); + border: 1px solid $dark-color; + } + + i { + padding-right: 10px; + } + } + } + + } +} +// ---- BackBtn P --- +.backBtn_p { + margin: 10px 0px 0px 20px; + + @include mQ($mobile) { + margin: 20px 10px; + } + + a { + font-size: 1.3em; + display: flex; + align-items: center; + transition: .2s ease; + width: 150px; + color: $primary-color; + + &:hover { + color: $dark-color; + + i { + color: $dark-color; + } + } + + i { + padding-right: 10px; + font-size: 1.5em; + + } + } +} + +// -- Table - P +.table_p { + //margin: 20px; + //margin-top: 0; + //width: calc(100% - 40px); + + .table-hover tbody tr:hover td, .table-hover tbody tr:hover th { + background-color: $tableHoverColor; + } + + // th { + // @include mQ($mobile) { + // font-size: .9em; + // } + // } + + td { + font-size: .95em; + vertical-align: middle; + + &:nth-child(1) { + text-align: center; + } + } + + .file_ic, .folder_ic { + color: $primary-color; + font-size: 1.5em; + transition: .2s ease; + + &:hover { + color: #0056b3; + } + } + + @include mQ($mobile) { + margin: 20px 10px; + width: calc(100% - 20px); + } +} + + +#tableData > tbody > tr > td { + padding: 0; + height: 20px; +} + +.dataTables_filter input { + border: 1px solid $dark-color; + padding: 3px 20px; +} + +// -- Footer -- +footer { + text-align: center; + font-size: .8em; + font-weight: bold; + margin: 30px 0; + margin-bottom: 0; +} \ No newline at end of file diff --git a/src/test/java/com/mesasoft/cn/SketchApplicationTest.java b/src/test/java/com/mesasoft/cn/SketchApplicationTest.java new file mode 100644 index 0000000..25e262b --- /dev/null +++ b/src/test/java/com/mesasoft/cn/SketchApplicationTest.java @@ -0,0 +1,27 @@ +package com.mesasoft.cn; + +import com.zhazhapan.config.JsonParser; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.IOException; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SketchApplicationTest { + + public static void setSettings() { + try { + SketchApplication.settings = new JsonParser(SketchApplicationTest.class.getResource("/config.json")); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Test + public void contextLoads() { + } + +} diff --git a/src/test/java/com/mesasoft/cn/common/CommonTest.java b/src/test/java/com/mesasoft/cn/common/CommonTest.java new file mode 100644 index 0000000..ced07d6 --- /dev/null +++ b/src/test/java/com/mesasoft/cn/common/CommonTest.java @@ -0,0 +1,46 @@ +package com.mesasoft.cn.common; + +import com.mesasoft.cn.SketchApplication; +import com.mesasoft.cn.SketchApplicationTest; +import com.mesasoft.cn.modules.constant.ConfigConsts; +import com.zhazhapan.util.FileExecutor; +import com.zhazhapan.util.Formatter; +import com.zhazhapan.util.MailSender; +import org.junit.Test; + +import javax.swing.filechooser.FileSystemView; +import java.io.File; + +/** + * @author pantao + * @since 2018/1/23 + */ +public class CommonTest { + + @Test + public void testSendEmail() throws Exception { + SketchApplicationTest.setSettings(); + MailSender.config(SketchApplication.settings.getObjectUseEval(ConfigConsts.EMAIL_CONFIG_OF_SETTINGS)); + MailSender.sendMail("tao@zhazhapan.com", "test", "test"); + } + + @Test + public void testGetDriver() { + FileSystemView fsv = FileSystemView.getFileSystemView(); + File[] fs = File.listRoots(); + for (File f : fs) { + System.out.println(fsv.getSystemDisplayName(f)); + System.out.print("总大小" + Formatter.formatSize(f.getTotalSpace())); + System.out.println("剩余" + Formatter.formatSize(f.getFreeSpace())); + System.out.println(f.isDirectory()); + } + } + + @Test + public void testListRoot() { + File[] files = FileExecutor.listFile("/c:/"); + for (File file : files) { + System.out.println(file.getName()); + } + } +} diff --git a/src/test/java/com/mesasoft/cn/common/SketchTest.java b/src/test/java/com/mesasoft/cn/common/SketchTest.java new file mode 100644 index 0000000..ad2aea4 --- /dev/null +++ b/src/test/java/com/mesasoft/cn/common/SketchTest.java @@ -0,0 +1,20 @@ +package com.mesasoft.cn.common; + +import org.junit.Test; + +/** + * @description: + * @author: zhq + * @create: 2022-03-18 + **/ +public class SketchTest { + + + @Test + public void testSendEmail() throws Exception { + String searchPath = "D://test//test"; + searchPath= searchPath.replace("\\\\",""); + searchPath =searchPath.replace("//",""); + System.err.println(searchPath); + } +} diff --git a/src/test/java/com/mesasoft/cn/config/SettingConfigTest.java b/src/test/java/com/mesasoft/cn/config/SettingConfigTest.java new file mode 100644 index 0000000..9808586 --- /dev/null +++ b/src/test/java/com/mesasoft/cn/config/SettingConfigTest.java @@ -0,0 +1,27 @@ +package com.mesasoft.cn.config; + +import com.mesasoft.cn.SketchApplication; +import com.mesasoft.cn.SketchApplicationTest; +import com.mesasoft.cn.modules.constant.ConfigConsts; +import org.junit.Test; + +import java.util.regex.Pattern; + +/** + * @author pantao + * @since 2018/1/26 + */ +public class SettingConfigTest { + + @Test + public void testFileSuffixPattern() { + SketchApplicationTest.setSettings(); + assert Pattern.compile(SketchApplication.settings.getStringUseEval(ConfigConsts.FILE_SUFFIX_MATCH_OF_SETTING)).matcher("jpg").matches(); + } + + @Test + public void testGetStoragePath() { + SketchApplicationTest.setSettings(); + System.out.println(SettingConfig.getStoragePath(ConfigConsts.TOKEN_OF_SETTINGS)); + } +} diff --git a/src/test/java/com/mesasoft/cn/dao/AuthDAOTest.java b/src/test/java/com/mesasoft/cn/dao/AuthDAOTest.java new file mode 100644 index 0000000..c3eb364 --- /dev/null +++ b/src/test/java/com/mesasoft/cn/dao/AuthDAOTest.java @@ -0,0 +1,30 @@ +package com.mesasoft.cn.dao; + +import com.mesasoft.cn.SketchApplicationTest; +import com.zhazhapan.util.Formatter; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * @author pantao + * @since 2018/1/19 + */ +@RunWith(SpringRunner.class) +@SpringBootTest +public class AuthDAOTest { + + static { + SketchApplicationTest.setSettings(); + } + + @Autowired + private AuthDAO authDAO; + + @Test + public void testGetAuthBy() { + System.out.println(Formatter.listToJson(authDAO.listAuthBy(0, 0, 0, "", 0))); + } +} diff --git a/src/test/java/com/mesasoft/cn/dao/CategoryDAOTest.java b/src/test/java/com/mesasoft/cn/dao/CategoryDAOTest.java new file mode 100644 index 0000000..c389c5a --- /dev/null +++ b/src/test/java/com/mesasoft/cn/dao/CategoryDAOTest.java @@ -0,0 +1,48 @@ +package com.mesasoft.cn.dao; + +import com.zhazhapan.util.Formatter; +import com.zhazhapan.util.RandomUtils; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * @author pantao + * @since 2018/1/18 + */ +@RunWith(SpringRunner.class) +@SpringBootTest +public class CategoryDAOTest { + + @Autowired + CategoryDAO categoryDAO; + + @Test + public void testInsertCategory() { + for (int i = 0; i < 10; i++) { + categoryDAO.insertCategory(RandomUtils.getRandomStringOnlyLowerCase(6)); + } + } + + @Test + public void testRemoveCategoryById() { + categoryDAO.removeCategoryById(1); + } + + @Test + public void testUpdateName() { + categoryDAO.updateNameById(3, "update"); + } + + @Test + public void testGetAllCategory() { + System.out.println(Formatter.listToJson(categoryDAO.listCategory())); + } + + @Test + public void testGetCategoryById() { + System.out.println(categoryDAO.getCategoryById(6).toString()); + } +} diff --git a/src/test/java/com/mesasoft/cn/dao/DownloadedDAOTest.java b/src/test/java/com/mesasoft/cn/dao/DownloadedDAOTest.java new file mode 100644 index 0000000..df8ec44 --- /dev/null +++ b/src/test/java/com/mesasoft/cn/dao/DownloadedDAOTest.java @@ -0,0 +1,30 @@ +package com.mesasoft.cn.dao; + +import com.mesasoft.cn.SketchApplicationTest; +import com.zhazhapan.modules.constant.ValueConsts; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * @author pantao + * @since 2018/1/19 + */ +@RunWith(SpringRunner.class) +@SpringBootTest +public class DownloadedDAOTest { + + static { + SketchApplicationTest.setSettings(); + } + + @Autowired + DownloadedDAO downloadDAO; + + @Test + public void testGetDownloadBy() { + System.out.println(downloadDAO.listDownloadedBy(1, 1, "", ValueConsts.ZERO_INT, 0)); + } +} diff --git a/src/test/java/com/mesasoft/cn/dao/FileDAOTest.java b/src/test/java/com/mesasoft/cn/dao/FileDAOTest.java new file mode 100644 index 0000000..d96328c --- /dev/null +++ b/src/test/java/com/mesasoft/cn/dao/FileDAOTest.java @@ -0,0 +1,32 @@ +package com.mesasoft.cn.dao; + +import com.mesasoft.cn.SketchApplicationTest; +import com.zhazhapan.util.Formatter; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * @author pantao + * @since 2018/2/5 + */ +@RunWith(SpringRunner.class) +@SpringBootTest +public class FileDAOTest { + + @Autowired + FileDAO fileDAO; + + @Test + public void testRemoveFile() { + assert fileDAO.removeById(3); + } + + @Test + public void testGetUserDownloaded() { + SketchApplicationTest.setSettings(); + System.out.println(Formatter.listToJson(fileDAO.listUserDownloaded(2, 0, ""))); + } +} diff --git a/src/test/java/com/mesasoft/cn/dao/UserDAOTest.java b/src/test/java/com/mesasoft/cn/dao/UserDAOTest.java new file mode 100644 index 0000000..214e325 --- /dev/null +++ b/src/test/java/com/mesasoft/cn/dao/UserDAOTest.java @@ -0,0 +1,63 @@ +package com.mesasoft.cn.dao; + +import com.mesasoft.cn.SketchApplicationTest; +import com.mesasoft.cn.entity.User; +import com.zhazhapan.util.Checker; +import com.zhazhapan.util.Formatter; +import com.zhazhapan.util.RandomUtils; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * @author pantao + * @since 2018/1/18 + */ +@RunWith(SpringRunner.class) +@SpringBootTest +public class UserDAOTest { + + static { + SketchApplicationTest.setSettings(); + } + + @Autowired + private UserDAO userDAO; + + @Test + public void testUpdateUserAuth() { + assert userDAO.updateAuthById(1, 1, 1, 1, 1, 1); + } + + @Test + public void testUpdateUserLoginTime() { + assert userDAO.updateUserLoginTime(1); + } + + @Test + public void testDoLogin() { + assert Checker.isNotNull(userDAO.login("system", "123456")); + } + + @Test + public void testInsertUser() { + String username = RandomUtils.getRandomStringOnlyLowerCase(6); + String realName = RandomUtils.getRandomStringOnlyLowerCase(6); + String email = RandomUtils.getRandomEmail(); + String password = RandomUtils.getRandomStringWithoutSymbol(16); + User user = new User(username, realName, email, password); + assert userDAO.insertUser(user); + } + + @Test + public void testGetAllUser() { + System.out.println(Formatter.listToJson(userDAO.listUserBy(3, "", 0))); + } + + @Test + public void testGetUser() { + System.out.println(userDAO.getUserById(1).toString()); + } +} diff --git a/src/test/java/com/mesasoft/cn/dao/sqlprovider/FileSqlProviderTest.java b/src/test/java/com/mesasoft/cn/dao/sqlprovider/FileSqlProviderTest.java new file mode 100644 index 0000000..1d1d201 --- /dev/null +++ b/src/test/java/com/mesasoft/cn/dao/sqlprovider/FileSqlProviderTest.java @@ -0,0 +1,41 @@ +package com.mesasoft.cn.dao.sqlprovider; + +import com.mesasoft.cn.SketchApplicationTest; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * @author pantao + * @since 2018/2/6 + */ +@RunWith(SpringRunner.class) +@SpringBootTest +public class FileSqlProviderTest { + + @Autowired + FileSqlProvider fileSqlProvider; + + @Test + public void updateAuthById() { + System.out.println(fileSqlProvider.updateAuthById()); + } + + @Test + public void getAll() { + SketchApplicationTest.setSettings(); + System.out.println(fileSqlProvider.getAll(0, 0, "", "")); + } + + @Test + public void getUserUploaded() { + System.out.println(fileSqlProvider.getUserUploaded(0, "")); + } + + @Test + public void getUserDownloaded() { + System.out.println(fileSqlProvider.getUserDownloaded(0, "")); + } +} diff --git a/src/test/java/com/mesasoft/cn/service/CategoryServiceTest.java b/src/test/java/com/mesasoft/cn/service/CategoryServiceTest.java new file mode 100644 index 0000000..1245c03 --- /dev/null +++ b/src/test/java/com/mesasoft/cn/service/CategoryServiceTest.java @@ -0,0 +1,24 @@ +package com.mesasoft.cn.service; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * @author pantao + * @since 2018/2/9 + */ +@RunWith(SpringRunner.class) +@SpringBootTest +public class CategoryServiceTest { + + @Autowired + ICategoryService categoryService; + + @Test + public void testGetIdByName() { + System.out.println(categoryService.getIdByName("fff")); + } +}