This commit is contained in:
Andrey Savihin 2019-09-05 12:03:44 +03:00
commit 4f0336e288
44 changed files with 3456 additions and 2 deletions

1
build/install/snap/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
!*/

View File

@ -0,0 +1,7 @@
#!/bin/bash
rm -dfr parts
rm -dfr prime
rm -dfr stage
#snapcraft

View File

@ -0,0 +1,4 @@
!GlobalState
assets:
build-packages: []
build-snaps: []

View File

@ -0,0 +1,27 @@
import os
import logging
import shutil
import re
import subprocess
import snapcraft
from snapcraft.plugins import make
logger = logging.getLogger(__name__)
class RedisPlugin(make.MakePlugin):
def build(self):
super(make.MakePlugin, self).build()
command = ['make']
if self.options.makefile:
command.extend(['-f', self.options.makefile])
if self.options.make_parameters:
command.extend(self.options.make_parameters)
self.run(command + ['-j{}'.format(self.project.parallel_build_count)])
self.run(command + ['install', 'PREFIX=' + self.installdir])

View File

@ -0,0 +1,153 @@
name: onlyoffice-communityserver
version: "10.0.0"
summary: ""
description: ""
grade: stable
confinement: devmode
apps:
nginx:
command: start_nginx
daemon: simple
restart-condition: always
plugs: [network, network-bind]
mysql:
command: start_mysql
stop-command: support-files/mysql.server stop
daemon: simple
restart-condition: always
plugs: [network, network-bind]
mysql-client:
command: mysql --defaults-file=$SNAP_DATA/mysql/root.ini
plugs: [network, network-bind]
mysqldump:
command: mysqldump --defaults-file=$SNAP_DATA/mysql/root.ini --lock-tables onlyoffice
plugs: [network, network-bind]
hooks:
configure:
plugs: [network, network-bind]
parts:
python:
plugin: python
python-version: python3
node:
plugin: nodejs
node-engine: 12.9.1
nginx:
plugin: autotools
source: https://github.com/nginx/nginx.git
source-type: git
# Need the prepare step because configure script resides in an unintuitive
# location.
override-build: |
cp auto/configure .
snapcraftctl build
build-packages:
- libpcre3
- libpcre3-dev
- zlib1g-dev
stage:
# Remove scripts that we'll be replacing with our own
- -conf/nginx.conf
nginx-customizations:
plugin: dump
source: src/nginx/
# Download the boost headers for MySQL. Note that the version used may need to
# be updated if the version of MySQL changes.
boost:
plugin: dump
source: https://github.com/kyrofa/boost_tarball/raw/master/boost_1_59_0.tar.gz
source-checksum: sha1/5123209db194d66d69a9cfa5af8ff473d5941d97
# When building MySQL, the headers in the source directory 'boost/' are
# required. Previously, using the 'copy' plugin, the whole archive was put
# under 'boost/', making the headers reside in 'boost/boost/'. Due to a bug,
# we now only stage the 'boost/' directory without moving it.
#
# Bug: https://bugs.launchpad.net/snapcraft/+bug/1757093
stage:
- boost/
prime:
- -*
mysql:
plugin: cmake
source: https://github.com/mysql/mysql-server.git
source-tag: mysql-5.7.22
source-depth: 1
override-pull: |
snapcraftctl pull
git apply $SNAPCRAFT_STAGE/support-compile-time-disabling-of-setpriority.patch
after: [boost, patches]
configflags:
- -DWITH_BOOST=$SNAPCRAFT_STAGE
- -DWITH_INNODB_PAGE_CLEANER_PRIORITY=OFF
- -DCMAKE_INSTALL_PREFIX=/
- -DBUILD_CONFIG=mysql_release
- -DWITH_UNIT_TESTS=OFF
- -DWITH_EMBEDDED_SERVER=OFF
- -DWITH_ARCHIVE_STORAGE_ENGINE=OFF
- -DWITH_BLACKHOLE_STORAGE_ENGINE=OFF
- -DWITH_FEDERATED_STORAGE_ENGINE=OFF
- -DWITH_PARTITION_STORAGE_ENGINE=OFF
- -DINSTALL_MYSQLTESTDIR=
build-packages:
- wget
- g++
- cmake
- bison
- libncurses5-dev
- libaio-dev
stage:
# Remove scripts that we'll be replacing with our own
- -support-files/mysql.server
- -COPYING
prime:
# Remove scripts that we'll be replacing with our own
- -support-files/mysql.server
# Remove unused binaries that waste space
- -bin/innochecksum
- -bin/lz4_decompress
- -bin/myisam*
- -bin/mysqladmin
- -bin/mysqlbinlog
- -bin/mysql_client_test
- -bin/mysql_config*
- -bin/mysqld_multi
- -bin/mysqlimport
- -bin/mysql_install_db
- -bin/mysql_plugin
- -bin/mysqlpump
- -bin/mysql_secure_installation
- -bin/mysqlshow
- -bin/mysqlslap
- -bin/mysql_ssl_rsa_setup
- -bin/mysqltest
- -bin/mysql_tzinfo_to_sql
- -bin/perror
- -bin/replace
- -bin/resolveip
- -bin/resolve_stack_dump
- -bin/zlib_decompress
# Copy over our MySQL scripts
mysql-customizations:
plugin: dump
source: src/mysql/
patches:
source: src/patches
plugin: dump
prime:
- -*
hooks:
plugin: dump
source: src/hooks/
organize:
bin/: snap/hooks/

10
build/install/snap/src/hooks/bin/configure vendored Executable file
View File

@ -0,0 +1,10 @@
#!/bin/sh
# shellcheck source=src/hooks/utilities/hook-utilities
. "$SNAP/utilities/hook-utilities"
# Signal to services that the configure hook is running. Useful to ensure
# services don't restart until the configuration transaction has completed.
set_configure_hook_running
trap 'set_configure_hook_not_running' EXIT

View File

@ -0,0 +1,32 @@
#!/bin/sh
CONFIGURE_LOCKFILE="/tmp/locks/configure-hook"
mkdir -p "$(dirname $CONFIGURE_LOCKFILE)"
chmod 750 "$(dirname $CONFIGURE_LOCKFILE)"
configure_hook_running()
{
[ -f "$CONFIGURE_LOCKFILE" ]
}
set_configure_hook_running()
{
touch "$CONFIGURE_LOCKFILE"
}
set_configure_hook_not_running()
{
rm -f "$CONFIGURE_LOCKFILE"
}
wait_for_configure_hook()
{
if configure_hook_running; then
printf "Waiting for configure hook... "
while configure_hook_running; do
sleep 1
done
printf "done\n"
fi
}

View File

@ -0,0 +1,97 @@
#!/bin/sh
# shellcheck source=src/mysql/utilities/mysql-utilities
. "$SNAP/utilities/mysql-utilities"
root_option_file="$SNAP_DATA/mysql/root.ini"
new_install=false
# Make sure the database is initialized (this is safe to run if already
# initialized)
if mysqld --initialize-insecure --basedir="$SNAP" --datadir="$SNAP_DATA/mysql" --lc-messages-dir="$SNAP/share"; then
new_install=true
fi
set_mysql_setup_running
# Start mysql
"$SNAP/support-files/mysql.server" start
# Initialize new installation if necessary.
if [ $new_install = true ]; then
# Generate a password for the root mysql user.
printf "Generating root mysql password... "
root_password="$(tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c64)"
printf "done\n"
# Generate a password for the onlyoffice mysql user.
printf "Generating onlyoffice mysql password... "
onlyoffice_password="$(tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c64)"
printf "done\n"
# Save root user information
cat <<-EOF > "$root_option_file"
[client]
socket=$MYSQL_SOCKET
user=root
EOF
chmod 600 "$root_option_file"
# Now set everything up in one step:
# 1) Set the root user's password
# 2) Create the onlyoffice user
# 3) Create the onlyoffice database
# 4) Grant the onlyoffice user privileges on the onlyoffice database
printf "Setting up users and onlyoffice database... "
if mysql --defaults-file="$root_option_file" <<-SQL
ALTER USER 'root'@'localhost' IDENTIFIED BY '$root_password';
CREATE USER 'onlyoffice'@'localhost' IDENTIFIED WITH mysql_native_password BY '$onlyoffice_password';
CREATE DATABASE IF NOT EXISTS onlyoffice CHARACTER SET utf8 COLLATE 'utf8_general_ci';
GRANT ALL PRIVILEGES ON onlyoffice.* TO 'onlyoffice'@'localhost' IDENTIFIED WITH mysql_native_password BY '$onlyoffice_password';
SQL
then
printf "done\n"
else
echo "Failed to initialize-- reverting..."
"$SNAP/support-files/mysql.server" stop
rm -rf "$SNAP_DATA"/mysql/*
fi
# Now the root mysql user has a password. Save that as well.
echo "password=$root_password" >> "$root_option_file"
else
# Okay, this isn't a new installation. However, we recently changed
# the location of MySQL's socket. Make sure the root
# option file is updated to look there instead of the old location.
sed -ri "s|(socket\s*=\s*)/var/snap/.*mysql.sock|\1$MYSQL_SOCKET|" "$root_option_file"
fi
# Wait here until mysql is running
wait_for_mysql -f
# Check and upgrade mysql tables if necessary. This will return 0 if the upgrade
# succeeded, in which case we need to restart mysql.
echo "Checking/upgrading mysql tables if necessary..."
if mysql_upgrade --defaults-file="$root_option_file"; then
echo "Restarting mysql server after upgrade..."
"$SNAP/support-files/mysql.server" restart
# Wait for server to come back after upgrade
wait_for_mysql -f
fi
# If this was a new installation, wait until the server is all up and running
# before saving off the onlyoffice user's password. This way the presence of the
# file can be used as a signal that mysql is ready to be used.
if [ $new_install = true ]; then
mysql_set_onlyoffice_password "$onlyoffice_password"
fi
set_mysql_setup_not_running
# Wait here until mysql exits (turn a forking service into simple). This is
# only needed for Ubuntu Core 15.04, as 16.04 supports forking services.
pid=$(mysql_pid)
while kill -0 "$pid" 2>/dev/null; do
sleep 1
done

View File

@ -0,0 +1,8 @@
[mysqld]
user=root
secure-file-priv=NULL
skip-networking
sql_mode = 'NO_ENGINE_SUBSTITUTION'
max_connections = 1000
max_allowed_packet = 1048576000
group_concat_max_len = 2048

View File

@ -0,0 +1,315 @@
#!/bin/sh
# Copyright Abandoned 1996 TCX DataKonsult AB & Monty Program KB & Detron HB
# This file is public domain and comes with NO WARRANTY of any kind
# MySQL daemon start/stop script.
# Usually this is put in /etc/init.d (at least on machines SYSV R4 based
# systems) and linked to /etc/rc3.d/S99mysql and /etc/rc0.d/K01mysql.
# When this is done the mysql server will be started when the machine is
# started and shut down when the systems goes down.
# Comments to support chkconfig on RedHat Linux
# chkconfig: 2345 64 36
# description: A very fast and reliable SQL database engine.
# Comments to support LSB init script conventions
### BEGIN INIT INFO
# Provides: mysql
# Required-Start: $local_fs $network $remote_fs
# Should-Start: ypbind nscd ldap ntpd xntpd
# Required-Stop: $local_fs $network $remote_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: start and stop MySQL
# Description: MySQL is a very fast and reliable SQL database engine.
### END INIT INFO
# If you install MySQL on some other places than /, then you
# have to do one of the following things for this script to work:
#
# - Run this script from within the MySQL installation directory
# - Create a /etc/my.cnf file with the following information:
# [mysqld]
# basedir=<path-to-mysql-installation-directory>
# - Add the above to any other configuration file (for example ~/.my.ini)
# and copy my_print_defaults to /usr/bin
# - Add the path to the mysql-installation-directory to the basedir variable
# below.
#
# If you want to affect other MySQL variables, you should make your changes
# in the /etc/my.cnf, ~/.my.cnf or other MySQL configuration files.
# If you change base dir, you must also change datadir. These may get
# overwritten by settings in the MySQL configuration files.
# shellcheck source=src/mysql/utilities/mysql-utilities
. "$SNAP/utilities/mysql-utilities"
basedir="$SNAP"
datadir="$SNAP_DATA/mysql"
# Default value, in seconds, afterwhich the script should timeout waiting
# for server start.
# Value here is overriden by value in my.cnf.
# 0 means don't wait at all
# Negative numbers mean to wait indefinitely
service_startup_timeout=900
# Lock directory for RedHat / SuSE.
lockdir="$SNAP_DATA/mysql/lock"
lock_file_path="$lockdir/mysql"
# The following variables are only set for letting mysql.server find things.
# Set some defaults
mysqld_pid_file_path="$MYSQL_PIDFILE"
if test -z "$basedir"
then
basedir=/
bindir=//bin
if test -z "$datadir"
then
datadir=//data
fi
libexecdir=//bin
else
bindir="$basedir/bin"
if test -z "$datadir"
then
datadir="$basedir/data"
fi
libexecdir="$basedir/libexec"
fi
#
# Use LSB init script functions for printing messages, if possible
#
lsb_functions="/lib/lsb/init-functions"
if test -f $lsb_functions ; then
. $lsb_functions
else
log_success_msg()
{
echo " SUCCESS! $*"
}
log_failure_msg()
{
echo " ERROR! $*"
}
fi
PATH="/sbin:/usr/sbin:/bin:/usr/bin:$basedir/bin"
export PATH
mode=$1 # start or stop
[ $# -ge 1 ] && shift
other_args="$*" # uncommon, but needed when called from an RPM upgrade action
# Expected: "--skip-networking --skip-grant-tables"
# They are not checked here, intentionally, as it is the resposibility
# of the "spec" file author to give correct arguments only.
# Upstream mysql stuff, no need to fix this
# shellcheck disable=SC2116,SC2039
case "$(echo "testing\c")","$(echo -n testing)" in
*c*,-n*) echo_n="" echo_c="" ;;
*c*,*) echo_n=-n echo_c="" ;;
*) echo_n="" echo_c='\c' ;;
esac
wait_for_pid () {
verb="$1" # created | removed
pid="$2" # process ID of the program operating on the pid-file
pid_file_path="$3" # path to the PID file.
i=0
avoid_race_condition="by checking again"
while test "$i" -ne "$service_startup_timeout" ; do
case "$verb" in
'created')
# wait for a PID-file to pop into existence.
test -s "$pid_file_path" && i='' && break
;;
'removed')
# wait for this PID-file to disappear
test ! -s "$pid_file_path" && i='' && break
;;
*)
echo "wait_for_pid () usage: wait_for_pid created|removed pid pid_file_path"
exit 1
;;
esac
# if server isn't running, then pid-file will never be updated
if test -n "$pid"; then
if kill -0 "$pid" 2>/dev/null; then
: # the server still runs
else
# The server may have exited between the last pid-file check and now.
if test -n "$avoid_race_condition"; then
avoid_race_condition=""
continue # Check again.
fi
# there's nothing that will affect the file.
log_failure_msg "The server quit without updating PID file ($pid_file_path)."
return 1 # not waiting any more.
fi
fi
echo $echo_n ".$echo_c"
i=$((i + 1))
sleep 1
done
if test -z "$i" ; then
log_success_msg
return 0
else
log_failure_msg
return 1
fi
}
#
# Set pid file if not given
#
if test -z "$mysqld_pid_file_path"
then
mysqld_pid_file_path="$datadir"/"$(hostname)".pid
else
case "$mysqld_pid_file_path" in
/* ) ;;
* ) mysqld_pid_file_path="$datadir/$mysqld_pid_file_path" ;;
esac
fi
case "$mode" in
'start')
# Start daemon
# Safeguard (relative paths, core dumps..)
cd "$basedir" || exit
echo $echo_n "Starting MySQL"
if test -x "$bindir/mysqld_safe"
then
# Give extra arguments to mysqld with the my.cnf file. This script
# may be overwritten at next upgrade.
"$bindir/mysqld_safe" --datadir="$datadir" --pid-file="$mysqld_pid_file_path" --lc-messages-dir="$SNAP/share" --socket="$MYSQL_SOCKET" "$other_args" >/dev/null 2>&1 &
wait_for_pid created "$!" "$mysqld_pid_file_path"; return_value=$?
# Make lock for RedHat / SuSE
if test -w "$lockdir"
then
touch "$lock_file_path"
fi
exit $return_value
else
log_failure_msg "Couldn't find MySQL server ($bindir/mysqld_safe)"
fi
;;
'stop')
# Stop daemon. We use a signal here to avoid having to know the
# root password.
if test -s "$mysqld_pid_file_path"
then
# signal mysqld_safe that it needs to stop
touch "$mysqld_pid_file_path.shutdown"
mysqld_pid="$(cat "$mysqld_pid_file_path")"
if (kill -0 "$mysqld_pid" 2>/dev/null)
then
echo $echo_n "Shutting down MySQL"
kill "$mysqld_pid"
# mysqld should remove the pid file when it exits, so wait for it.
wait_for_pid removed "$mysqld_pid" "$mysqld_pid_file_path"; return_value=$?
else
log_failure_msg "MySQL server process #$mysqld_pid is not running!"
rm "$mysqld_pid_file_path"
fi
# Delete lock for RedHat / SuSE
if test -f "$lock_file_path"
then
rm -f "$lock_file_path"
fi
exit $return_value
else
log_failure_msg "MySQL server PID file could not be found!"
fi
;;
'restart')
# Stop the service and regardless of whether it was
# running or not, start it again.
if $0 stop "$other_args"; then
$0 start "$other_args"
else
log_failure_msg "Failed to stop running server, so refusing to try to start."
exit 1
fi
;;
'reload'|'force-reload')
if test -s "$mysqld_pid_file_path" ; then
read -r mysqld_pid < "$mysqld_pid_file_path"
kill -HUP "$mysqld_pid" && log_success_msg "Reloading service MySQL"
touch "$mysqld_pid_file_path"
else
log_failure_msg "MySQL PID file could not be found!"
exit 1
fi
;;
'status')
# First, check to see if pid file exists
if test -s "$mysqld_pid_file_path" ; then
read -r mysqld_pid < "$mysqld_pid_file_path"
if kill -0 "$mysqld_pid" 2>/dev/null ; then
log_success_msg "MySQL running ($mysqld_pid)"
exit 0
else
log_failure_msg "MySQL is not running, but PID file exists"
exit 1
fi
else
# Try to find appropriate mysqld process
mysqld_pid="$(pidof "$libexecdir/mysqld")"
# test if multiple pids exist
pid_count="$(echo "$mysqld_pid" | wc -w)"
if test "$pid_count" -gt 1 ; then
log_failure_msg "Multiple MySQL running but PID file could not be found ($mysqld_pid)"
exit 5
elif test -z "$mysqld_pid" ; then
if test -f "$lock_file_path" ; then
log_failure_msg "MySQL is not running, but lock file ($lock_file_path) exists"
exit 2
fi
log_failure_msg "MySQL is not running"
exit 3
else
log_failure_msg "MySQL is running but PID file could not be found"
exit 4
fi
fi
;;
*)
# usage
basename="$(basename "$0")"
echo "Usage: $basename {start|stop|restart|reload|force-reload|status} [ MySQL server options ]"
exit 1
;;
esac
exit 0

View File

@ -0,0 +1,72 @@
#!/bin/sh
export MYSQL_PIDFILE="/tmp/pids/mysql.pid"
export MYSQL_SOCKET="/tmp/sockets/mysql.sock"
export ONLYOFFICE_PASSWORD_FILE="$SNAP_DATA/mysql/onlyoffice_password"
MYSQL_SETUP_LOCKFILE="/tmp/locks/mysql-setup"
mkdir -p "$(dirname "$MYSQL_PIDFILE")"
mkdir -p "$(dirname "$MYSQL_SOCKET")"
chmod 750 "$(dirname "$MYSQL_PIDFILE")"
chmod 750 "$(dirname "$MYSQL_SOCKET")"
mysql_is_running()
{
# Arguments:
# -f: Force the check, i.e. ignore if it's currently in setup
[ -f "$MYSQL_PIDFILE" ] && [ -S "$MYSQL_SOCKET" ] && (! mysql_setup_running || [ "$1" = "-f" ])
}
wait_for_mysql()
{
# Arguments:
# -f: Force the check, i.e. ignore if it's currently in setup
if ! mysql_is_running "$@"; then
printf "Waiting for MySQL... "
while ! mysql_is_running "$@"; do
sleep 1
done
printf "done\n"
fi
}
mysql_setup_running()
{
[ -f "$MYSQL_SETUP_LOCKFILE" ]
}
set_mysql_setup_running()
{
touch "$MYSQL_SETUP_LOCKFILE"
}
set_mysql_setup_not_running()
{
rm -f "$MYSQL_SETUP_LOCKFILE"
}
mysql_pid()
{
if mysql_is_running; then
cat "$MYSQL_PIDFILE"
else
echo "Unable to get MySQL PID as it's not yet running" >&2
echo ""
fi
}
mysql_set_onlyoffice_password()
{
echo "$1" > "$ONLYOFFICE_PASSWORD_FILE"
chmod 600 "$ONLYOFFICE_PASSWORD_FILE"
}
mysql_get_onlyoffice_password()
{
if [ -f "$ONLYOFFICE_PASSWORD_FILE" ]; then
cat "$ONLYOFFICE_PASSWORD_FILE"
else
echo "MySQL ONLYOFFICE password has not yet been generated" >&2
echo ""
fi
}

View File

@ -0,0 +1,12 @@
#!/bin/sh
# shellcheck source=src/nginx/utilities/nginx-utilities
. "$SNAP/utilities/nginx-utilities"
cp -dfr ${SNAP}/config/nginx-config/* ${SNAP_DATA}/nginx/config
sed -e "s|\${SNAP}|$SNAP|;s|\${SNAP_DATA}|$SNAP_DATA|;s|\${NGINX_PIDFILE}|$NGINX_PIDFILE|;s|\${ONLYOFFICE_SOCKET}|$ONLYOFFICE_SOCKET|;s|\${ONLYOFFICE_API_SYSTEM_SOCKET}|$ONLYOFFICE_API_SYSTEM_SOCKET|" -i ${SNAP_DATA}/nginx/config/conf.d/onlyoffice
sed -e "s|\${SNAP}|$SNAP|;s|\${SNAP_DATA}|$SNAP_DATA|;s|\${NGINX_PIDFILE}|$NGINX_PIDFILE|;s|\${ONLYOFFICE_SOCKET}|$ONLYOFFICE_SOCKET|;s|\${ONLYOFFICE_API_SYSTEM_SOCKET}|$ONLYOFFICE_API_SYSTEM_SOCKET|" -i ${SNAP_DATA}/nginx/config/conf.d/includes/onlyoffice-communityserver-common.conf
sed -e "s|\${SNAP}|$SNAP|;s|\${SNAP_DATA}|$SNAP_DATA|;s|\${NGINX_PIDFILE}|$NGINX_PIDFILE|;s|\${ONLYOFFICE_SOCKET}|$ONLYOFFICE_SOCKET|;s|\${ONLYOFFICE_API_SYSTEM_SOCKET}|$ONLYOFFICE_API_SYSTEM_SOCKET|" -i ${SNAP_DATA}/nginx/config/nginx.conf
exec "$SNAP/sbin/nginx" "-c" "$SNAP_DATA/nginx/config/nginx.conf" "-p" "$SNAP_DATA/nginx" "-g" "daemon off;" "$@"

View File

@ -0,0 +1,43 @@
upstream fastcgi_backend {
server unix:/var/run/onlyoffice/onlyoffice.socket;
keepalive 32;
}
server {
listen 80;
fastcgi_keep_conn on;
fastcgi_index Default.aspx;
fastcgi_intercept_errors on;
include fastcgi_params;
fastcgi_param HTTP_X_REWRITER_URL $http_x_rewriter_url;
fastcgi_param SERVER_NAME $host;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO "";
fastcgi_read_timeout 600;
fastcgi_send_timeout 600;
location / {
root /var/www/onlyoffice/WebStudio/;
expires 0;
add_header Cache-Control no-cache;
rewrite ^(.*)$ /StartConfigure.htm break;
}
location /api {
fastcgi_pass fastcgi_backend;
break;
}
location ~* ^/(warmup[2-9]?)/ {
rewrite /warmup([^/]*)/(.*) /$2 break;
fastcgi_pass unix:/var/run/onlyoffice/onlyoffice$1.socket;
}
}

View File

@ -0,0 +1,129 @@
upstream fastcgi_backend_apisystem {
server unix:/var/run/onlyoffice/onlyofficeApiSystem.socket;
keepalive 32;
}
upstream fastcgi_backend {
server unix:/var/run/onlyoffice/onlyoffice.socket;
keepalive {{ONLYOFFICE_NIGNX_KEEPLIVE}};
}
fastcgi_cache_path /var/cache/nginx/onlyoffice
levels=1:2
keys_zone=onlyoffice:16m
max_size=256m
inactive=1d;
geo $ip_external {
default 1;
{{DOCKER_ONLYOFFICE_SUBNET}} 0;
127.0.0.1 0;
}
map $http_host $this_host {
"" $host;
default $http_host;
}
map $http_x_forwarded_proto $the_scheme {
default $http_x_forwarded_proto;
"" $scheme;
}
map $http_x_forwarded_host $the_host {
default $http_x_forwarded_host;
"" $this_host;
}
## Normal HTTP host
server {
listen 0.0.0.0:80;
listen [::]:80 default_server;
server_name _;
server_tokens off;
root /nowhere; ## root doesn't have to be a valid path since we are redirecting
location / {
if ($ip_external) {
## Redirects all traffic to the HTTPS host
rewrite ^ https://$host$request_uri? permanent;
}
client_max_body_size 100m;
proxy_pass https://127.0.0.1;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_ssl_verify off;
}
}
## HTTPS host
server {
listen 0.0.0.0:443 ssl;
listen [::]:443 ssl default_server;
server_tokens off;
root /usr/share/nginx/html;
## Increase this if you want to upload large attachments
client_max_body_size 100m;
## Strong SSL Security
## https://cipherli.st/
ssl on;
ssl_certificate {{SSL_CERTIFICATE_PATH}};
ssl_certificate_key {{SSL_KEY_PATH}};
ssl_verify_client {{SSL_VERIFY_CLIENT}};
ssl_client_certificate {{CA_CERTIFICATES_PATH}};
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off; # Requires nginx >= 1.5.9
add_header Strict-Transport-Security "max-age={{ONLYOFFICE_HTTPS_HSTS_MAXAGE}}; includeSubDomains; preload" always;
# add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header Access-Control-Allow-Origin *;
## [Optional] If your certficate has OCSP, enable OCSP stapling to reduce the overhead and latency of running SSL.
## Replace with your ssl_trusted_certificate. For more info see:
## - https://medium.com/devops-programming/4445f4862461
## - https://www.ruby-forum.com/topic/4419319
## - https://www.digitalocean.com/community/tutorials/how-to-configure-ocsp-stapling-on-apache-and-nginx
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate {{SSL_OCSP_CERTIFICATE_PATH}};
resolver 8.8.8.8 8.8.4.4 127.0.0.11 valid=300s; # Can change to your DNS resolver if desired
resolver_timeout 10s;
## [Optional] Generate a stronger DHE parameter:
## cd /etc/ssl/certs
## sudo openssl dhparam -out dhparam.pem 4096
##
ssl_dhparam {{SSL_DHPARAM_PATH}};
large_client_header_buffers 4 16k;
set $X_REWRITER_URL $the_scheme://$the_host;
if ($http_x_rewriter_url != '') {
set $X_REWRITER_URL $http_x_rewriter_url ;
}
include /etc/nginx/includes/onlyoffice-communityserver-*.conf;
}

View File

@ -0,0 +1,83 @@
location / {
root ${SNAP}/var/www/onlyoffice/WebStudio/;
index index.html index.htm default.aspx Default.aspx;
client_max_body_size 4G;
fastcgi_pass fastcgi_backend;
fastcgi_keep_conn on;
error_page 404 /404.htm;
gzip off;
gzip_comp_level 2;
gzip_min_length 1000;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/html application/x-javascript text/css application/xml;
fastcgi_index Default.aspx;
fastcgi_intercept_errors on;
include ${SNAP}/conf/fastcgi_params;
fastcgi_param HTTP_X_REWRITER_URL $X_REWRITER_URL;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO "";
fastcgi_read_timeout 600;
fastcgi_send_timeout 600;
location ~* (^\/(?:skins|products|addons).*\.(?:jpg|jpeg|gif|png|svg|ico)$)|(.*bundle/(?!clientscript).*) {
fastcgi_pass fastcgi_backend;
fastcgi_temp_path ${SNAP_DATA}/nginx/cache/tmp 1 2;
fastcgi_cache onlyoffice;
fastcgi_cache_key "$scheme|$request_method|$host|$request_uri|$query_string";
fastcgi_cache_use_stale updating error timeout invalid_header http_500;
fastcgi_cache_valid 1d;
fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
add_header X-Fastcgi-Cache $upstream_cache_status;
access_log off;
log_not_found off;
expires max;
}
}
location /apisystem {
rewrite /apisystem(.*) /$1 break;
root ${SNAP}/var/www/onlyoffice/ApiSystem/;
index index.html index.htm default.aspx Default.aspx;
add_header Access-Control-Allow-Origin *;
add_header X-Frame-Options DENY;
client_max_body_size 4G;
fastcgi_keep_conn on;
fastcgi_pass fastcgi_backend_apisystem;
include ${SNAP}/conf/fastcgi_params;
set $X_REWRITER_URL $scheme://$http_host;
if ($http_x_rewriter_url != '') {
set $X_REWRITER_URL $http_x_rewriter_url ;
}
fastcgi_param HTTP_X_REWRITER_URL $X_REWRITER_URL;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO "";
fastcgi_read_timeout 600;
fastcgi_send_timeout 600;
}
location /filesData {
rewrite /filesData/var/www/onlyoffice/Data/Products/Files(.*) /$1 break;
root ${SNAP_DATA}/onlyoffice/Data/Products/Files;
internal;
}

View File

@ -0,0 +1,46 @@
upstream fastcgi_backend_apisystem {
server unix:/var/run/onlyoffice/onlyofficeApiSystem.socket;
keepalive 32;
}
upstream fastcgi_backend {
server unix:/var/run/onlyoffice/onlyoffice.socket;
keepalive {{ONLYOFFICE_NIGNX_KEEPLIVE}};
}
fastcgi_cache_path /var/cache/nginx/onlyoffice
levels=1:2
keys_zone=onlyoffice:16m
max_size=256m
inactive=1d;
map $http_host $this_host {
"" $host;
default $http_host;
}
map $http_x_forwarded_proto $the_scheme {
default $http_x_forwarded_proto;
"" $scheme;
}
map $http_x_forwarded_host $the_host {
default $http_x_forwarded_host;
"" $this_host;
}
server {
listen 80;
add_header Access-Control-Allow-Origin *;
large_client_header_buffers 4 16k;
set $X_REWRITER_URL $the_scheme://$the_host;
if ($http_x_rewriter_url != '') {
set $X_REWRITER_URL $http_x_rewriter_url ;
}
include /etc/nginx/includes/onlyoffice-communityserver-*.conf;
}

View File

@ -0,0 +1,3 @@
location /.well-known/acme-challenge {
root /var/www/onlyoffice/Data/certs/;
}

View File

@ -0,0 +1,32 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/sites-enabled/*;
include /etc/nginx/conf.d/*.conf;
}

View File

@ -0,0 +1,36 @@
location /controlpanel {
proxy_pass http://{{CONTROL_PANEL_HOST_ADDR}};
client_max_body_size 100m;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-REWRITER-URL $X_REWRITER_URL;
}
location /sso/ {
proxy_pass http://{{SERVICE_SSO_AUTH_HOST_ADDR}}:9834;
client_max_body_size 100m;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-REWRITER-URL $X_REWRITER_URL;
proxy_redirect / /;
}

View File

@ -0,0 +1,18 @@
location ~* ^/ds-vpath/ {
rewrite /ds-vpath/(.*) /$1 break;
proxy_pass {{DOCUMENT_SERVER_HOST_ADDR}};
proxy_redirect off;
client_max_body_size 100m;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $the_host/ds-vpath;
proxy_set_header X-Forwarded-Proto $the_scheme;
}

View File

@ -0,0 +1,44 @@
location /addons/talk/http-poll/httppoll.ashx {
proxy_pass http://localhost:5280/http-poll/;
proxy_buffering off;
client_max_body_size 10m;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
location /socketio {
rewrite /socketio/(.*) /$1 break;
proxy_pass http://localhost:9899;
client_max_body_size 100m;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Forwarded-Proto "http";
proxy_set_header X-REWRITER-URL $X_REWRITER_URL;
}
location /healthcheck {
rewrite /healthcheck(.*) /$1 break;
proxy_pass http://localhost:9810;
proxy_redirect ~*/(.*) /healthcheck/$1;
client_max_body_size 100m;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Forwarded-Proto $scheme;
}

View File

@ -0,0 +1,46 @@
upstream fastcgi_backend_apisystem {
server unix:${ONLYOFFICE_API_SYSTEM_SOCKET};
keepalive 32;
}
upstream fastcgi_backend {
server unix:${ONLYOFFICE_SOCKET};
keepalive 32;
}
fastcgi_cache_path ${SNAP_DATA}/nginx/cache/onlyoffice
levels=1:2
keys_zone=onlyoffice:16m
max_size=256m
inactive=1d;
map $http_host $this_host {
"" $host;
default $http_host;
}
map $http_x_forwarded_proto $the_scheme {
default $http_x_forwarded_proto;
"" $scheme;
}
map $http_x_forwarded_host $the_host {
default $http_x_forwarded_host;
"" $this_host;
}
server {
listen 80;
add_header Access-Control-Allow-Origin *;
large_client_header_buffers 4 16k;
set $X_REWRITER_URL $the_scheme://$the_host;
if ($http_x_rewriter_url != '') {
set $X_REWRITER_URL $http_x_rewriter_url ;
}
include ${SNAP_DATA}/nginx/config/conf.d/includes/onlyoffice-communityserver-*.conf;
}

View File

@ -0,0 +1,30 @@
user root;
worker_processes 2;
error_log ${SNAP_DATA}/nginx/logs/error.log warn;
pid ${NGINX_PIDFILE};
events {
worker_connections 1048576;
}
http {
include ${SNAP}/conf/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log ${SNAP_DATA}/nginx/logs/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include ${SNAP_DATA}/nginx/config/conf.d/onlyoffice;
}

View File

@ -0,0 +1,17 @@
#!/bin/sh
export NGINX_PIDFILE="/tmp/pids/nginx.pid"
mkdir -p "$(dirname "$NGINX_PIDFILE")"
chmod 750 "$(dirname "$NGINX_PIDFILE")"
mkdir -p ${SNAP_DATA}/nginx/logs
chmod 750 ${SNAP_DATA}/nginx/logs
mkdir -p ${SNAP_DATA}/nginx/cache
chmod 750 ${SNAP_DATA}/nginx/cache
mkdir -p ${SNAP_DATA}/nginx/config
chmod 750 ${SNAP_DATA}/nginx/config

View File

@ -0,0 +1,87 @@
#!/bin/bash
set -x
# shellcheck source=src/mysql/utilities/mysql-utilities
. "$SNAP/utilities/mysql-utilities"
# shellcheck source=src/onlyoffice/utilities/monoserve-utilities
. "${SNAP}/utilities/monoserve-utilities"
# shellcheck source=src/redis/utilities/redis-utilities
. "$SNAP/utilities/redis-utilities"
wait_for_redis
wait_for_mysql
DB_NAME="onlyoffice";
DB_HOST="localhost";
DB_USER="onlyoffice";
DB_PWD=$( mysql_get_onlyoffice_password );
ONLYOFFICE_CORE_MACHINEKEY=$( onlyoffice_get_core_machine_key );
cp -drf ${SNAP}/config/hyperfastcgi-config/* ${SNAP_DATA}/hyperfastcgi/
[ -e ${ONLYOFFCE_SOCKET} ] && rm -f ${ONLYOFFCE_SOCKET}
sed -e "s|\${SNAP}|$SNAP|;s|\${SNAP_DATA}|$SNAP_DATA|;s|\${ONLYOFFICE_SOCKET}|$ONLYOFFICE_SOCKET|" -i ${SNAP_DATA}/hyperfastcgi/onlyoffice
mkdir -p ${SNAP_DATA}/onlyoffice/config/WebStudio/
cp -dfr ${SNAP}/var/www/onlyoffice/WebStudio/*.config ${SNAP_DATA}/onlyoffice/config/WebStudio/
sed "/core.machinekey/s!value=\".*\"!value=\"${ONLYOFFICE_CORE_MACHINEKEY}\"!g" -i ${SNAP_DATA}/onlyoffice/config/WebStudio/web.appsettings.config
sed "s!/var/log/onlyoffice/!${SNAP_DATA}/onlyoffice/logs/!g" -i ${SNAP_DATA}/onlyoffice/config/WebStudio/web.log4net.config
sed "s|\.*\\\Data\\\|${SNAP_DATA}/onlyoffice/data/|g" -i ${SNAP_DATA}/onlyoffice/config/WebStudio/web.storage.config
sed "s|Password=.*;|Password=${DB_PWD};|g" -i ${SNAP_DATA}/onlyoffice/config/WebStudio/web.connections.config
sed "s|User\\s*ID=.*;|User\\s*ID=${DB_USER};|g" -i ${SNAP_DATA}/onlyoffice/config/WebStudio/web.connections.config
export ONLYOFFICE_APP_CONFIG_FILE="${SNAP_DATA}/onlyoffice/config/WebStudio/Web.config";
MYSQL="mysql -h$DB_HOST -u$DB_USER -p$DB_PWD -S$MYSQL_SOCKET";
DB_TABLES_COUNT=$($MYSQL --silent --skip-column-names -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='${DB_NAME}'");
if [ "${DB_TABLES_COUNT}" -eq "0" ]; then
$MYSQL "$DB_NAME" < $SNAP/var/www/onlyoffice/Sql/onlyoffice.sql
$MYSQL "$DB_NAME" < $SNAP/var/www/onlyoffice/Sql/onlyoffice.data.sql
$MYSQL "$DB_NAME" < $SNAP/var/www/onlyoffice/Sql/onlyoffice.resources.sql
fi
for i in $(ls $SNAP/var/www/onlyoffice/Sql/onlyoffice.upgrade*); do
$MYSQL "$DB_NAME" < ${i};
done
# export mono variables
export MONO_IOMAP=all
export MONO_ASPNET_WEBCONFIG_CACHESIZE=2000
export MONO_THREADS_PER_CPU=2000
export MONO_OPTIONS="--server"
export MONO_GC_PARAMS=nursery-size=64m
PKG_DIR=$SNAP/usr
export MONO_PATH=$PKG_DIR/lib/mono/4.5
export MONO_CONFIG=$SNAP/etc/mono/config
export MONO_CFG_DIR=$SNAP/etc
export C_INCLUDE_PATH=${PKG_DIR}/include
export MONO_REGISTRY_PATH=~/.mono/registry
export MONO_GAC_PREFIX=$PKG_DIR/lib/mono/gac/
#export LD_LIBRARY_PATH=$PKG_DIR/lib:$LD_LIBRARY_PATH
export LD_RUN_PATH=$LD_LIBRARY_PATH
#export LD_DEBUG=files
export PKG_CONFIG_PATH=$PKG_DIR/lib/pkgconfig:$PKG_CONFIG_PATH
export ACLOCAL_PATH=${PKG_DIR}/share/aclocal
#export MONO_LOG_LEVEL=debug
#export FONTCONFIG_PATH=${PKG_DIR}/etc/fonts
#export XDG_DATA_HOME=${PKG_DIR}/etc/fonts
exec ${SNAP}/usr/bin/mono ${SNAP}/usr/lib/hyperfastcgi/4.0/HyperFastCgi.exe /config=${SNAP_DATA}/hyperfastcgi/onlyoffice /logfile=${SNAP_DATA}/onlyoffice/logs/onlyoffice.log

View File

@ -0,0 +1,24 @@
<configuration>
<server type="HyperFastCgi.ApplicationServers.SimpleApplicationServer">
<root-dir>${SNAP}/var/www/onlyoffice/WebStudio</root-dir>
<threads min-worker="40" max-worker="0" min-io="4" max-io="0" />
</server>
<listener type="HyperFastCgi.Listeners.NativeListener">
<apphost-transport type="HyperFastCgi.Transports.NativeTransport">
<multithreading>Task</multithreading>
</apphost-transport>
<protocol>Unix</protocol>
<address>//666@${ONLYOFFICE_SOCKET}</address>
</listener>
<apphost type="HyperFastCgi.AppHosts.AspNet.AspNetApplicationHost">
<log level="Error" write-to-console="false" />
<add-trailing-slash>false</add-trailing-slash>
</apphost>
<web-applications>
<web-application>
<name>onlyoffice</name>
<vpath>/</vpath>
<path>.</path>
</web-application>
</web-applications>
</configuration>

View File

@ -0,0 +1,24 @@
<configuration>
<server type="HyperFastCgi.ApplicationServers.SimpleApplicationServer">
<root-dir>${SNAP}/var/www/onlyoffice/ApiSystem</root-dir>
<threads min-worker="40" max-worker="0" min-io="4" max-io="0" />
</server>
<listener type="HyperFastCgi.Listeners.NativeListener">
<apphost-transport type="HyperFastCgi.Transports.NativeTransport">
<multithreading>Task</multithreading>
</apphost-transport>
<protocol>Unix</protocol>
<address>//666@${ONLYOFFICE_API_SYSTEM_SOCKET}</address>
</listener>
<apphost type="HyperFastCgi.AppHosts.AspNet.AspNetApplicationHost">
<log level="Error" write-to-console="false" />
<add-trailing-slash>false</add-trailing-slash>
</apphost>
<web-applications>
<web-application>
<name>onlyofficeApiSystem</name>
<vpath>/</vpath>
<path>.</path>
</web-application>
</web-applications>
</configuration>

View File

@ -0,0 +1,36 @@
#!/bin/bash
export ONLYOFFICE_SOCKET="/tmp/sockets/onlyoffice.socket"
export ONLYOFFICE_API_SYSTEM_SOCKET="/tmp/sockets/onlyofficeApiSystem.socket"
export ONLYOFFICE_CORE_MACHINEKEY_FILE="$SNAP_DATA/onlyoffice/.onlyoffice_core_machine_key"
mkdir -p "$(dirname "$ONLYOFFICE_SOCKET")"
chmod 750 "$(dirname "$ONLYOFFICE_SOCKET")"
mkdir -p "$(dirname "$ONLYOFFICE_API_SYSTEM_SOCKET")"
chmod 750 "$(dirname "$ONLYOFFICE_API_SYSTEM_SOCKET")"
mkdir -p "$(dirname "$ONLYOFFICE_CORE_MACHINEKEY_FILE")"
chmod 750 "$(dirname "$ONLYOFFICE_CORE_MACHINEKEY_FILE")"
mkdir -p $SNAP_DATA/hyperfastcgi
chmod 750 $SNAP_DATA/hyperfastcgi
mkdir -p $SNAP_DATA/onlyoffice/logs
chmod 750 $SNAP_DATA/onlyoffice/logs
mkdir -p $SNAP_DATA/onlyoffice/data
chmod 750 $SNAP_DATA/onlyoffice/data
mkdir -p $SNAP_DATA/onlyoffice/config
chmod 750 $SNAP_DATA/onlyoffice/config
onlyoffice_get_core_machine_key() {
if [ ! -f "$ONLYOFFICE_CORE_MACHINEKEY_FILE" ]; then
echo "$(tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c64)" > ${ONLYOFFICE_CORE_MACHINEKEY_FILE};
chmod 600 ${ONLYOFFICE_CORE_MACHINEKEY_FILE};
fi
cat "$ONLYOFFICE_CORE_MACHINEKEY_FILE";
}

View File

@ -0,0 +1,92 @@
From bb6c86ca997b2ca1b052cb83e91152220fe149ad Mon Sep 17 00:00:00 2001
From: Kyle Fazzari <oracle@status.e4ward.com>
Date: Fri, 25 Mar 2016 15:03:38 +0000
Subject: [PATCH] Support compile-time disabling of setpriority().
This is to support running on systems such as Snappy Ubuntu Core,
e.g. heavily confined using seccomp filters. In such a situation,
without this commit, MySQL is aborted as soon as it tries to call
setpriority(). With this commit, MySQL can be built without
setpriority() by using -DWITH_INNODB_PAGE_CLEANER_PRIORITY=OFF,
thus supporting such systems.
Signed-off-by: Kyle Fazzari <oracle@status.e4ward.com>
---
storage/innobase/buf/buf0flu.cc | 12 ++++++------
storage/innobase/innodb.cmake | 5 +++++
2 files changed, 11 insertions(+), 6 deletions(-)
diff --git a/storage/innobase/buf/buf0flu.cc b/storage/innobase/buf/buf0flu.cc
index 5a8a3567e0f..0961f757b1a 100644
--- a/storage/innobase/buf/buf0flu.cc
+++ b/storage/innobase/buf/buf0flu.cc
@@ -2952,7 +2952,7 @@ pc_wait_finished(
return(all_succeeded);
}
-#ifdef UNIV_LINUX
+#if defined(UNIV_LINUX) && defined(SET_PAGE_CLEANER_PRIORITY)
/**
Set priority for page_cleaner threads.
@param[in] priority priority intended to set
@@ -2967,7 +2967,7 @@ buf_flush_page_cleaner_set_priority(
return(getpriority(PRIO_PROCESS, (pid_t)syscall(SYS_gettid))
== priority);
}
-#endif /* UNIV_LINUX */
+#endif /* UNIV_LINUX && SET_PAGE_CLEANER_PRIORITY */
#ifdef UNIV_DEBUG
/** Loop used to disable page cleaner threads. */
@@ -3113,7 +3113,7 @@ DECLARE_THREAD(buf_flush_page_cleaner_coordinator)(
<< os_thread_pf(os_thread_get_curr_id());
#endif /* UNIV_DEBUG_THREAD_CREATION */
-#ifdef UNIV_LINUX
+#if defined(UNIV_LINUX) && defined(SET_PAGE_CLEANER_PRIORITY)
/* linux might be able to set different setting for each thread.
worth to try to set high priority for page cleaner threads */
if (buf_flush_page_cleaner_set_priority(
@@ -3126,7 +3126,7 @@ DECLARE_THREAD(buf_flush_page_cleaner_coordinator)(
" page cleaner thread priority can be changed."
" See the man page of setpriority().";
}
-#endif /* UNIV_LINUX */
+#endif /* UNIV_LINUX && SET_PAGE_CLEANER_PRIORITY */
buf_page_cleaner_is_active = true;
@@ -3481,7 +3481,7 @@ DECLARE_THREAD(buf_flush_page_cleaner_worker)(
page_cleaner->n_workers++;
mutex_exit(&page_cleaner->mutex);
-#ifdef UNIV_LINUX
+#if defined(UNIV_LINUX) && defined(SET_PAGE_CLEANER_PRIORITY)
/* linux might be able to set different setting for each thread
worth to try to set high priority for page cleaner threads */
if (buf_flush_page_cleaner_set_priority(
@@ -3490,7 +3490,7 @@ DECLARE_THREAD(buf_flush_page_cleaner_worker)(
ib::info() << "page_cleaner worker priority: "
<< buf_flush_page_cleaner_priority;
}
-#endif /* UNIV_LINUX */
+#endif /* UNIV_LINUX && SET_PAGE_CLEANER_PRIORITY */
while (true) {
os_event_wait(page_cleaner->is_requested);
diff --git a/storage/innobase/innodb.cmake b/storage/innobase/innodb.cmake
index a90fe67f492..0d0a3ad7e3b 100644
--- a/storage/innobase/innodb.cmake
+++ b/storage/innobase/innodb.cmake
@@ -38,6 +38,11 @@ IF(UNIX)
LINK_LIBRARIES(aio)
ENDIF()
+ OPTION(WITH_INNODB_PAGE_CLEANER_PRIORITY "Set a high priority for page cleaner threads" ON)
+ IF(WITH_INNODB_PAGE_CLEANER_PRIORITY)
+ ADD_DEFINITIONS("-DSET_PAGE_CLEANER_PRIORITY")
+ ENDIF()
+
ELSEIF(CMAKE_SYSTEM_NAME STREQUAL "SunOS")
ADD_DEFINITIONS("-DUNIV_SOLARIS")
ENDIF()

View File

@ -0,0 +1,12 @@
#!/bin/sh
# shellcheck source=src/redis/utilities/redis-utilities
. "$SNAP/utilities/redis-utilities"
mkdir -p "${SNAP_DATA}/redis"
chmod 750 "${SNAP_DATA}/redis"
# redis doesn't support environment variables in its config files. Thankfully
# it supports reading the config file from stdin though, so we'll rewrite the
# config file on the fly and pipe it in.
sed -e "s|\${SNAP_DATA}|$SNAP_DATA|;s|\${REDIS_PIDFILE}|$REDIS_PIDFILE|;s|\${REDIS_SOCKET}|$REDIS_SOCKET|" "$SNAP/config/redis/redis.conf" | redis-server -

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,35 @@
#!/bin/sh
export REDIS_PIDFILE="/tmp/pids/redis.pid"
export REDIS_SOCKET="/tmp/sockets/redis.sock"
mkdir -p "$(dirname "$REDIS_PIDFILE")"
mkdir -p "$(dirname "$REDIS_SOCKET")"
chmod 750 "$(dirname "$REDIS_PIDFILE")"
chmod 750 "$(dirname "$REDIS_SOCKET")"
redis_is_running()
{
[ -f "$REDIS_PIDFILE" ] && [ -S "$REDIS_SOCKET" ]
}
wait_for_redis()
{
if ! redis_is_running; then
printf "Waiting for redis... "
while ! redis_is_running; do
sleep 1
done
printf "done\n"
fi
}
redis_pid()
{
if redis_is_running; then
cat "$REDIS_PIDFILE"
else
echo "Unable to get redis PID as it's not yet running" >&2
echo ""
fi
}

View File

@ -1,6 +1,6 @@
{
"name": "asc-web-components",
"version": "1.0.36",
"version": "1.0.38",
"description": "Ascensio System SIA component library",
"license": "AGPL-3.0",
"main": "dist/asc-web-components.cjs.js",
@ -33,6 +33,7 @@
"prop-types": "^15.7.2",
"rc-tree": "^2.1.2",
"react-autosize-textarea": "^7.0.0",
"react-avatar-edit": "^0.8.3",
"react-custom-scrollbars": "^4.2.1",
"react-datepicker": "^2.8.0",
"react-lifecycles-compat": "^3.0.4",

View File

@ -0,0 +1,218 @@
import React, { memo } from 'react'
import styled, { css } from 'styled-components'
import PropTypes from 'prop-types'
import ModalDialog from '../modal-dialog'
import Button from '../button'
import { Text } from '../text'
import Avatar from 'react-avatar-edit'
import { default as ASCAvatar } from '../avatar/index'
const StyledASCAvatar = styled(ASCAvatar)`
display: inline-block;
vertical-align: bottom;
`;
const StyledAvatarContainer = styled.div`
text-align: center;
div:first-child {
margin: 0 auto;
}
`;
class AvatarEditorBody extends React.Component {
constructor(props) {
super(props);
this.state = {
croppedImage: null,
src: this.props.image,
hasMaxSizeError: false
}
this.onCrop = this.onCrop.bind(this)
this.onClose = this.onClose.bind(this)
this.onBeforeFileLoad = this.onBeforeFileLoad.bind(this)
this.onFileLoad = this.onFileLoad.bind(this)
}
onClose() {
this.props.onCloseEditor();
this.setState({ croppedImage: null })
}
onCrop(croppedImage) {
this.props.onCropImage(croppedImage);
this.setState({ croppedImage })
}
onBeforeFileLoad(elem) {
if (elem.target.files[0].size > this.props.maxSize * 1000000) {
this.setState({
hasMaxSizeError: true
});
elem.target.value = "";
}else if(this.state.hasMaxSizeError){
this.setState({
hasMaxSizeError: false
});
};
}
onFileLoad(file){
let reader = new FileReader();
let _this = this;
reader.onloadend = () => {
_this.props.onFileLoad(reader.result);
};
reader.readAsDataURL(file)
}
render() {
return (
<StyledAvatarContainer>
<Avatar
width={400}
height={295}
imageWidth={400}
cropRadius={50}
onCrop={this.onCrop}
onClose={this.onClose}
onBeforeFileLoad={this.onBeforeFileLoad}
onFileLoad={this.onFileLoad}
label={this.props.label}
src={this.state.src}
/>
{this.state.croppedImage && (
<div>
<StyledASCAvatar
size='max'
role='user'
source={this.state.croppedImage}
editing={false}
/>
<StyledASCAvatar
size='big'
role='user'
source={this.state.croppedImage}
editing={false}
/>
</div>
)
}
{
this.state.hasMaxSizeError &&
<Text.Body as='span' color="#ED7309" isBold={true}>
{this.props.maxSizeErrorLabel}
</Text.Body>
}
</StyledAvatarContainer>
);
}
}
class AvatarEditor extends React.Component {
constructor(props) {
super(props);
this.state = {
defaultImage: null,
croppedImage: null,
visible: props.value
};
this.onClose = this.onClose.bind(this);
this.onCropImage = this.onCropImage.bind(this);
this.onCloseEditor = this.onCloseEditor.bind(this);
this.onFileLoad = this.onFileLoad.bind(this);
this.onSaveButtonClick = this.onSaveButtonClick.bind(this);
}
onFileLoad(file){
this.setState({ defaultImage: file });
}
onSaveButtonClick() {
this.props.onSave({
defaultImage: this.state.defaultImage,
croppedImage: this.state.croppedImage
});
this.setState({ visible: false });
}
onCloseEditor() {
this.setState({
croppedImage: null
});
}
onCropImage(result) {
this.setState({
croppedImage: result
});
}
onClose() {
this.setState({ visible: false });
this.props.onClose();
}
componentDidUpdate(prevProps) {
if (this.props.visible !== prevProps.visible) {
this.setState({ visible: this.props.visible });
}
}
render() {
return (
<ModalDialog
visible={this.state.visible}
headerContent={this.props.headerLabel}
bodyContent={
<AvatarEditorBody
maxSize={this.props.maxSize}
image={this.props.image}
onCropImage={this.onCropImage}
onCloseEditor={this.onCloseEditor}
label={this.props.chooseFileLabel}
maxSizeErrorLabel={this.props.maxSizeErrorLabel}
onFileLoad={this.onFileLoad}
/>
}
footerContent={[
<Button
key="SaveBtn"
label={this.props.saveButtonLabel}
primary={true}
onClick={this.onSaveButtonClick}
/>,
<Button
key="CancelBtn"
label={this.props.cancelButtonLabel}
onClick={this.onClose}
style={{ marginLeft: "8px" }}
/>
]}
onClose={this.props.onClose}
/>
);
}
}
AvatarEditor.propTypes = {
visible: PropTypes.bool,
headerLabel: PropTypes.string,
chooseFileLabel: PropTypes.string,
saveButtonLabel: PropTypes.string,
maxSizeErrorLabel: PropTypes.string,
image: PropTypes.string,
cancelButtonLabel: PropTypes.string,
maxSize: PropTypes.number,
onSave: PropTypes.func,
onClose: PropTypes.func
};
AvatarEditor.defaultProps = {
visible: false,
maxSize: 1, //1MB
chooseFileLabel: 'Choose a file',
headerLabel: 'Edit Photo',
saveButtonLabel: 'Save',
cancelButtonLabel: 'Cancel',
maxSizeErrorLabel: 'File is too big'
};
export default AvatarEditor;

View File

@ -56,7 +56,7 @@ const RoleWrapper = styled.div`
`;
const ImageStyled = styled.img`
max-width: 100%;
width: 100%;
height: auto;
border-radius: 50%;

View File

@ -0,0 +1,373 @@
import React from 'react'
import styled from 'styled-components'
import PropTypes from 'prop-types'
import { tablet } from '../../utils/device';
import InputBlock from '../input-block'
import { Icons } from '../icons'
import Link from '../link'
import { Text } from '../text'
import DropDown from '../drop-down'
const StyledInput = styled.div`
display: flex;
align-items: center;
line-height: 32px;
flex-direction: row;
flex-wrap: nowrap;
@media ${tablet} {
flex-wrap: wrap;
}
`;
const PasswordProgress = styled.div`
${props => props.inputWidth ? `width: ${props.inputWidth};` : `flex: auto;`}
`;
const NewPasswordButton = styled.div`
margin-left: 16px;
margin-top: -6px;
`;
const CopyLink = styled.div`
margin-top: -6px;
margin-left: 16px;
@media ${tablet} {
width: 100%;
margin-left: 0px;
margin-top: 8px;
}
`;
const Progress = styled.div`
border: 3px solid ${props => (!props.isDisabled && props.progressColor) ? props.progressColor : 'transparent'};
border-radius: 2px;
margin-top: -4px;
width: ${props => props.progressWidth ? props.progressWidth + '%' : '0%'};
`;
const StyledTooltipContainer = styled(Text.Body)`
margin: 8px 16px 16px 16px;
`;
const StyledTooltipItem = styled(Text.Body)`
margin-left: 8px;
height: 24px;
color: ${props => props.valid ? '#44bb00' : '#B40404'};
`;
class PasswordInput extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
type: props.inputType,
progressColor: 'transparent',
progressWidth: 0,
inputValue: '',
displayTooltip: false,
validLength: false,
validDigits: false,
validCapital: false,
validSpecial: false
}
}
onFocus = () => {
this.setState({
displayTooltip: true
});
}
onBlur = () => {
this.setState({
displayTooltip: false
});
}
changeInputType = () => {
const newType = this.state.type === 'text' ? 'password' : 'text';
this.setState({
type: newType
});
}
testStrength = value => {
const { generatorSpecial, passwordSettings } = this.props;
const specSymbols = new RegExp('[' + generatorSpecial + ']');
let capital;
let digits;
let special;
passwordSettings.upperCase
? capital = /[A-Z]/.test(value)
: capital = true;
passwordSettings.digits
? digits = /\d/.test(value)
: digits = true;
passwordSettings.specSymbols
? special = specSymbols.test(value)
: special = true;
return {
digits: digits,
capital: capital,
special: special,
length: value.length >= passwordSettings.minLength
};
}
checkPassword = (value) => {
const greenColor = '#44bb00';
const redColor = '#B40404';
const passwordValidation = this.testStrength(value);
const progressScore = passwordValidation.digits
&& passwordValidation.capital
&& passwordValidation.special
&& passwordValidation.length;
const progressWidth = value.length * 100 / this.props.passwordSettings.minLength;
const progressColor = progressScore
? greenColor
: (value.length === 0)
? 'transparent'
: redColor;
this.setState({
progressColor: progressColor,
progressWidth: progressWidth > 100 ? 100 : progressWidth,
inputValue: value,
validLength: passwordValidation.length,
validDigits: passwordValidation.digits,
validCapital: passwordValidation.capital,
validSpecial: passwordValidation.special
});
}
onChangeAction = (e) => {
this.props.onChange && this.props.onChange(e);
this.checkPassword(e.target.value);
}
onGeneratePassword = (e) => {
if (this.props.isDisabled)
return e.preventDefault();
const newPassword = this.getNewPassword();
this.checkPassword(newPassword);
}
getNewPassword = () => {
const { passwordSettings, generatorSpecial } = this.props;
const length = passwordSettings.minLength;
const string = 'abcdefghijklmnopqrstuvwxyz';
const numeric = '0123456789';
const special = generatorSpecial;
let password = '';
let character = '';
while (password.length < length) {
const a = Math.ceil(string.length * Math.random() * Math.random());
const b = Math.ceil(numeric.length * Math.random() * Math.random());
const c = Math.ceil(special.length * Math.random() * Math.random());
let hold = string.charAt(a);
if (passwordSettings.upperCase) {
hold = (password.length % 2 == 0)
? (hold.toUpperCase())
: (hold);
}
character += hold;
if (passwordSettings.digits) {
character += numeric.charAt(b);
}
if (passwordSettings.specSymbols) {
character += special.charAt(c);
}
password = character;
}
password = password
.split('')
.sort(() => 0.5 - Math.random())
.join('');
return password.substr(0, length);
}
copyToClipboard = emailInputName => {
const { clipEmailResource, clipPasswordResource, isDisabled } = this.props;
if (isDisabled)
return event.preventDefault();
const textField = document.createElement('textarea');
const emailValue = document.getElementsByName(emailInputName)[0].value;
textField.innerText = clipEmailResource + emailValue + ' | ' + clipPasswordResource + this.state.inputValue;
document.body.appendChild(textField);
textField.select();
document.execCommand('copy');
textField.remove();
}
render() {
const {
inputName,
isDisabled,
scale,
size,
clipActionResource,
tooltipPasswordTitle,
tooltipPasswordLength,
tooltipPasswordDigits,
tooltipPasswordCapital,
tooltipPasswordSpecial,
emailInputName,
inputWidth,
passwordSettings
} = this.props;
const {
type,
progressColor,
progressWidth,
inputValue,
validLength,
validDigits,
validCapital,
validSpecial,
displayTooltip
} = this.state;
const iconsColor = isDisabled ? '#D0D5DA' : '#A3A9AE';
const tooltipContent = (
<StyledTooltipContainer forwardedAs='div' title={tooltipPasswordTitle}>
{tooltipPasswordTitle}
<StyledTooltipItem forwardedAs='div' title={tooltipPasswordLength} valid={validLength} >
{tooltipPasswordLength}
</StyledTooltipItem>
{passwordSettings.digits &&
<StyledTooltipItem forwardedAs='div' title={tooltipPasswordDigits} valid={validDigits} >
{tooltipPasswordDigits}
</StyledTooltipItem>
}
{passwordSettings.upperCase &&
<StyledTooltipItem forwardedAs='div' title={tooltipPasswordCapital} valid={validCapital} >
{tooltipPasswordCapital}
</StyledTooltipItem>
}
{passwordSettings.specSymbols &&
<StyledTooltipItem forwardedAs='div' title={tooltipPasswordSpecial} valid={validSpecial} >
{tooltipPasswordSpecial}
</StyledTooltipItem>
}
</StyledTooltipContainer>
);
return (
<StyledInput>
<PasswordProgress inputWidth={inputWidth}>
<InputBlock
name={inputName}
hasError={false}
isDisabled={isDisabled}
iconName='EyeIcon'
value={inputValue}
onIconClick={this.changeInputType}
onChange={this.onChangeAction}
scale={scale}
size={size}
type={type}
iconColor={iconsColor}
isIconFill={true}
onFocus={this.onFocus}
onBlur={this.onBlur}
>
{displayTooltip &&
<DropDown directionY='top' manualY='150%' isOpen={true}>
{tooltipContent}
</DropDown>
}
</InputBlock>
<Progress progressColor={progressColor} progressWidth={progressWidth} isDisabled={isDisabled} />
</PasswordProgress>
<NewPasswordButton>
<Icons.RefreshIcon
size="medium"
color={iconsColor}
isfill={true}
onClick={this.onGeneratePassword}
/>
</NewPasswordButton>
<CopyLink>
<Link
type="action"
isHovered={true}
fontSize={13}
color={iconsColor}
onClick={this.copyToClipboard.bind(this, emailInputName)}
>
{clipActionResource}
</Link>
</CopyLink>
</StyledInput>
);
};
};
PasswordInput.propTypes = {
inputType: PropTypes.oneOf(['text', 'password']),
inputName: PropTypes.string,
emailInputName: PropTypes.string.isRequired,
inputValue: PropTypes.string,
onChange: PropTypes.func,
isDisabled: PropTypes.bool,
size: PropTypes.oneOf(['base', 'middle', 'big', 'huge']),
scale: PropTypes.bool,
clipActionResource: PropTypes.string,
clipEmailResource: PropTypes.string,
clipPasswordResource: PropTypes.string,
tooltipPasswordTitle: PropTypes.string,
tooltipPasswordLength: PropTypes.string,
tooltipPasswordDigits: PropTypes.string,
tooltipPasswordCapital: PropTypes.string,
tooltipPasswordSpecial: PropTypes.string,
generatorSpecial: PropTypes.string,
passwordSettings: PropTypes.object.isRequired
}
PasswordInput.defaultProps = {
inputType: 'password',
inputName: 'passwordInput',
size: 'base',
scale: true,
clipEmailResource: 'E-mail',
clipPasswordResource: 'Password',
generatorSpecial: '!@#$%^&*'
}
export default PasswordInput;

View File

@ -11,6 +11,7 @@ export { default as GroupButtonsMenu } from './components/group-buttons-menu'
export { default as TreeMenu } from './components/tree-menu'
export { default as TreeNode } from './components/tree-menu-node'
export { default as Avatar } from './components/avatar'
export { default as AvatarEditor } from './components/avatar-editor'
export { default as RequestLoader } from './components/request-loader'
export { default as MainButton } from './components/main-button'
export { default as ContextMenuButton } from './components/context-menu-button'
@ -54,3 +55,4 @@ export { default as RowContainer } from './components/row-container'
export { default as FieldContainer } from './components/field-container'
export { default as utils } from './utils'
export { default as DatePicker } from './components/calendar-new/date-input'
export { default as PasswordInput } from './components/password-input'

View File

@ -4976,6 +4976,11 @@ kind-of@^6.0.0, kind-of@^6.0.2:
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051"
integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==
konva@2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/konva/-/konva-2.5.1.tgz#cca611a9522e831e54cf57c508a1aed3f0ceac25"
integrity sha512-YdHEWqmbWPieqIZuLx7JFGm9Ui08hSUaSJ2k2Ml8o5giFgJ0WmxAS0DPXIM+Ty2ADRagOHZfXSJ/skwYqqlwgQ==
lazy-cache@^0.2.3:
version "0.2.7"
resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-0.2.7.tgz#7feddf2dcb6edb77d11ef1d117ab5ffdf0ab1b65"
@ -6777,6 +6782,13 @@ react-autosize-textarea@^7.0.0:
line-height "^0.3.1"
prop-types "^15.5.6"
react-avatar-edit@^0.8.3:
version "0.8.3"
resolved "https://registry.yarnpkg.com/react-avatar-edit/-/react-avatar-edit-0.8.3.tgz#0ebf21391328fc255429bdfbc782f795827109bf"
integrity sha512-QEedh6DjDCSI7AUsUHHtfhxApCWC5hJAoywxUA5PtUdw03iIjEurgVqPOIt1UBHhU/Zk/9amElRF3oepN9JZSg==
dependencies:
konva "2.5.1"
react-custom-scrollbars@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/react-custom-scrollbars/-/react-custom-scrollbars-4.2.1.tgz#830fd9502927e97e8a78c2086813899b2a8b66db"

View File

@ -0,0 +1,34 @@
# Avatar Editor
## Usage
```js
import { AvatarEditor } from 'asc-web-components';
```
#### Description
Required to display user avatar editor on page.
#### Usage
```js
<AvatarEditor
visible={true}
onSave={(data) =>{console.log(data.croppedImage, data.defaultImage)}}
/>
```
#### Properties
| Props | Type | Required | Values | Default | Description |
| ------------------ | -------- | :------: | ----------------------------------------- | ------------------ | ----------------------------------------------------- |
| `visible` | `bool` | - | | `false` | Display avatar editor or not |
| `chooseFileLabel` | `string` | - | | `Choose a file` | |
| `headerLabel` | `string` | - | | `Edit Photo` | |
| `saveButtonLabel` | `string` | - | | `Save` | |
| `cancelButtonLabel` | `string` | - | | `Cancel` | |
| `maxSizeErrorLabel` | `string` | - | | `File is too big` | |
| `maxSize` | `number` | - | | `1` | Max size of image |
| `onSave` | `function` | - | | | |
| `onClose` | `function` | - | | | |

View File

@ -0,0 +1,72 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { withKnobs, boolean, text, select } from '@storybook/addon-knobs/react';
import withReadme from 'storybook-readme/with-readme';
import Readme from './README.md';
import { AvatarEditor, Avatar } from 'asc-web-components';
import Section from '../../.storybook/decorators/section';
class AvatarEditorStory extends React.Component {
constructor(props) {
super(props);
this.state = {
isOpen: false,
userImage: null
}
this.openEditor = this.openEditor.bind(this);
this.onClose = this.onClose.bind(this);
this.onSave = this.onSave.bind(this);
}
onSave(result){
action('onSave')(result);
this.setState({
userImage: result.croppedImage,
isOpen: false
})
}
openEditor(){
this.setState({
isOpen: true
})
}
onClose(){
action('onClose');
this.setState({
isOpen: false
})
}
render(){
return(
<div>
<Avatar
size='max'
role='user'
source={this.state.userImage }
editing={true}
editAction={this.openEditor}
/>
<AvatarEditor
visible={this.state.isOpen}
onClose={this.onClose}
onSave={this.onSave}
/>
</div>
)
}
}
storiesOf('Components|AvatarEditor', module)
.addDecorator(withKnobs)
.addDecorator(withReadme(Readme))
.add('avatar editor', () => {
return (
<Section>
<AvatarEditorStory />
</Section>
);
});

View File

@ -0,0 +1,78 @@
# PasswordInput
#### Description
Password entry field with advanced capabilities for displaying, validation of correspondence and generation based on settings.
Object with settings:
```js
{
minLength: 6,
upperCase: false,
digits: false,
specSymbols: false
}
```
Check for compliance with settings is carried out on fly. As you type in required number of characters, progress bar will fill up and when all conditions are met, the color will change from red to green.
Depending on screen width of device, input will change location of elements.
When setting focus to input, tooltip will be shown with progress in fulfilling conditions specified in settings. When unfocused, tooltip disappears.
#### Usage
```js
import { PasswordInput } from "asc-web-components";
const settings = {
minLength: 6,
upperCase: false,
digits: false,
specSymbols: false
};
<PasswordInput
inputName="demoPasswordInput"
emailInputName="demoEmailInput"
inputValue={value}
onChange={e => {
set(e.target.value);
}}
clipActionResource="Copy e-mail and password"
clipEmailResource="E-mail: "
clipPasswordResource="Password: "
tooltipPasswordTitle="Password must contain:"
tooltipPasswordLength="from 6 to 30 characters"
tooltipPasswordDigits="digits"
tooltipPasswordCapital="capital letters"
tooltipPasswordSpecial="special characters (!@#$%^&*)"
generatorSpecial="!@#$%^&*"
passwordSettings={settings}
isDisabled={false}
/>;
```
#### Properties
| Props | Type | Required | Values | Default | Description |
| ------------------- | --------- | :------: | ------------------ | --------- | --------------------------------------------------------- |
| `inputType` | `array` | - | `text`, `password` | `password`| It is necessary for correct display of values inside input|
| `inputName` | `string` | - | - | `passwordInput`| Input name |
| `emailInputName` | `string` | ✅ | - | - | Required to associate password field with email field |
| `inputValue` | `string` | - | - | - | Input value |
| `onChange` | `func` | - | - | - | Will be triggered whenever an PasswordInput typing |
| `clipActionResource`| `string` | - | - | - | Translation of text for copying email data and password |
| `clipEmailResource` | `string` | - | - | `E-mail` | Text translation email to copy |
| `clipPasswordResource`| `string` | - | - | `Password`| Text translation password to copy |
| `tooltipPasswordTitle`| `string` | - | - | - | Text translation tooltip |
| `tooltipPasswordLength`| `string` | - | - | - | Password text translation is long tooltip |
| `tooltipPasswordDigits`| `string` | - | - | - | Digit text translation tooltip |
| `tooltipPasswordCapital`| `string` |- | - | - | Capital text translation tooltip |
| `tooltipPasswordSpecial`| `string` |- | - | - | Special text translation tooltip |
| `generatorSpecial` | `string` | - | - | `!@#$%^&*`| Set of special characters for password generator and validator|
| `passwordSettings` | `object` | ✅ | - | - | Set of settings for password generator and validator |
| `isDisabled` | `bool` | - | - | `false` | Set input disabled |
| `inputWidth` | `string` | - | - | - | If you need to set input width manually |

View File

@ -0,0 +1,66 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { StringValue } from 'react-values';
import { withKnobs, boolean, text, select, number } from '@storybook/addon-knobs/react';
import withReadme from 'storybook-readme/with-readme';
import Readme from './README.md';
import { PasswordInput, TextInput } from 'asc-web-components';
import Section from '../../../.storybook/decorators/section';
storiesOf('Components|Input', module)
.addDecorator(withKnobs)
.addDecorator(withReadme(Readme))
.add('advanced password', () => {
const isDisabled = boolean('isDisabled', false);
const settingsUpperCase = boolean('settingsUpperCase', false);
const settingsDigits = boolean('settingsDigits', false);
const settingsSpecSymbols = boolean('settingsSpecSymbols', false);
const fakeSettings = {
minLength: 6,
upperCase: settingsUpperCase,
digits: settingsDigits,
specSymbols: settingsSpecSymbols
};
const tooltipPasswordLength = 'from ' + fakeSettings.minLength + ' to 30 characters';
return (
<Section>
<div style={{height: '110px'}}></div>
<TextInput
name='demoEmailInput'
size='base'
isDisabled={isDisabled}
isReadOnly={true}
scale={true}
value='demo@gmail.com'
/>
<br />
<StringValue>
{({ value, set }) => (
<PasswordInput
inputName='demoPasswordInput'
emailInputName='demoEmailInput'
inputValue={value}
onChange={e => {
set(e.target.value);
}}
clipActionResource='Copy e-mail and password'
clipEmailResource='E-mail: '
clipPasswordResource='Password: '
tooltipPasswordTitle='Password must contain:'
tooltipPasswordLength={tooltipPasswordLength}
tooltipPasswordDigits='digits'
tooltipPasswordCapital='capital letters'
tooltipPasswordSpecial='special characters (!@#$%^&*)'
generatorSpecial='!@#$%^&*'
passwordSettings={fakeSettings}
isDisabled={isDisabled}
/>
)}
</StringValue>
</Section>
)
});