1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/webtrees_ynh.git synced 2024-09-03 18:26:37 +02:00

Added check_process,LICENSE,.fonctions and made the install,update script according to .fonctions requirements

This commit is contained in:
Anmol 2017-07-05 15:58:42 +05:30
parent 3d63405114
commit e34ff68e31
5824 changed files with 313 additions and 657856 deletions

10
LICENSE Normal file
View file

@ -0,0 +1,10 @@
License
webtrees: online genealogy
Copyright (C) 2017 webtrees development team
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

38
check_process Normal file
View file

@ -0,0 +1,38 @@
;; Nom du test
# Commentaire ignoré
; Manifest
domain="domain.tld" (DOMAIN)
path="/path" (PATH)
admin="john" (USER)
language="fr"
# is_public="Yes" (PUBLIC|public=Yes|private=No)
is_public=1 (PUBLIC|public=1|private=0)
password="password"
port="666" (PORT)
; Checks
pkg_linter=1
setup_sub_dir=1
setup_root=1
setup_nourl=0
setup_private=1
setup_public=1
upgrade=1
backup_restore=1
multi_instance=1
incorrect_path=1
port_already_use=1 (66)
change_url=0
;;; Levels
Level 1=auto
Level 2=auto
Level 3=auto
Level 4=0
Level 5=auto
Level 6=auto
Level 7=auto
Level 8=0
Level 9=0
Level 10=0
;;; Options
Email=
Notification=none

View file

@ -1,5 +1,5 @@
{ {
"name": "webtrees", "name": "Webtrees",
"id": "webtrees", "id": "webtrees",
"packaging_format": 1, "packaging_format": 1,
"description": { "description": {
@ -13,12 +13,13 @@
"requirements": { "requirements": {
"yunohost": ">> 2.4.0" "yunohost": ">> 2.4.0"
}, },
"multi_instance": false, "multi_instance": true,
"services": [ "services": [
"nginx", "nginx",
"php5-fpm", "php5-fpm",
"mysql" "mysql"
], ],
"license": "free",
"arguments": { "arguments": {
"install" : [ "install" : [
{ {

180
scripts/.fonctions Normal file
View file

@ -0,0 +1,180 @@
#!/bin/bash
ynh_version="2.4"
YNH_VERSION () { # Display number version of the YunoHost moulinette
ynh_version=$(sudo yunohost -v | grep "moulinette:" | cut -d' ' -f2 | cut -d'.' -f1,2)
}
CHECK_VAR () { # Check variable is not empty
# $1 = Checking variable
# $2 = Text to display on error
test -n "$1" || (echo "$2" >&2 && false)
}
EXIT_PROPERLY () { # Causes the script to stop in the event of an error. And clean the residue.
trap '' ERR
echo -e "\e[91m \e[1m" # Shell in light red bold
echo -e "!!\n $app install's script has encountered an error. Installation was cancelled.\n!!" >&2
if type -t CLEAN_SETUP > /dev/null; then # Checks the existence of the function before executing it.
CLEAN_SETUP # Call the specific cleanup function of the install script.
fi
sudo sed -i "\@\"$domain$path/\":@d" /etc/ssowat/conf.json
if [ "$ynh_version" = "2.2" ]; then
/bin/bash $script_dir/remove # Call the remove script. In 2.2, this behavior is not automatic.
fi
ynh_die
}
TRAP_ON () { # Activate signal capture
trap EXIT_PROPERLY ERR # Capturing exit signals on error
}
TRAP_OFF () { # Ignoring signal capture until TRAP_ON
trap '' ERR # Ignoring exit signals
}
CHECK_USER () { # Check the validity of the user admin
# $1 = User admin variable
ynh_user_exists "$1" || (echo "Wrong admin" >&2 && false)
}
CHECK_PATH () { # Checks / at the beginning of the path. And his absence at the end.
if [ "${path:0:1}" != "/" ]; then # If the first character is not /
path="/$path" # Add / at the beginning of path
fi
if [ "${path:${#path}-1}" == "/" ] && [ ${#path} -gt 1 ]; then # If the last character is a / and it is not the only character.
path="${path:0:${#path}-1}" # Delete last character
fi
}
CHECK_DOMAINPATH () { # Checks the availability of the path and domain.
sudo yunohost app checkurl $domain$path -a $app
}
CHECK_FINALPATH () { # Checks that the destination folder is not already in use.
final_path=/var/www/$app
if [ -e "$final_path" ]
then
echo "This path already contains a folder" >&2
false
fi
}
SETUP_SOURCE () { # Download source, decompress and copu into $final_path
src=$(cat ../sources/source_md5 | awk -F' ' {'print $2'})
sudo wget -nv -i ../sources/source_url -O $src
# Checks the checksum of the downloaded source.
# md5sum -c ../sources/source_md5 --status || ynh_die "Corrupt source"
# Decompress source
if [ "$(echo ${src##*.})" == "tgz" ]; then
tar -x -f $src
elif [ "$(echo ${src##*.})" == "zip" ]; then
unzip -q $src
else
false # Unsupported archive format.
fi
# Copy file source
sudo cp -a $(cat ../sources/source_dir)/. "$final_path"
# Copy additional file and modified
if test -e "../sources/ajouts"; then
sudo cp -a ../sources/ajouts/. "$final_path"
fi
}
POOL_FPM () { # Create the php-fpm pool configuration file and configure it.
sed -i "s@__NAMETOCHANGE__@$app@g" ../conf/php-fpm.conf
sed -i "s@__FINALPATH__@$final_path@g" ../conf/php-fpm.conf
finalphpconf=/etc/php5/fpm/pool.d/$app.conf
sudo cp ../conf/php-fpm.conf $finalphpconf
sudo chown root: $finalphpconf
finalphpini=/etc/php5/fpm/conf.d/20-$app.ini
sudo cp ../conf/php-fpm.ini $finalphpini
sudo chown root: $finalphpini
sudo systemctl reload php5-fpm
}
STORE_MD5_CONFIG () { # Saves the checksum of the config file
# $1 = Name of the conf file for storage in settings.yml
# $2 = Full name and path of the conf file.
ynh_app_setting_set $app $1_file_md5 $(sudo md5sum "$2" | cut -d' ' -f1)
}
CHECK_MD5_CONFIG () { # Created a backup of the config file if it was changed.
# $1 = Name of the conf file for storage in settings.yml
# $2 = Full name and path of the conf file.onf.
if [ "$(ynh_app_setting_get $app $1_file_md5)" != $(sudo md5sum "$2" | cut -d' ' -f1) ]; then
sudo cp -a "$2" "$2.backup.$(date '+%d.%m.%y_%Hh%M,%Ss')" # Si le fichier de config a été modifié, créer un backup.
fi
}
FIND_PORT () { # Search free port
# $1 = Port number to start the search.
port=$1
while ! sudo yunohost app checkport $port ; do
port=$((port+1))
done
CHECK_VAR "$port" "port empty"
}
### REMOVE SCRIPT
REMOVE_NGINX_CONF () { # Delete nginx configuration
if [ -e "/etc/nginx/conf.d/$domain.d/$app.conf" ]; then
echo "Delete nginx config"
sudo rm "/etc/nginx/conf.d/$domain.d/$app.conf"
sudo systemctl reload nginx
fi
}
REMOVE_FPM_CONF () { # Delete pool php-fpm configuration
if [ -e "/etc/php5/fpm/pool.d/$app.conf" ]; then # Delete fpm config
echo "Delete fpm config"
sudo rm "/etc/php5/fpm/pool.d/$app.conf"
fi
if [ -e "/etc/php5/fpm/conf.d/20-$app.ini" ]; then # Delete php config
echo "Delete php config"
sudo rm "/etc/php5/fpm/conf.d/20-$app.ini"
fi
sudo systemctl reload php5-fpm
}
REMOVE_LOGROTATE_CONF () { # Delete logrotate configuration
if [ -e "/etc/logrotate.d/$app" ]; then
echo "Delete logrotate config"
sudo rm "/etc/logrotate.d/$app"
fi
}
SECURE_REMOVE () { # Deleting a folder with variable verification
chaine="$1" # The argument must be given between simple quotes '', to avoid interpreting the variables.
no_var=0
while (echo "$chaine" | grep -q '\$') # Loop as long as there are $ in the string
do
no_var=1
global_var=$(echo "$chaine" | cut -d '$' -f 2) # Isole the first variable found.
only_var=\$$(expr "$global_var" : '\([A-Za-z0-9_]*\)') # Isole completely the variable by adding the $ at the beginning and keeping only the name of the variable. Mostly gets rid of / and a possible path behind.
real_var=$(eval "echo ${only_var}") # `eval "echo ${var}` Allows to interpret a variable contained in a variable.
if test -z "$real_var" || [ "$real_var" = "/" ]; then
echo "Variable $only_var is empty, suppression of $chaine cancelled." >&2
return 1
fi
chaine=$(echo "$chaine" | sed "s@$only_var@$real_var@") # Replaces variable with its value in the string.
done
if [ "$no_var" -eq 1 ]
then
if [ -e "$chaine" ]; then
echo "Delete directory $chaine"
sudo rm -r "$chaine"
fi
return 0
else
echo "No detected variable." >&2
return 1
fi
}

View file

@ -13,42 +13,54 @@ set -eu
# The app instance name is probably what you are interested the most, since this is # The app instance name is probably what you are interested the most, since this is
# guaranteed to be unique. This is a good unique identifier to define installation path, # guaranteed to be unique. This is a good unique identifier to define installation path,
# db names, ... # db names, ...
source .fonctions # Loads the generic functions usually used in the script
# Source YunoHost helpers
source /usr/share/yunohost/helpers
TRAP_ON # Active trap for strop script if detect error
app=$YNH_APP_INSTANCE_NAME app=$YNH_APP_INSTANCE_NAME
# Retrieve arguments # Retrieve arguments
domain=$YNH_APP_ARG_DOMAIN domain=$YNH_APP_ARG_DOMAIN
path_url=$YNH_APP_ARG_PATH path=$YNH_APP_ARG_PATH
is_public=$YNH_APP_ARG_IS_PUBLIC is_public=$YNH_APP_ARG_IS_PUBLIC
admin_username=$YNH_APP_ARG_USERNAME admin_username=$YNH_APP_ARG_USERNAME
admin_name=$YNH_APP_ARG_NAME admin_name=$YNH_APP_ARG_NAME
admin_email=$YNH_APP_ARG_EMAIL admin_email=$YNH_APP_ARG_EMAIL
admin_password=$(openssl passwd -1 -salt xyz $YNH_APP_ARG_PASSWORD) admin_password=$(openssl passwd -1 -salt xyz $YNH_APP_ARG_PASSWORD)
CHECK_VAR "$app" "app name not set"
CHECK_PATH
CHECK_DOMAINPATH
CHECK_FINALPATH
# Source YunoHost helpers
source /usr/share/yunohost/helpers
# Save app settings # Save app settings
ynh_app_setting_set "$app" is_public "$is_public" ynh_app_setting_set "$app" is_public "$is_public"
ynh_app_setting_set $app domain $domain
ynh_app_setting_set $app path $path
# Check domain/path availability
sudo yunohost app checkurl "${domain}${path_url}" -a "$app" \
|| ynh_die "Path not available: ${domain}${path_url}"
# Check password strength # Check password strength
[[ ${#admin_password} -gt 6 ]] || ynh_die \ [[ ${#admin_password} -gt 6 ]] || ynh_die \
"The password is too weak, it must be longer than 6 characters" "The password is too weak, it must be longer than 6 characters"
# Copy source files # Copy files to the right place
src_path=/var/www/$app sudo mkdir "$final_path"
sudo mkdir -p $src_path ynh_app_setting_set $app final_path $final_path
sudo cp -a ../sources/. $src_path # Get source
SETUP_SOURCE
# Set permissions to app files # Set permissions to app files
# you may need to make some file and/or directory writeable by www-data (nginx user) # you may need to make some file and/or directory writeable by www-data (nginx user)
sudo chown -R root: $src_path sudo chown -R root: $final_path
### MySQL (can be removed if not used) ### ### MySQL (can be removed if not used) ###
# If your app use a MySQL database you can use these lines to bootstrap # If your app use a MySQL database you can use these lines to bootstrap
@ -66,8 +78,8 @@ sed -i "s@__dbuser__@$dbuser@g" ../conf/config.ini.php
sed -i "s@__dbpass__@$dbpass@g" ../conf/config.ini.php sed -i "s@__dbpass__@$dbpass@g" ../conf/config.ini.php
sed -i "s@__dbname__@$dbname@g" ../conf/config.ini.php sed -i "s@__dbname__@$dbname@g" ../conf/config.ini.php
# Copy the config file to the src_path # Copy the config file to the final path
sudo cp ../conf/config.ini.php $src_path/data/. sudo cp ../conf/config.ini.php $final_path/data/.
@ -89,8 +101,8 @@ ynh_mysql_connect_as "$dbuser" "$dbpass" "$dbname" < "../conf/sql/admin.sql"
# Modify Nginx configuration file and copy it to Nginx conf directory # Modify Nginx configuration file and copy it to Nginx conf directory
nginx_conf=../conf/nginx.conf nginx_conf=../conf/nginx.conf
sed -i "s@YNH_WWW_PATH@$path_url@g" $nginx_conf sed -i "s@YNH_WWW_PATH@$path@g" $nginx_conf
sed -i "s@YNH_WWW_ALIAS@$src_path/@g" $nginx_conf sed -i "s@YNH_WWW_ALIAS@$final_path/@g" $nginx_conf
# If a dedicated php-fpm process is used: # If a dedicated php-fpm process is used:
# Don't forget to modify ../conf/nginx.conf accordingly or your app will not work! # Don't forget to modify ../conf/nginx.conf accordingly or your app will not work!
# sed -i "s@YNH_WWW_APP@$app@g" $nginx_conf # sed -i "s@YNH_WWW_APP@$app@g" $nginx_conf
@ -103,7 +115,7 @@ if [[ $is_public -eq 1 ]]; then
ynh_app_setting_set "$app" unprotected_uris "/" ynh_app_setting_set "$app" unprotected_uris "/"
fi fi
sudo chmod -R 777 $src_path/data sudo chmod -R 777 $final_path/data
# Reload services # Reload services
sudo service nginx reload sudo service nginx reload

View file

@ -1,8 +1,12 @@
#!/bin/bash #!/bin/bash
# Exit on command errors and treat unset variables as an error
set -u
# See comments in install script # See comments in install script
app=$YNH_APP_INSTANCE_NAME app=$YNH_APP_INSTANCE_NAME
# Source YunoHost helpers # Source YunoHost helpers
source /usr/share/yunohost/helpers source /usr/share/yunohost/helpers
@ -13,7 +17,7 @@ domain=$(ynh_app_setting_get "$app" domain)
sudo rm -rf /var/www/$app sudo rm -rf /var/www/$app
# Remove nginx configuration file # Remove nginx configuration file
sudo rm -f /etc/nginx/conf.d/$domain.d/$app.conf [[ -n $domain ]] && sudo rm -f "/etc/nginx/conf.d/${domain}.d/${app}.conf"
### PHP (remove if not used) ### ### PHP (remove if not used) ###
# If a dedicated php-fpm process is used: # If a dedicated php-fpm process is used:
@ -32,3 +36,4 @@ ynh_mysql_drop_user "$dbuser" || true
# Reload nginx service # Reload nginx service
sudo service nginx reload sudo service nginx reload
echo -e "\e[0m" # Restore normal color

View file

@ -6,7 +6,7 @@
# Exit on command errors and treat unset variables as an error # Exit on command errors and treat unset variables as an error
set -eu set -eu
# See comments in install script # See comsudo yunohost app ssowatconfments in install script
app=$YNH_APP_INSTANCE_NAME app=$YNH_APP_INSTANCE_NAME
# Source YunoHost helpers # Source YunoHost helpers
@ -14,19 +14,24 @@ source /usr/share/yunohost/helpers
# Retrieve old app settings # Retrieve old app settings
domain=$(ynh_app_setting_get "$app" domain) domain=$(ynh_app_setting_get "$app" domain)
path_url=$(ynh_app_setting_get "$app" path_url) path=$(ynh_app_setting_get "$app" path_url)
is_public=$(ynh_app_setting_get "$app" is_public)
# Check domain/path availability # Check domain/path availability
sudo yunohost app checkurl "${domain}${path_url}" -a "$app" \ sudo yunohost app checkurl "${domain}${path}" -a "$app" \
|| ynh_die "Path not available: ${domain}${path_url}" || ynh_die "Path not available: ${domain}${path}"
# Restore sources & data # Restore sources & data
src_path="/var/www/${app}" final_path="/var/www/${app}"
sudo cp -a ./sources "$src_path" if [ -d $final_path ]; then
ynh_die "There is already a directory: $final_path"
fi
sudo cp -a ./sources "${final_path}"
# Restore permissions to app files # Restore permissions to app files
# you may need to make some file and/or directory writeable by www-data (nginx user) # you may need to make some file and/or directory writeable by www-data (nginx user)
sudo chown -R root: "$src_path" sudo chown -R root: "${final_path}"
### MySQL (remove if not used) ### ### MySQL (remove if not used) ###
# If a MySQL database is used: # If a MySQL database is used:
@ -38,8 +43,14 @@ ynh_mysql_create_db "$dbname" "$dbuser" "$dbpass"
ynh_mysql_connect_as "$dbuser" "$dbpass" "$dbname" < ./dump.sql ynh_mysql_connect_as "$dbuser" "$dbpass" "$dbname" < ./dump.sql
### MySQL end ### ### MySQL end ###
# Check configuration files nginx
nginx_conf="/etc/nginx/conf.d/${domain}.d/${app}.conf"
if [ -f $nginx_conf ]; then
ynh_die "The NGINX configuration already exists at '${nginx_conf}'. You should safely delete it before restoring this app."
fi
# Restore NGINX configuration # Restore NGINX configuration
sudo cp -a ./nginx.conf "/etc/nginx/conf.d/${domain}.d/${app}.conf" sudo cp -a ./nginx.conf "${nginx_conf}"
### PHP (remove if not used) ### ### PHP (remove if not used) ###
# If a dedicated php-fpm process is used: # If a dedicated php-fpm process is used:
@ -48,7 +59,13 @@ sudo cp -a ./nginx.conf "/etc/nginx/conf.d/${domain}.d/${app}.conf"
# sudo service php5-fpm reload # sudo service php5-fpm reload
### PHP end ### ### PHP end ###
sudo chmod -R 777 $src_path/data # If app is public, add url to SSOWat conf as skipped_uris
if [[ $is_public -eq 1 ]]; then
# See install script
ynh_app_setting_set "$app" unprotected_uris "/"
fi
sudo chmod -R 777 $final_path/data
# Restart webserver # Restart webserver
sudo service nginx reload sudo service nginx reload

View file

@ -3,6 +3,8 @@
# Exit on command errors and treat unset variables as an error # Exit on command errors and treat unset variables as an error
set -eu set -eu
source .fonctions
# See comments in install script # See comments in install script
app=$YNH_APP_INSTANCE_NAME app=$YNH_APP_INSTANCE_NAME
@ -11,38 +13,39 @@ source /usr/share/yunohost/helpers
# Retrieve app settings # Retrieve app settings
domain=$(ynh_app_setting_get "$app" domain) domain=$(ynh_app_setting_get "$app" domain)
path_url=$(ynh_app_setting_get "$app" path) path=$(ynh_app_setting_get "$app" path)
is_public=$(ynh_app_setting_get "$app" is_public) is_public=$(ynh_app_setting_get "$app" is_public)
# Check src_path directory # Check final_path directory
src_path=/var/www/$app final_path=/var/www/$app
[[ ! -d $src_path ]] && ynh_die \ [[ ! -d $final_path ]] && ynh_die \
"The destination directory '$DESTDIR' does not exist.\ "The destination directory '$final_path' does not exist.\
The app is not correctly installed, you should remove it first." The app is not correctly installed, you should remove it first."
# Move old app dir # Move old app dir
sudo mv ${src_path} ${src_path}.old sudo mv ${final_path} ${final_path}.old
# Copy source files # Copy source files
sudo mkdir -p $src_path sudo mkdir -p $final_path
sudo cp -a ../sources/. $src_path # Get source
SETUP_SOURCE
# restore data # restore data
sudo cp -a ${src_path}.old/data ${src_path} sudo cp -a ${final_path}.old/data ${final_path}
# delete temp directory # delete temp directory
sudo rm -Rf ${src_path}.old sudo rm -Rf ${final_path}.old
# Set permissions to app files # Set permissions to app files
# you may need to make some file and/or directory writeable by www-data (nginx user) # you may need to make some file and/or directory writeable by www-data (nginx user)
sudo chown -R root: $src_path sudo chown -R root: $final_path
# Modify Nginx configuration file and copy it to Nginx conf directory # Modify Nginx configuration file and copy it to Nginx conf directory
nginx_conf=../conf/nginx.conf nginx_conf=../conf/nginx.conf
sed -i "s@YNH_WWW_PATH@$path_url@g" $nginx_conf sed -i "s@YNH_WWW_PATH@$path@g" $nginx_conf
sed -i "s@YNH_WWW_ALIAS@$src_path/@g" $nginx_conf sed -i "s@YNH_WWW_ALIAS@$final_path/@g" $nginx_conf
# If a dedicated php-fpm process is used: # If a dedicated php-fpm process is used:
# #
# sed -i "s@YNH_WWW_APP@$app@g" $nginx_conf # sed -i "s@YNH_WWW_APP@$app@g" $nginx_conf
@ -52,7 +55,7 @@ sudo cp $nginx_conf /etc/nginx/conf.d/$domain.d/$app.conf
# If a dedicated php-fpm process is used: # If a dedicated php-fpm process is used:
# # Modify PHP-FPM pool configuration and copy it to the pool directory # # Modify PHP-FPM pool configuration and copy it to the pool directory
# sed -i "s@YNH_WWW_APP@$app@g" ../conf/php-fpm.conf # sed -i "s@YNH_WWW_APP@$app@g" ../conf/php-fpm.conf
# sed -i "s@YNH_WWW_ALIAS@$src_path/@g" ../conf/php-fpm.conf # sed -i "s@YNH_WWW_ALIAS@$final_path/@g" ../conf/php-fpm.conf
# finalphpconf=/etc/php5/fpm/pool.d/$app.conf # finalphpconf=/etc/php5/fpm/pool.d/$app.conf
# sudo cp ../conf/php-fpm.conf $finalphpconf # sudo cp ../conf/php-fpm.conf $finalphpconf
# sudo chown root: $finalphpconf # sudo chown root: $finalphpconf
@ -66,6 +69,6 @@ if [[ $is_public -eq 1 ]]; then
ynh_app_setting_set "$app" unprotected_uris "/" ynh_app_setting_set "$app" unprotected_uris "/"
fi fi
sudo chmod -R 777 $src_path/data sudo chmod -R 777 $final_path/data
# Reload nginx service # Reload nginx service
sudo service nginx reload sudo service nginx reload

View file

@ -1,429 +0,0 @@
[![Latest Stable Version](https://poser.pugx.org/fisharebest/webtrees/v/stable.svg)](https://packagist.org/packages/fisharebest/webtrees)
[![Build Status](https://travis-ci.org/fisharebest/webtrees.svg?branch=master)](https://travis-ci.org/fisharebest/webtrees)
[![Coverage Status](https://coveralls.io/repos/github/fisharebest/webtrees/badge.svg?branch=master)](https://coveralls.io/github/fisharebest/webtrees?branch=master)
[![Translation status](https://translate.webtrees.net/widgets/webtrees/-/svg-badge.svg)](https://translate.webtrees.net/engage/webtrees/?utm_source=widget)
[![SensioLabsInsight](https://insight.sensiolabs.com/projects/78a5ba19-7ddf-4a58-8262-1c8a149f38de/mini.png)](https://insight.sensiolabs.com/projects/78a5ba19-7ddf-4a58-8262-1c8a149f38de)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/fisharebest/webtrees/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/fisharebest/webtrees/?branch=master)
[![Code Climate](https://codeclimate.com/github/fisharebest/webtrees/badges/gpa.svg)](https://codeclimate.com/github/fisharebest/webtrees)
# webtrees
The projects website is [www.webtrees.net](https://www.webtrees.net).
Further documentation is available at [wiki.webtrees.net](https://wiki.webtrees.net).
## Contents
* [License](#license)
* [Introduction](#introduction)
* [System requirements](#system-requirements)
* [Installation](#installation)
* [Upgrading](#upgrading)
* [Gedcom (family tree) files](#gedcom-family-tree-files)
* [Security](#security)
* [Backup](#backup)
* [Converting from phpgedview](#converting-from-phpgedview)
### License
* **webtrees: online genealogy**
* Copyright (C) 2016 webtrees development team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
### Introduction
**webtrees** is the web's leading online collaborative genealogy application.
* It works from standard GEDCOM files, and is therefore compatible with every
major desktop application.
* It aims to to be efficient and effective by using the right combination of
third-party tools, design techniques and open standards.
**webtrees** allows you to view and edit your genealogy on your website. It has
full editing capabilities, full privacy functions, and supports imedia such as
photos and document images. As an online program, it fosters extended family
participation and good ancestral recording habits, as it simplifies the process
of collaborating with others working on your family lines. Your latest information
is always on your web site and available for others to see, defined by viewing
rules you set. For more information and to see working demos, visit
[webtrees.net](https://webtrees.net/).
**webtrees** is Open Source software that has been produced by people from many
countries freely donating their time and talents to the project. All service,
support, and future development is dependent on the time developers are willing
to donate to the project, often at the expense of work, recreation, and family.
Beyond the few donations received from users, developers receive no compensation
for the time they spend working on the project. There is also no outside source
of revenue to support the project. Please consider these circumstances when
making support requests and consider volunteering your own time and skills to make
the project even stronger and better.
### System requirements
To install **webtrees**, you need:
* A webserver. Apache and IIS are the most common types. There are no requirements
to use a specific type or version.
* Approximately 65MB of disk space for the application files, plus whatever is
needed for your media files, GEDCOM files and database.
* PHP 5.3.2 or later. Note that many web hosts offer *both* PHP4 and PHP5,
typically with PHP4 as the default. If this is the case, you will be able to
switch between the two using a control panel or a configuration file. Refer
to your web host's support documentation for details.
* PHP should be configured with the PHP/PDO library for MySQL. This is a
server configuration option. It is enabled by default on most hosts.
See [http://php.net/pdo](http://php.net/pdo)
* PHP should be configured to allow sufficient server resources (memory and
execution time) for the size of your system. Typical requirements are:
* Small systems (500 individuals): 1632 MB, 1020 seconds
* Medium systems (5,000 individuals): 3264 MB, 2040 seconds
* Large systems (50,000 individuals): 64128 MB, 4080 seconds
* MySQL 5.0.13 or later. Note that **webtrees** can share a single database
with other applications, by choosing a unique table prefix during configuration.
If the number of databases is not restricted, you can set up a database purely
for use by **webtrees** and create a separate user and password for only
your genealogy.
* Internet browser compatibility. **webtrees** supports the use of most
current versions of open-source browsers such as Firefox, Chrome, and Safari.
We will do our best to support others such as Opera and Internet Explorer,
though not their earlier versions. Currently many things do not work well in
IE7, and some not in IE8 either. We strongly recommend anyone using these
obsolete browsers upgrade as soon as possible. We are also aware that IE
provides poor RTL language support generally, so cannot recommend it for
sites requiring RTL languages.
* To view sites that contain both left-to-right and right-to-left text (e.g.
English data on Hebrew pages), you will need to use a browser that provides
support for the HTML5 **dir="auto"** attribute. At present, Internet Explorer
(11 and lower) do not support this.
* HTML Frames. Note that **webtrees** uses cookies to track login sessions. Sites
that make **webtrees** pages available inside an HTML Frames will encounter
problems with login for versions 7, 8, and 9 of Internet Explorer. IE users
should review the ``Privacy settings Tools`` / ``Internet Options`` for more details.
### Installation
Installing **webtrees** is really easy. All you need is a webserver with PHP and
MySQL. Almost every web hosting service provides these, but be sure to confirm
that those supplied meet or exceed the minimum system requirements.
1. Download latest stable version from [webtrees.net](https://webtrees.net/)
2. Unzip the files and upload them to an empty directory on your web server.
3. Open your web browser and type the URL for your **webtrees** site (for example,
[http://www.yourserver.com/webtrees](http://www.yourserver.com/webtrees)) into
the address bar.
4. The **webtrees** setup wizard will start automatically. Simply follow the steps,
answering each question as you proceed. (See '''Step Six''' procedure below.)
That's it!
However, before you can use **webtrees**, you need one (or possibly more) GEDCOM
(family tree) files. If you have been doing your research using a desktop program
such as Family Tree Maker, you can use it's “save as GEDCOM” function to create
a GEDCOM file. If you are starting from scratch, then **webtrees** can create a
GEDCOM file for you. Alternatively, you can import data directly from PhpGedView.
So, after installation, you'll be directed to the GEDCOM (family tree)
administration page, where you'll need to select one of the following options:
* On successful completion of all steps you will be taken to the GEDCOM (family tree)
administration page where you can:
* UPLOAD a GEDCOM file from your local machine
* ADD a GEDCOM file from your server, (if your GEDCOM file is too large to upload,
you can copy it to the webtrees/data folder, and load it from there)
* CREATE a new, empty GEDCOM file
* TRANSFER your existing PhpGedView data straight into **webtrees**, using the
PhpGedView-to-**webtrees** wizard described in section 9 below:
[Converting from phpgedview](#converting-from-phpgedview)
There are *lots* of configuration options. You'll probably want to review the
privacy settings first. Don't worry too much about all the other options - the
defaults are good for most people. If you get stuck, there's lots of built-in
help and you can get friendly advice from the [help](https://webtrees.net/forums)
forum.
### Upgrading
Upgrading **webtrees** is quick and easy. It is strongly recommended that you
upgrade your installation whenever a new version is made available. Even minor
**webtrees** version updates usually contain a significant number of bug fixes
as well as interface improvements and program enhancements. The Administration
page of your **webtrees** installation will display a notification whenever a
new version is available.
1. Now would be a good time to make a [backup](#backup).
2. Download the latest version of **webtrees** available from
[webtrees.net](https://webtrees.net/)
3. While you are in the middle of uploading the new files,
a visitor to your site would encounter a mixture of new and old files. This
could cause unpredictable behaviour or errors. To prevent this, create the
file **data/offline.txt**. While this file exists, visitors will see a
“site unavailable - come back later” message.
4. Unzip the .ZIP file, and upload the files to your web server, overwriting the existing files.
5. Delete the file **data/offline.txt**
#### Note for Macintosh users
Step 4 assumes you are using a copy tool that **merges** directories rather than
replaces them. (**Merge** is standard behaviour on Windows and Linux.) If you use
the Macintosh Finder or other similar tool to perform step 3, it will **replace**
your configuration, media and other directories with the empty/default ones from
the installation. This would be very bad (but you did take a backup in step 1,
didn't you!). Further details and recommendations for suitable tools can be found
by searching [google.com](http://google.com).
#### Note for anyone using custom code (modules, themes, etc.).
It is **very likely** that your custom code will not work when you upgrade
**webtrees**.
**We recommend that you disable all custom code before you apply the upgrade.**
Disable custom modules, switch over to a standard
theme, and remove any code “hacks”. Once the upgrade is complete and you are satisfied
your site is fully operational contact the source of those modules or themes for
a new version.
#### General note
Depending on the changes in the new files, your browser configuration
and possibly other factors, it is always wise to clear both the **webtrees** cache
and your browser cache immediately after the upgrade is completed. The **webtrees**
cache can be cleared simply by going to ``Administration`` ->
``Cleanup data directory`` and deleting the cache.
If you have any problems or questions, help is available on the
[webtrees forum](https://webtrees.net/forums).
### Gedcom (family tree) files
When you ADD, IMPORT or UPLOAD a family tree (GEDCOM) file in **webtrees** the
data from the file is all transferred to the database tables. The file itself is
no longer used or required by **webtrees**
* If you use ADD or IMPORT, your file remains in the webtrees/data folder you
first copied it to, and will not be changed by any subsequent editing of the
**webtrees** data.
* If you use UPLOAD, the file is left in its original location, and again remains
untouched.
When or if you change your genealogy data outside of **webtrees**, it is not
necessary to delete your GEDCOM file or database from **webtrees** and start
over. Follow these steps to update a GEDCOM that has already been imported:
* Decide if you want to IMPORT or UPLOAD your new GEDCOM file.
* Use UPLOAD if your family tree file is smaller than your server's PHP file
upload limit (often 2MB).The new file can have any name you choose.
* Use IMPORT for larger files. In this case you need to use FTP to first copy
your file to the webtrees/data folder. Either copy over the existing file,
or use a different name.
* From the Administration page, go to your **webtrees** Family trees (GEDCOM)
configuration page. On the line relating to this particular family tree (GEDCOM)
file (or a new one) click either IMPORT or UPLOAD.
* Take careful note of the media items option (_“If you have created media objects
in **webtrees**, and have edited your data off-line using software that
deletes media objects, then tick this box to merge the current media objects
with the new GEDCOM.”_) In most cases you should leave this box **UNCHECKED**.
* Click “SAVE”. **webtrees** will validate the GEDCOM again before importing.
During this process, **webtrees** copies your entire family tree (GEDCOM file)
to a 'chunk' table within your database. Depending on the coding of your file,
its file size and the capabilities of your server and the supporting software,
this may take some time. **No progress bar will show while the data is being
copied** and should you navigate away from this page, the process is suspended.
It will start again when you return to the Family Tree management page.
#### FORMAT
Every Family History program has its own method of creating GEDCOM files, and
differing output format options to select from. **webtrees'** import routines
can read many different formats, but not necessarily all. If your software has
a “UTF8” option you should always use that. However, **webtrees** has been
tested with these alternative formats:
* ANSI
* imports OK, but is slow due to the translation into UTF8 as part
of the import process.
* MAC
* imports OK, but is slow due to the translation into UTF8 as part
of the import process.
* DOS
* imports OK, but is slow due to the translation into UTF8 as part
of the import process.
* ANSEL
* currently will not import. Gives warning *Error: cannot convert
GEDCOM file from ANSEL encoding to UTF-8 encoding*. Later releases
of **webtrees** may include translation from ANSEL to UTF8, but this
is not a simple process.
### Security
**Security** in _webtrees_ means ensuring your site is safe from unwanted
intrusions, hacking, or access to data and configuration files. The developers
of _webtrees_ regard security as an extremely important part of its development
and have made every attempt to ensure your data is safe.
The area most at risk of intrusion would be the /data folder that contains your
config.ini.php file, and various temporary files. If you are concerned there
may be a risk there is a very simple test you can do: try to fetch **config.ini.php**
by typing **http://_url to your site_/data/config.ini.php** in your web
browser.
The most likely result is an “access denied” message like this:
Forbidden
You don't have permission to access /data/xxxx.ged on this server.
This indicates that the protection built into **webtrees** is working, and no
further action is required.
In the unlikely event you do fetch the file (you will just see a semicolon),
then that protection is not working on your site and you should take some further
action.
If your server runs PHP in CGI mode, then change the permission of the /data
directory to 700 instead of 777. This will block access to the httpd process,
while still allowing access to PHP scripts.
This will work for perhaps 99% of all users. Only the remaining 1% should consider
the most complex solution, moving the /data/ directory out of accessible web
space. (**_Note:_** In many shared hosting environments this is not an option anyway.)
If you do find it necessary, following is an example of the process required:
If your home directory is something like **/home/username**,
and the root directory for your web site is **/home/username/public_html**,
and you have installed **webtrees** in the **public_html/webtrees** directory,
then you would create a new **data** folder in your home directory at the same
level as your public_html directory, such as **/home/username/private/data**,
and place your GEDCOM (family tree) file there.
Then change the **Data file directory** setting on the ``Admin`` ->
``Site Administration`` page from the default **data/** to the new
location **/home/username/private/data**
You will have **two** data directories:
* [path to webtrees]/data - just needs to contain config.ini.php
* /home/username/private/data - contains everything else
#### Hypertext Transfer Protocol Secure (HTTPS)
**webtrees** supports https access. If your website is configured with mandatory
or optional https support **webtrees** will operate correctly in either mode.
If your website is configured with optional https support, **webtrees** can be
configured to switch to https at login. To enable https at login, set the Login
URL setting on the ``Admin`` -> ``Site Administration`` ->
``Configuration page`` to your https login URL, which is often in the form
[https://example.com/admin.php](https://example.com/admin.php)
(substitute your domain for example.com).
**Warning:** Misconfiguration can disable your login links. If this occurs,
access the login by typing the correct URL directly into your browser's address input.
### Backup
Backups are good. Whatever problem you have, it can always be fixed from a good
backup.
To make a backup of webtrees, you need to make a copy of the following
1. The files in the *webtrees/data* directory.
2. The files in the *webtrees/media* directory.
3. The tables in the database. Freely available tools such as
[phpMyAdmin](http://www.phpmyadmin.net) allow you to do this in one click.
Remember that most web hosting services do NOT backup your data, and this is
your responsibility.
### Converting from phpgedview
If you are moving to **webtrees** from an existing PhpGedView setup, and
your PhpGedView install meets certain requirements, **webtrees** has provided a “wizard”
to help make the transfer of the majority of your data a relatively quick and
painless operation. See exceptions noted below. Please note that we have designed
this wizard so as to not disturb your existing PhpGedView installation, leaving all those
settings, data and your website intact and fully functional.
The requirements are:
* The PhpGedView database and index directory must be on the same server as **webtrees**.
* Your **webtrees** MySQL database username and password must either be the same
as your PhpGedView username and password, or if you created a new user for **webtrees**,
that new user must also have full privileges to access your PhpGedView database.
* PhpGedView must be at least versions 4.2.3 or 4.2.4 (this corresponds to an internal
“PGV_SCHEMA_VERSION” of between 10 and 14). Newer versions, including the current
version 4.3 SVN work (as of JAN 2013) also currently, and later versions, should
they be released, will probably work, provided the data structures do not change;
* All changes in PhpGedView must be accepted (as pending edits will not be transfered).
* All existing PhpGedView users must have an email address, and it must be unique to that
user (PhpGedView allows users to delete their email address, or have the same email
address as other users; **webtrees** requires that all users have their own
unique email address).
* The wizard transfer process overwrites the username and password you may have
entered in setting up the initial admin account. The main administration user
name and password in **webtrees** will be identical to the admin username and
password from PhpGedView after running the wizard. Once done, you can change it back
if desired.
#### Warning
Please read the [https://wiki.webtrees.net/en/Converting_from_PhpGedView](https://wiki.webtrees.net/en/Converting_from_PhpGedView)
before doing a transfer as important pre-processing steps and known issues may
be outlined there.
#### Important Note
This transfer wizard is not able to assist with moving media items. You will need
to set up and move or copy your media configuration and objects separately after
the transfer wizard is finished. If you use the media firewall in PhpGedView with a
directory outside the PhpGedView root, then duplicating the media configuration in
**webtrees** to use the same firewall directory should make your media available
in **webtrees**.
After the transfer is complete, you should check your family tree configuration
and privacy settings. Due to differences in internal data formats, the following
settings are not yet transfered: custom privacy restrictions, block configuration,
FAQs, and HTML blocks. We hope to add these to the wizard in a future release.
#### Custom privacy restrictions, block configuration, FAQs and HTML blocks
We hope to add these to the wizard in a future release. Otherwise, read the
[https://wiki.webtrees.net/en/Converting_from_PhpGedView](https://wiki.webtrees.net/en/Converting_from_PhpGedView)
before reporting a problem in the forum.
The transfer wizard is accessed in **webtrees** from the bottom of the
“Manage family trees” page to which you will be automatically directed once you
have completed the initial **webtrees** installation steps (section 4 above:
[installation](#installation)). This option is only available on a new,
empty **webtrees** installation; once you have created a GEDCOM (family tree)
or added user accounts, it will no longer be available.

View file

@ -1,253 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
/**
* Defined in session.php
*
* @global Tree $WT_TREE
*/
global $WT_TREE;
use Fisharebest\Webtrees\Functions\FunctionsDb;
use Fisharebest\Webtrees\Functions\FunctionsEdit;
use Fisharebest\Webtrees\Functions\FunctionsImport;
define('WT_SCRIPT_NAME', 'action.php');
require './includes/session.php';
header('Content-type: text/html; charset=UTF-8');
if (!Filter::checkCsrf()) {
http_response_code(406);
return;
}
switch (Filter::post('action')) {
case 'accept-changes':
// Accept all the pending changes for a record
$record = GedcomRecord::getInstance(Filter::post('xref', WT_REGEX_XREF), $WT_TREE);
if ($record && Auth::isModerator($record->getTree()) && $record->canShow() && $record->canEdit()) {
if ($record->isPendingDeletion()) {
FlashMessages::addMessage(/* I18N: %s is the name of a genealogy record */
I18N::translate('“%s” has been deleted.', $record->getFullName()));
} else {
FlashMessages::addMessage(/* I18N: %s is the name of a genealogy record */
I18N::translate('The changes to “%s” have been accepted.', $record->getFullName()));
}
FunctionsImport::acceptAllChanges($record->getXref(), $record->getTree()->getTreeId());
} else {
http_response_code(406);
}
break;
case 'copy-fact':
// Copy a fact to the clipboard
$xref = Filter::post('xref', WT_REGEX_XREF);
$fact_id = Filter::post('fact_id');
$record = GedcomRecord::getInstance($xref, $WT_TREE);
if ($record && $record->canEdit()) {
foreach ($record->getFacts() as $fact) {
if ($fact->getFactId() == $fact_id) {
switch ($fact->getTag()) {
case 'NOTE':
case 'SOUR':
case 'OBJE':
$type = 'all'; // paste this anywhere
break;
default:
$type = $record::RECORD_TYPE; // paste only to the same record type
break;
}
$clipboard = Session::get('clipboard');
if (!is_array($clipboard)) {
$clipboard = array();
}
$clipboard[$fact_id] = array(
'type' => $type,
'factrec' => $fact->getGedcom(),
'fact' => $fact->getTag(),
);
// The clipboard only holds 10 facts
while (count($clipboard) > 10) {
array_shift($clipboard);
}
Session::put('clipboard', $clipboard);
FlashMessages::addMessage(I18N::translate('The record has been copied to the clipboard.'));
break 2;
}
}
}
break;
case 'paste-fact':
// Paste a fact from the clipboard
$xref = Filter::post('xref', WT_REGEX_XREF);
$fact_id = Filter::post('fact_id');
$record = GedcomRecord::getInstance($xref, $WT_TREE);
$clipboard = Session::get('clipboard');
if ($record && $record->canEdit() && isset($clipboard[$fact_id])) {
$record->createFact($clipboard[$fact_id]['factrec'], true);
}
break;
case 'delete-fact':
$xref = Filter::post('xref', WT_REGEX_XREF);
$fact_id = Filter::post('fact_id');
$record = GedcomRecord::getInstance($xref, $WT_TREE);
if ($record && $record->canShow() && $record->canEdit()) {
foreach ($record->getFacts() as $fact) {
if ($fact->getFactId() == $fact_id && $fact->canShow() && $fact->canEdit()) {
$record->deleteFact($fact_id, true);
break 2;
}
}
}
// Cant find the record/fact, or dont have permission to delete it.
http_response_code(406);
break;
case 'delete-record':
$record = GedcomRecord::getInstance(Filter::post('xref', WT_REGEX_XREF), $WT_TREE);
if ($record && Auth::isEditor($record->getTree()) && $record->canShow() && $record->canEdit()) {
// Delete links to this record
foreach (FunctionsDb::fetchAllLinks($record->getXref(), $record->getTree()->getTreeId()) as $xref) {
$linker = GedcomRecord::getInstance($xref, $WT_TREE);
$old_gedcom = $linker->getGedcom();
$new_gedcom = FunctionsEdit::removeLinks($old_gedcom, $record->getXref());
// FunctionsDb::fetch_all_links() does not take account of pending changes. The links (or even the
// record itself) may have already been deleted.
if ($old_gedcom !== $new_gedcom) {
// If we have removed a link from a family to an individual, and it has only one member
if (preg_match('/^0 @' . WT_REGEX_XREF . '@ FAM/', $new_gedcom) && preg_match_all('/\n1 (HUSB|WIFE|CHIL) @(' . WT_REGEX_XREF . ')@/', $new_gedcom, $match) == 1) {
// Delete the family
$family = GedcomRecord::getInstance($xref, $WT_TREE);
FlashMessages::addMessage(/* I18N: %s is the name of a family group, e.g. “Husband name + Wife name” */ I18N::translate('The family “%s” has been deleted because it only has one member.', $family->getFullName()));
$family->deleteRecord();
// Delete any remaining link to this family
if ($match) {
$relict = GedcomRecord::getInstance($match[2][0], $WT_TREE);
$new_gedcom = $relict->getGedcom();
$new_gedcom = FunctionsEdit::removeLinks($new_gedcom, $linker->getXref());
$relict->updateRecord($new_gedcom, false);
FlashMessages::addMessage(/* I18N: %s are names of records, such as sources, repositories or individuals */ I18N::translate('The link from “%1$s” to “%2$s” has been deleted.', $relict->getFullName(), $family->getFullName()));
}
} else {
// Remove links from $linker to $record
FlashMessages::addMessage(/* I18N: %s are names of records, such as sources, repositories or individuals */ I18N::translate('The link from “%1$s” to “%2$s” has been deleted.', $linker->getFullName(), $record->getFullName()));
$linker->updateRecord($new_gedcom, false);
}
}
}
// Delete the record itself
$record->deleteRecord();
} else {
http_response_code(406);
}
break;
case 'delete-user':
$user = User::find(Filter::postInteger('user_id'));
if ($user && Auth::isAdmin() && Auth::user() !== $user) {
Log::addAuthenticationLog('Deleted user: ' . $user->getUserName());
$user->delete();
}
break;
case 'language':
// Change the current language
$language = Filter::post('language');
try {
I18N::init($language);
Session::put('locale', $language);
// Remember our selection
Auth::user()->setPreference('language', $language);
} catch (\Exception $ex) {
// Request for a non-existant language.
http_response_code(406);
}
break;
case 'masquerade':
$user = User::find(Filter::postInteger('user_id'));
if ($user && Auth::isAdmin() && Auth::user() !== $user) {
Log::addAuthenticationLog('Masquerade as user: ' . $user->getUserName());
Auth::login($user);
Session::put('masquerade', '1');
} else {
http_response_code(406);
}
break;
case 'unlink-media':
// Remove links from an individual and their spouse-family records to a media object.
// Used by the "unlink" option on the album (lightbox) tab.
$source = Individual::getInstance(Filter::post('source', WT_REGEX_XREF), $WT_TREE);
$target = Filter::post('target', WT_REGEX_XREF);
if ($source && $source->canShow() && $source->canEdit() && $target) {
// Consider the individual and their spouse-family records
$sources = $source->getSpouseFamilies();
$sources[] = $source;
foreach ($sources as $source) {
foreach ($source->getFacts() as $fact) {
if (!$fact->isPendingDeletion()) {
if ($fact->getValue() == '@' . $target . '@') {
// Level 1 links
$source->deleteFact($fact->getFactId(), true);
} elseif (strpos($fact->getGedcom(), ' @' . $target . '@')) {
// Level 2-3 links
$source->updateFact($fact->getFactId(), preg_replace(array('/\n2 OBJE @' . $target . '@(\n[3-9].*)*/', '/\n3 OBJE @' . $target . '@(\n[4-9].*)*/'), '', $fact->getGedcom()), true);
}
}
}
}
} else {
http_response_code(406);
}
break;
case 'reject-changes':
// Reject all the pending changes for a record
$record = GedcomRecord::getInstance(Filter::post('xref', WT_REGEX_XREF), $WT_TREE);
if ($record && $record->canEdit() && Auth::isModerator($record->getTree())) {
FlashMessages::addMessage(/* I18N: %s is the name of an individual, source or other record */ I18N::translate('The changes to “%s” have been rejected.', $record->getFullName()));
FunctionsImport::rejectAllChanges($record);
} else {
http_response_code(406);
}
break;
case 'theme':
// Change the current theme
$theme = Filter::post('theme');
if (Site::getPreference('ALLOW_USER_THEMES') && array_key_exists($theme, Theme::themeNames())) {
Session::put('theme_id', $theme);
// Remember our selection
Auth::user()->setPreference('theme', $theme);
} else {
// Request for a non-existant theme.
http_response_code(406);
}
break;
}

View file

@ -1,719 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
/**
* Defined in session.php
*
* @global Tree $WT_TREE
*/
global $WT_TREE;
use Fisharebest\Webtrees\Controller\SimpleController;
use Fisharebest\Webtrees\Functions\Functions;
use Fisharebest\Webtrees\Functions\FunctionsDb;
use Fisharebest\Webtrees\Functions\FunctionsEdit;
use Fisharebest\Webtrees\Functions\FunctionsImport;
use Fisharebest\Webtrees\Functions\FunctionsPrint;
use Fisharebest\Webtrees\Query\QueryMedia;
define('WT_SCRIPT_NAME', 'addmedia.php');
require './includes/session.php';
$NO_UPDATE_CHAN = $WT_TREE->getPreference('NO_UPDATE_CHAN');
$MEDIA_DIRECTORY = $WT_TREE->getPreference('MEDIA_DIRECTORY');
$pid = Filter::get('pid', WT_REGEX_XREF, Filter::post('pid', WT_REGEX_XREF)); // edit this media object
$linktoid = Filter::get('linktoid', WT_REGEX_XREF, Filter::post('linktoid', WT_REGEX_XREF)); // create a new media object, linked to this record
$action = Filter::get('action', null, Filter::post('action'));
$filename = Filter::get('filename', null, Filter::post('filename'));
$text = Filter::postArray('text');
$tag = Filter::postArray('tag', WT_REGEX_TAG);
$islink = Filter::postArray('islink');
$glevels = Filter::postArray('glevels', '[0-9]');
$folder = Filter::post('folder');
$update_CHAN = !Filter::postBool('preserve_last_changed');
$controller = new SimpleController;
$controller
->addExternalJavascript(WT_AUTOCOMPLETE_JS_URL)
->addInlineJavascript('autocomplete();')
->restrictAccess(Auth::isMember($WT_TREE));
$disp = true;
$media = Media::getInstance($pid, $WT_TREE);
if ($media) {
$disp = $media->canShow();
}
if ($action == 'update' || $action == 'create') {
if ($linktoid) {
$disp = GedcomRecord::getInstance($linktoid, $WT_TREE)->canShow();
}
}
if (!Auth::isEditor($WT_TREE) || !$disp) {
$controller
->pageHeader()
->addInlineJavascript('closePopupAndReloadParent();');
return;
}
// There is a lot of common code in the create and update cases…
// …and also in the admin_media_upload.php script
switch ($action) {
case 'create': // Save the information from the “showcreateform” action
$controller->setPageTitle(I18N::translate('Create a media object'));
// Validate the media folder
$folderName = str_replace('\\', '/', $folder);
$folderName = trim($folderName, '/');
if ($folderName == '.') {
$folderName = '';
}
if ($folderName) {
$folderName .= '/';
// Not allowed to use “../”
if (strpos('/' . $folderName, '/../') !== false) {
FlashMessages::addMessage('Folder names are not allowed to include “../”');
break;
}
}
// Make sure the media folder exists
if (!is_dir(WT_DATA_DIR . $MEDIA_DIRECTORY)) {
if (File::mkdir(WT_DATA_DIR . $MEDIA_DIRECTORY)) {
FlashMessages::addMessage(I18N::translate('The folder %s has been created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY)));
} else {
FlashMessages::addMessage(I18N::translate('The folder %s does not exist, and it could not be created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY)), 'danger');
break;
}
}
// Managers can create new media paths (subfolders). Users must use existing folders.
if ($folderName && !is_dir(WT_DATA_DIR . $MEDIA_DIRECTORY . $folderName)) {
if (Auth::isManager($WT_TREE)) {
if (File::mkdir(WT_DATA_DIR . $MEDIA_DIRECTORY . $folderName)) {
FlashMessages::addMessage(I18N::translate('The folder %s has been created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY . $folderName)));
} else {
FlashMessages::addMessage(I18N::translate('The folder %s does not exist, and it could not be created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY . $folderName)), 'danger');
break;
}
} else {
// Regular users should not have seen this option - so no need for an error message.
break;
}
}
// The media folder exists. Now create a thumbnail folder to match it.
if (!is_dir(WT_DATA_DIR . $MEDIA_DIRECTORY . 'thumbs/' . $folderName)) {
if (!File::mkdir(WT_DATA_DIR . $MEDIA_DIRECTORY . 'thumbs/' . $folderName)) {
FlashMessages::addMessage(I18N::translate('The folder %s does not exist, and it could not be created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY . 'thumbs/' . $folderName)), 'danger');
break;
}
}
// A thumbnail file with no main image?
if (!empty($_FILES['thumbnail']['name']) && empty($_FILES['mediafile']['name'])) {
// Assume the user used the wrong field, and treat this as a main image
$_FILES['mediafile'] = $_FILES['thumbnail'];
unset($_FILES['thumbnail']);
}
// Thumbnail files must contain images.
if (!empty($_FILES['thumbnail']['name']) && !preg_match('/^image/', $_FILES['thumbnail']['type'])) {
FlashMessages::addMessage(I18N::translate('Thumbnail files must contain images.'));
break;
}
// User-specified filename?
if ($tag[0] == 'FILE' && $text[0]) {
$filename = $text[0];
}
// Use the name of the uploaded file?
// If no filename specified, use the name of the uploaded file?
if (!$filename && !empty($_FILES['mediafile']['name'])) {
$filename = $_FILES['mediafile']['name'];
}
// Validate the media path and filename
if (preg_match('/^https?:\/\//i', $text[0], $match)) {
// External media needs no further validation
$fileName = $filename;
$folderName = '';
unset($_FILES['mediafile'], $_FILES['thumbnail']);
} elseif (preg_match('/([\/\\\\<>])/', $filename, $match)) {
// Local media files cannot contain certain special characters
FlashMessages::addMessage(I18N::translate('Filenames are not allowed to contain the character “%s”.', $match[1]));
break;
} elseif (preg_match('/(\.(php|pl|cgi|bash|sh|bat|exe|com|htm|html|shtml))$/i', $filename, $match)) {
// Do not allow obvious script files.
FlashMessages::addMessage(I18N::translate('Filenames are not allowed to have the extension “%s”.', $match[1]));
break;
} elseif (!$filename) {
FlashMessages::addMessage(I18N::translate('No media file was provided.'));
break;
} else {
$fileName = $filename;
}
// Now copy the file to the correct location.
if (!empty($_FILES['mediafile']['name'])) {
$serverFileName = WT_DATA_DIR . $MEDIA_DIRECTORY . $folderName . $fileName;
if (file_exists($serverFileName)) {
FlashMessages::addMessage(I18N::translate('The file %s already exists. Use another filename.', $folderName . $fileName));
break;
}
if (move_uploaded_file($_FILES['mediafile']['tmp_name'], $serverFileName)) {
Log::addMediaLog('Media file ' . $serverFileName . ' uploaded');
} else {
FlashMessages::addMessage(
I18N::translate('There was an error uploading your file.') .
'<br>' .
Functions::fileUploadErrorText($_FILES['mediafile']['error'])
);
break;
}
// Now copy the (optional) thumbnail
if (!empty($_FILES['thumbnail']['name']) && preg_match('/^image\/(png|gif|jpeg)/', $_FILES['thumbnail']['type'], $match)) {
// Thumbnails have either
// (a) the same filename as the main image
// (b) the same filename as the main image - but with a .png extension
if ($match[1] == 'png' && !preg_match('/\.(png)$/i', $fileName)) {
$thumbFile = preg_replace('/\.[a-z0-9]{3,5}$/', '.png', $fileName);
} else {
$thumbFile = $fileName;
}
$serverFileName = WT_DATA_DIR . $MEDIA_DIRECTORY . 'thumbs/' . $folderName . $thumbFile;
if (move_uploaded_file($_FILES['thumbnail']['tmp_name'], $serverFileName)) {
Log::addMediaLog('Thumbnail file ' . $serverFileName . ' uploaded');
}
}
}
$controller->pageHeader();
// Build the gedcom record
$newged = "0 @new@ OBJE";
if ($tag[0] == 'FILE') {
// The admin has an edit field to change the filename
$text[0] = $folderName . $fileName;
} else {
// Users keep the original filename
$newged .= "\n1 FILE " . $folderName . $fileName;
}
$newged = FunctionsEdit::handleUpdates($newged);
$new_media = $WT_TREE->createRecord($newged);
if ($linktoid) {
$record = GedcomRecord::getInstance($linktoid, $WT_TREE);
$record->createFact('1 OBJE @' . $new_media->getXref() . '@', true);
Log::addEditLog('Media ID ' . $new_media->getXref() . " successfully added to $linktoid.");
$controller->addInlineJavascript('closePopupAndReloadParent();');
} else {
Log::addEditLog('Media ID ' . $new_media->getXref() . ' successfully added.');
$controller->addInlineJavascript('openerpasteid("' . $new_media->getXref() . '");');
}
echo '<button onclick="closePopupAndReloadParent();">', I18N::translate('close'), '</button>';
return;
case 'update': // Save the information from the “editmedia” action
$controller->setPageTitle(I18N::translate('Edit the media object'));
// Validate the media folder
$folderName = str_replace('\\', '/', $folder);
$folderName = trim($folderName, '/');
if ($folderName == '.') {
$folderName = '';
}
if ($folderName) {
$folderName .= '/';
// Not allowed to use “../”
if (strpos('/' . $folderName, '/../') !== false) {
FlashMessages::addMessage('Folder names are not allowed to include “../”');
break;
}
}
// Make sure the media folder exists
if (!is_dir(WT_DATA_DIR . $MEDIA_DIRECTORY)) {
if (File::mkdir(WT_DATA_DIR . $MEDIA_DIRECTORY)) {
FlashMessages::addMessage(I18N::translate('The folder %s has been created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY)));
} else {
FlashMessages::addMessage(I18N::translate('The folder %s does not exist, and it could not be created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY)), 'danger');
break;
}
}
// Managers can create new media paths (subfolders). Users must use existing folders.
if ($folderName && !is_dir(WT_DATA_DIR . $MEDIA_DIRECTORY . $folderName)) {
if (Auth::isManager($WT_TREE)) {
if (File::mkdir(WT_DATA_DIR . $MEDIA_DIRECTORY . $folderName)) {
FlashMessages::addMessage(I18N::translate('The folder %s has been created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY . $folderName)));
} else {
FlashMessages::addMessage(I18N::translate('The folder %s does not exist, and it could not be created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY . $folderName)), 'danger');
break;
}
} else {
// Regular users should not have seen this option - so no need for an error message.
break;
}
}
// The media folder exists. Now create a thumbnail folder to match it.
if (!is_dir(WT_DATA_DIR . $MEDIA_DIRECTORY . 'thumbs/' . $folderName)) {
if (!File::mkdir(WT_DATA_DIR . $MEDIA_DIRECTORY . 'thumbs/' . $folderName)) {
FlashMessages::addMessage(I18N::translate('The folder %s does not exist, and it could not be created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY . 'thumbs/' . $folderName)), 'danger');
break;
}
}
// Validate the media path and filename
if (preg_match('/^https?:\/\//i', $filename, $match)) {
// External media needs no further validation
$fileName = $filename;
$folderName = '';
unset($_FILES['mediafile'], $_FILES['thumbnail']);
} elseif (preg_match('/([\/\\\\<>])/', $filename, $match)) {
// Local media files cannot contain certain special characters
FlashMessages::addMessage(I18N::translate('Filenames are not allowed to contain the character “%s”.', $match[1]));
break;
} elseif (preg_match('/(\.(php|pl|cgi|bash|sh|bat|exe|com|htm|html|shtml))$/i', $filename, $match)) {
// Do not allow obvious script files.
FlashMessages::addMessage(I18N::translate('Filenames are not allowed to have the extension “%s”.', $match[1]));
break;
} elseif (!$filename) {
FlashMessages::addMessage(I18N::translate('No media file was provided.'));
break;
} else {
$fileName = $filename;
}
$oldFilename = $media->getFilename();
$newFilename = $folderName . $fileName;
// Cannot rename local to external or vice-versa
if (Functions::isFileExternal($oldFilename) != Functions::isFileExternal($filename)) {
FlashMessages::addMessage(I18N::translate('The media file %1$s could not be renamed to %2$s.', Html::filename($oldFilename), Html::filename($newFilename)));
break;
}
$messages = false;
$move_file = false;
// Move files on disk (if we can) to reflect the change to the GEDCOM data
if (!$media->isExternal()) {
$oldServerFile = $media->getServerFilename('main');
$oldServerThumb = $media->getServerFilename('thumb');
$newmedia = new Media("xxx", "0 @xxx@ OBJE\n1 FILE " . $newFilename, null, $WT_TREE);
$newServerFile = $newmedia->getServerFilename('main');
$newServerThumb = $newmedia->getServerFilename('thumb');
// We could be either renaming an existing file, or updating a record (with no valid file) to point to a new file
if ($oldServerFile !== $newServerFile) {
//-- check if the file is used in more than one gedcom
//-- do not allow it to be moved or renamed if it is
if (!$media->isExternal() && FunctionsDb::isMediaUsedInOtherTree($media->getFilename(), $WT_TREE->getTreeId())) {
FlashMessages::addMessage(I18N::translate('This file is linked to another family tree on this server. It cannot be deleted, moved, or renamed until these links have been removed.'));
break;
}
$move_file = true;
if (!file_exists($newServerFile) || md5_file($oldServerFile) === md5_file($newServerFile)) {
try {
rename($oldServerFile, $newServerFile);
FlashMessages::addMessage(I18N::translate('The media file %1$s has been renamed to %2$s.', Html::filename($oldFilename), Html::filename($newFilename)));
} catch (\ErrorException $ex) {
FlashMessages::addMessage(I18N::translate('The media file %1$s could not be renamed to %2$s.', Html::filename($oldFilename), Html::filename($newFilename)));
}
$messages = true;
}
if (!file_exists($newServerFile)) {
FlashMessages::addMessage(I18N::translate('The media file %s does not exist.', Html::filename($newFilename)));
$messages = true;
}
}
if ($oldServerThumb != $newServerThumb) {
$move_file = true;
if (!file_exists($newServerThumb) || md5_file($oldServerFile) == md5_file($newServerThumb)) {
try {
rename($oldServerThumb, $newServerThumb);
FlashMessages::addMessage(I18N::translate('The thumbnail file %1$s has been renamed to %2$s.', Html::filename($oldFilename), Html::filename($newFilename)));
} catch (\ErrorException $ex) {
FlashMessages::addMessage(I18N::translate('The thumbnail file %1$s could not be renamed to %2$s.', Html::filename($oldFilename), Html::filename($newFilename)));
}
$messages = true;
}
if (!file_exists($newServerThumb)) {
FlashMessages::addMessage(I18N::translate('The thumbnail file %s does not exist.', Html::filename($newFilename)));
$messages = true;
}
}
}
// Insert the 1 FILE xxx record into the arrays used by function FunctionsEdit::handle_updatesges()
$glevels = array_merge(array('1'), $glevels);
$tag = array_merge(array('FILE'), $tag);
$islink = array_merge(array(0), $islink);
$text = array_merge(array($newFilename), $text);
$record = GedcomRecord::getInstance($pid, $WT_TREE);
$newrec = "0 @$pid@ OBJE\n";
$newrec = FunctionsEdit::handleUpdates($newrec);
$record->updateRecord($newrec, $update_CHAN);
if ($move_file) {
// We've moved a file. Therefore we must approve the change, as rejecting
// the change will create broken references.
FunctionsImport::acceptAllChanges($record->getXref(), $record->getTree()->getTreeId());
}
if ($pid && $linktoid) {
$record = GedcomRecord::getInstance($linktoid, $WT_TREE);
$record->createFact('1 OBJE @' . $pid . '@', true);
Log::addEditLog('Media ID ' . $pid . " successfully added to $linktoid.");
}
$controller->pageHeader();
if ($messages) {
echo '<button onclick="closePopupAndReloadParent();">', I18N::translate('close'), '</button>';
} else {
$controller->addInlineJavascript('closePopupAndReloadParent();');
}
return;
case 'showmediaform':
$controller->setPageTitle(I18N::translate('Create a media object'));
$action = 'create';
break;
case 'editmedia':
$controller->setPageTitle(I18N::translate('Edit the media object'));
$action = 'update';
break;
default:
throw new \Exception('Bad $action (' . $action . ') in addmedia.php');
}
$controller->pageHeader();
echo '<div id="addmedia-page">'; //container for media edit pop-up
echo '<form method="post" name="newmedia" action="addmedia.php" enctype="multipart/form-data">';
echo '<input type="hidden" name="action" value="', $action, '">';
echo '<input type="hidden" name="ged" value="', $WT_TREE->getNameHtml(), '">';
echo '<input type="hidden" name="pid" value="', $pid, '">';
if ($linktoid) {
echo '<input type="hidden" name="linktoid" value="', $linktoid, '">';
}
echo '<table class="facts_table">';
echo '<tr><td class="topbottombar" colspan="2">';
echo $controller->getPageTitle(), FunctionsPrint::helpLink('OBJE');
echo '</td></tr>';
if (!$linktoid && $action == 'create') {
echo '<tr><td class="descriptionbox wrap width25">';
echo I18N::translate('Enter an individual, family, or source ID');
echo '</td><td class="optionbox wrap"><input type="text" data-autocomplete-type="IFS" name="linktoid" id="linktoid" size="6" value="">';
echo ' ', FunctionsPrint::printFindIndividualLink('linktoid');
echo ' ', FunctionsPrint::printFindFamilyLink('linktoid');
echo ' ', FunctionsPrint::printFindSourceLink('linktoid');
echo '<p class="small text-muted">', I18N::translate('Enter or search for the ID of the individual, family, or source to which this media object should be linked.'), '</p></td></tr>';
}
if ($media) {
$gedrec = $media->getGedcom();
} else {
$gedrec = '';
}
// 1 FILE
if (preg_match('/\n\d (FILE.*)/', $gedrec, $match)) {
$gedfile = $match[1];
} elseif ($filename) {
$gedfile = 'FILE ' . $filename;
} else {
$gedfile = 'FILE';
}
if ($gedfile == 'FILE') {
// Box for user to choose to upload file from local computer
echo '<tr><td class="descriptionbox wrap width25">';
echo I18N::translate('Media file to upload') . '</td><td class="optionbox wrap"><input type="file" name="mediafile" onchange="updateFormat(this.value);" size="40"></td></tr>';
// Check for thumbnail generation support
if (Auth::isManager($WT_TREE)) {
echo '<tr><td class="descriptionbox wrap width25">';
echo I18N::translate('Thumbnail to upload') . '</td><td class="optionbox wrap"><input type="file" name="thumbnail" size="40">';
echo '<p class="small text-muted">', I18N::translate('Choose the thumbnail image that you want to upload. Although thumbnails can be generated automatically for images, you may wish to generate your own thumbnail, especially for other media types. For example, you can provide a still image from a video, or a photograph of the individual who made an audio recording.'), '</p>';
echo '</td></tr>';
}
}
// Filename on server
$isExternal = Functions::isFileExternal($gedfile);
if ($gedfile == 'FILE') {
if (Auth::isManager($WT_TREE)) {
FunctionsEdit::addSimpleTag(
"1 $gedfile",
'',
I18N::translate('Filename on server'),
'<p class="small text-muted">' . I18N::translate('Do not change to keep original filename.') . '<br>' . I18N::translate('You may enter a URL, beginning with “http://”.') . '</p>'
);
}
$folder = '';
} else {
if ($isExternal) {
$fileName = substr($gedfile, 5);
$folder = '';
} else {
$tmp = substr($gedfile, 5);
$fileName = basename($tmp);
$folder = dirname($tmp);
if ($folder === '.') {
$folder = '';
}
}
echo '<tr>';
echo '<td class="descriptionbox wrap width25">';
echo I18N::translate('Filename on server');
echo '</td>';
echo '<td class="optionbox wrap wrap">';
if (Auth::isManager($WT_TREE)) {
echo '<input name="filename" type="text" value="' . Filter::escapeHtml($fileName) . '" size="40"';
if ($isExternal) {
echo '>';
} else {
echo '><p class="small text-muted">' . I18N::translate('Do not change to keep original filename.') . '</p>';
}
} else {
echo $fileName;
echo '<input name="filename" type="hidden" value="' . Filter::escapeHtml($fileName) . '" size="40">';
}
echo '</td>';
echo '</tr>';
}
// Box for user to choose the folder to store the image
if (!$isExternal) {
echo '<tr><td class="descriptionbox wrap width25">';
echo I18N::translate('Folder name on server'), '</td><td class="optionbox wrap">';
//-- dont let regular users change the location of media items
if ($action !== 'update' || Auth::isManager($WT_TREE)) {
$mediaFolders = QueryMedia::folderList();
echo '<select name="folder_list" onchange="document.newmedia.folder.value=this.options[this.selectedIndex].value;">';
echo '<option ';
if ($folder == '') {
echo 'selected';
}
echo ' value=""> ', I18N::translate('Choose: '), ' </option>';
if (Auth::isAdmin()) {
echo '<option value="other" disabled>', I18N::translate('Other folder… please type in'), "</option>";
}
foreach ($mediaFolders as $f) {
echo '<option value="', $f, '" ';
if ($folder == $f) {
echo 'selected';
}
echo '>', $f, "</option>";
}
echo '</select>';
} else {
echo $folder;
}
if (Auth::isAdmin()) {
echo '<br><input type="text" name="folder" size="40" value="', $folder, '">';
if ($gedfile === 'FILE') {
echo '<p class="small text-muted">', I18N::translate('This entry is ignored if you have entered a URL into the filename field.'), '</p>';
}
} else {
echo '<input name="folder" type="hidden" value="', Filter::escapeHtml($folder), '">';
}
echo '<p class="small text-muted">', I18N::translate('If you have a large number of media files, you can organize them into folders and subfolders.'), '</p>'; echo '</td></tr>';
} else {
echo '<input name="folder" type="hidden" value="">';
}
// 1 FILE / 2 FORM
if (preg_match('/\n(2 FORM .*)/', $gedrec, $match)) {
$gedform = $match[1];
} else {
$gedform = '2 FORM';
}
$formid = FunctionsEdit::addSimpleTag($gedform);
// automatically set the format field from the filename
$controller->addInlineJavascript('
function updateFormat(filename) {
var extsearch=/\.([a-zA-Z]{3,4})$/;
if (extsearch.exec(filename)) {
ext = RegExp.$1.toLowerCase();
if (ext=="jpg") ext="jpeg";
if (ext=="tif") ext="tiff";
} else {
ext = "";
}
formfield = document.getElementById("' . $formid . '");
formfield.value = ext;
}
');
// 1 FILE / 2 FORM / 3 TYPE
if (preg_match('/\n(3 TYPE .*)/', $gedrec, $match)) {
$gedtype = $match[1];
} else {
$gedtype = '3 TYPE photo'; // default to Photo
}
FunctionsEdit::addSimpleTag($gedtype);
// 1 FILE / 2 TITL
if (preg_match('/\n(2 TITL .*)/', $gedrec, $match)) {
$gedtitl = $match[1];
} else {
$gedtitl = '2 TITL';
}
FunctionsEdit::addSimpleTag($gedtitl);
// 1 FILE / 2 TITL / 3 _HEB
if (strstr($WT_TREE->getPreference('ADVANCED_NAME_FACTS'), '_HEB') !== false) {
if (preg_match('/\n(3 _HEB .*)/', $gedrec, $match)) {
$gedtitl = $match[1];
} else {
$gedtitl = '3 _HEB';
}
FunctionsEdit::addSimpleTag($gedtitl);
}
// 1 FILE / 2 TITL / 3 ROMN
if (strstr($WT_TREE->getPreference('ADVANCED_NAME_FACTS'), 'ROMN') !== false) {
if (preg_match('/\n(3 ROMN .*)/', $gedrec, $match)) {
$gedtitl = $match[1];
} else {
$gedtitl = '3 ROMN';
}
FunctionsEdit::addSimpleTag($gedtitl);
}
// 1 _PRIM
if (preg_match('/\n(1 _PRIM .*)/', $gedrec, $match)) {
$gedprim = $match[1];
} else {
$gedprim = '1 _PRIM';
}
FunctionsEdit::addSimpleTag($gedprim);
//-- print out editing fields for any other data in the media record
$sourceLevel = 0;
$sourceSOUR = '';
$sourcePAGE = '';
$sourceTEXT = '';
$sourceDATE = '';
$sourceQUAY = '';
if (!empty($gedrec)) {
preg_match_all('/\n(1 (?!FILE|FORM|TYPE|TITL|_PRIM|_THUM|CHAN|DATA).*(\n[2-9] .*)*)/', $gedrec, $matches);
foreach ($matches[1] as $subrec) {
$pieces = explode("\n", $subrec);
foreach ($pieces as $piece) {
$ft = preg_match("/(\d) (\w+)(.*)/", $piece, $match);
if ($ft == 0) {
continue;
}
$subLevel = $match[1];
$fact = trim($match[2]);
$event = trim($match[3]);
if ($fact === 'NOTE' || $fact === 'TEXT') {
$event .= Functions::getCont($subLevel + 1, $subrec);
}
if ($sourceSOUR !== '' && $subLevel <= $sourceLevel) {
// Get rid of all saved Source data
FunctionsEdit::addSimpleTag($sourceLevel . ' SOUR ' . $sourceSOUR);
FunctionsEdit::addSimpleTag(($sourceLevel + 1) . ' PAGE ' . $sourcePAGE);
FunctionsEdit::addSimpleTag(($sourceLevel + 2) . ' TEXT ' . $sourceTEXT);
FunctionsEdit::addSimpleTag(($sourceLevel + 2) . ' DATE ' . $sourceDATE, '', GedcomTag::getLabel('DATA:DATE'));
FunctionsEdit::addSimpleTag(($sourceLevel + 1) . ' QUAY ' . $sourceQUAY);
$sourceSOUR = '';
}
if ($fact === 'SOUR') {
$sourceLevel = $subLevel;
$sourceSOUR = $event;
$sourcePAGE = '';
$sourceTEXT = '';
$sourceDATE = '';
$sourceQUAY = '';
continue;
}
// Save all incoming data about this source reference
if ($sourceSOUR !== '') {
if ($fact === 'PAGE') {
$sourcePAGE = $event;
continue;
}
if ($fact === 'TEXT') {
$sourceTEXT = $event;
continue;
}
if ($fact === 'DATE') {
$sourceDATE = $event;
continue;
}
if ($fact === 'QUAY') {
$sourceQUAY = $event;
continue;
}
continue;
}
// Output anything that isnt part of a source reference
if (!empty($fact) && $fact !== 'CONC' && $fact !== 'CONT' && $fact !== 'DATA') {
FunctionsEdit::addSimpleTag($subLevel . ' ' . $fact . ' ' . $event);
}
}
}
if ($sourceSOUR !== '') {
// Get rid of all saved Source data
FunctionsEdit::addSimpleTag($sourceLevel . ' SOUR ' . $sourceSOUR);
FunctionsEdit::addSimpleTag(($sourceLevel + 1) . ' PAGE ' . $sourcePAGE);
FunctionsEdit::addSimpleTag(($sourceLevel + 2) . ' TEXT ' . $sourceTEXT);
FunctionsEdit::addSimpleTag(($sourceLevel + 2) . ' DATE ' . $sourceDATE, '', GedcomTag::getLabel('DATA:DATE'));
FunctionsEdit::addSimpleTag(($sourceLevel + 1) . ' QUAY ' . $sourceQUAY);
}
}
if (Auth::isAdmin() && $action === 'update') {
echo '<tr><td class="descriptionbox wrap width25">';
echo GedcomTag::getLabel('CHAN'), '</td><td class="optionbox wrap">';
if ($NO_UPDATE_CHAN) {
echo '<input type="checkbox" checked name="preserve_last_changed">';
} else {
echo '<input type="checkbox" name="preserve_last_changed">';
}
echo I18N::translate('Keep the existing “last change” information'), '<br>';
echo '</td></tr>';
}
echo '</table>';
FunctionsEdit::printAddLayer('SOUR', 1);
FunctionsEdit::printAddLayer('NOTE', 1);
FunctionsEdit::printAddLayer('SHARED_NOTE', 1);
FunctionsEdit::printAddLayer('RESN', 1);
?>
<p id="save-cancel">
<input type="submit" class="save" value="<?php echo I18N::translate('save'); ?>">
<input type="button" class="cancel" value="<?php echo I18N::translate('close'); ?>" onclick="window.close();">
</p>
</form>
</div>

View file

@ -1,913 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
/**
* Defined in session.php
*
* @global Tree $WT_TREE
*/
global $WT_TREE;
use Fisharebest\Webtrees\Controller\PageController;
use Fisharebest\Webtrees\Functions\Functions;
define('WT_SCRIPT_NAME', 'admin.php');
require './includes/session.php';
// This is a list of old files and directories, from earlier versions of webtrees, that can be deleted.
// It was generated with the help of a command like this:
// git diff 1.6.0..master --name-status | grep ^D
$old_files = array(
// Removed in 1.0.2
WT_ROOT . 'language/en.mo',
// Removed in 1.0.3
WT_ROOT . 'themechange.php',
// Removed in 1.0.4
// Removed in 1.0.5
// Removed in 1.0.6
WT_ROOT . 'includes/extras',
// Removed in 1.1.0
WT_ROOT . 'addremotelink.php',
WT_ROOT . 'addsearchlink.php',
WT_ROOT . 'client.php',
WT_ROOT . 'dir_editor.php',
WT_ROOT . 'editconfig_gedcom.php',
WT_ROOT . 'editgedcoms.php',
WT_ROOT . 'edit_merge.php',
WT_ROOT . 'genservice.php',
WT_ROOT . 'includes/classes',
WT_ROOT . 'includes/controllers',
WT_ROOT . 'includes/family_nav.php',
WT_ROOT . 'logs.php',
WT_ROOT . 'manageservers.php',
WT_ROOT . 'media.php',
WT_ROOT . 'module_admin.php',
//WT_ROOT.'modules', // Do not delete - users may have stored custom modules/data here
WT_ROOT . 'opensearch.php',
WT_ROOT . 'PEAR.php',
WT_ROOT . 'pgv_to_wt.php',
WT_ROOT . 'places',
//WT_ROOT.'robots.txt', // Do not delete this - it may contain user data
WT_ROOT . 'serviceClientTest.php',
WT_ROOT . 'siteconfig.php',
WT_ROOT . 'SOAP',
WT_ROOT . 'themes/clouds/mozilla.css',
WT_ROOT . 'themes/clouds/netscape.css',
WT_ROOT . 'themes/colors/mozilla.css',
WT_ROOT . 'themes/colors/netscape.css',
WT_ROOT . 'themes/fab/mozilla.css',
WT_ROOT . 'themes/fab/netscape.css',
WT_ROOT . 'themes/minimal/mozilla.css',
WT_ROOT . 'themes/minimal/netscape.css',
WT_ROOT . 'themes/webtrees/mozilla.css',
WT_ROOT . 'themes/webtrees/netscape.css',
WT_ROOT . 'themes/webtrees/style_rtl.css',
WT_ROOT . 'themes/xenea/mozilla.css',
WT_ROOT . 'themes/xenea/netscape.css',
WT_ROOT . 'uploadmedia.php',
WT_ROOT . 'useradmin.php',
WT_ROOT . 'webservice',
WT_ROOT . 'wtinfo.php',
// Removed in 1.1.1
// Removed in 1.1.2
WT_ROOT . 'treenav.php',
// Removed in 1.2.0
WT_ROOT . 'themes/clouds/jquery',
WT_ROOT . 'themes/colors/jquery',
WT_ROOT . 'themes/fab/jquery',
WT_ROOT . 'themes/minimal/jquery',
WT_ROOT . 'themes/webtrees/jquery',
WT_ROOT . 'themes/xenea/jquery',
// Removed in 1.2.1
// Removed in 1.2.2
WT_ROOT . 'themes/clouds/chrome.css',
WT_ROOT . 'themes/clouds/opera.css',
WT_ROOT . 'themes/clouds/print.css',
WT_ROOT . 'themes/clouds/style_rtl.css',
WT_ROOT . 'themes/colors/chrome.css',
WT_ROOT . 'themes/colors/opera.css',
WT_ROOT . 'themes/colors/print.css',
WT_ROOT . 'themes/colors/style_rtl.css',
WT_ROOT . 'themes/fab/chrome.css',
WT_ROOT . 'themes/fab/opera.css',
WT_ROOT . 'themes/minimal/chrome.css',
WT_ROOT . 'themes/minimal/opera.css',
WT_ROOT . 'themes/minimal/print.css',
WT_ROOT . 'themes/minimal/style_rtl.css',
WT_ROOT . 'themes/xenea/chrome.css',
WT_ROOT . 'themes/xenea/opera.css',
WT_ROOT . 'themes/xenea/print.css',
WT_ROOT . 'themes/xenea/style_rtl.css',
// Removed in 1.2.3
//WT_ROOT.'modules_v2', // Do not delete - users may have stored custom modules/data here
// Removed in 1.2.4
WT_ROOT . 'includes/cssparser.inc.php',
WT_ROOT . 'modules_v3/gedcom_favorites/help_text.php',
WT_ROOT . 'modules_v3/GEDFact_assistant/_MEDIA/media_3_find.php',
WT_ROOT . 'modules_v3/GEDFact_assistant/_MEDIA/media_3_search_add.php',
WT_ROOT . 'modules_v3/GEDFact_assistant/_MEDIA/media_5_input.js',
WT_ROOT . 'modules_v3/GEDFact_assistant/_MEDIA/media_5_input.php',
WT_ROOT . 'modules_v3/GEDFact_assistant/_MEDIA/media_7_parse_addLinksTbl.php',
WT_ROOT . 'modules_v3/GEDFact_assistant/_MEDIA/media_query_1a.php',
WT_ROOT . 'modules_v3/GEDFact_assistant/_MEDIA/media_query_2a.php',
WT_ROOT . 'modules_v3/GEDFact_assistant/_MEDIA/media_query_3a.php',
WT_ROOT . 'modules_v3/lightbox/css/album_page_RTL2.css',
WT_ROOT . 'modules_v3/lightbox/css/album_page_RTL.css',
WT_ROOT . 'modules_v3/lightbox/css/album_page_RTL_ff.css',
WT_ROOT . 'modules_v3/lightbox/css/clearbox_music.css',
WT_ROOT . 'modules_v3/lightbox/css/clearbox_music_RTL.css',
WT_ROOT . 'modules_v3/user_favorites/db_schema',
WT_ROOT . 'modules_v3/user_favorites/help_text.php',
WT_ROOT . 'search_engine.php',
WT_ROOT . 'themes/clouds/modules.css',
WT_ROOT . 'themes/colors/modules.css',
WT_ROOT . 'themes/fab/modules.css',
WT_ROOT . 'themes/minimal/modules.css',
WT_ROOT . 'themes/webtrees/modules.css',
WT_ROOT . 'themes/xenea/modules.css',
// Removed in 1.2.5
WT_ROOT . 'includes/media_reorder_count.php',
WT_ROOT . 'includes/media_tab_head.php',
WT_ROOT . 'modules_v3/clippings/index.php',
WT_ROOT . 'modules_v3/googlemap/css/googlemap_style.css',
WT_ROOT . 'modules_v3/googlemap/css/wt_v3_places_edit.css',
WT_ROOT . 'modules_v3/googlemap/index.php',
WT_ROOT . 'modules_v3/lightbox/index.php',
WT_ROOT . 'modules_v3/recent_changes/help_text.php',
WT_ROOT . 'modules_v3/todays_events/help_text.php',
WT_ROOT . 'sidebar.php',
// Removed in 1.2.6
WT_ROOT . 'modules_v3/sitemap/admin_index.php',
WT_ROOT . 'modules_v3/sitemap/help_text.php',
WT_ROOT . 'modules_v3/tree/css/styles',
WT_ROOT . 'modules_v3/tree/css/treebottom.gif',
WT_ROOT . 'modules_v3/tree/css/treebottomleft.gif',
WT_ROOT . 'modules_v3/tree/css/treebottomright.gif',
WT_ROOT . 'modules_v3/tree/css/tree.jpg',
WT_ROOT . 'modules_v3/tree/css/treeleft.gif',
WT_ROOT . 'modules_v3/tree/css/treeright.gif',
WT_ROOT . 'modules_v3/tree/css/treetop.gif',
WT_ROOT . 'modules_v3/tree/css/treetopleft.gif',
WT_ROOT . 'modules_v3/tree/css/treetopright.gif',
WT_ROOT . 'modules_v3/tree/css/treeview_print.css',
WT_ROOT . 'modules_v3/tree/help_text.php',
WT_ROOT . 'modules_v3/tree/images/print.png',
// Removed in 1.2.7
WT_ROOT . 'login_register.php',
WT_ROOT . 'modules_v3/top10_givnnames/help_text.php',
WT_ROOT . 'modules_v3/top10_surnames/help_text.php',
// Removed in 1.3.0
WT_ROOT . 'admin_site_ipaddress.php',
WT_ROOT . 'downloadgedcom.php',
WT_ROOT . 'export_gedcom.php',
WT_ROOT . 'gedcheck.php',
WT_ROOT . 'images',
WT_ROOT . 'includes/dmsounds_UTF8.php',
WT_ROOT . 'includes/grampsxml.rng',
WT_ROOT . 'includes/session_spider.php',
WT_ROOT . 'modules_v3/googlemap/admin_editconfig.php',
WT_ROOT . 'modules_v3/googlemap/admin_placecheck.php',
WT_ROOT . 'modules_v3/googlemap/flags.php',
WT_ROOT . 'modules_v3/googlemap/images/pedigree_map.gif',
WT_ROOT . 'modules_v3/googlemap/pedigree_map.php',
WT_ROOT . 'modules_v3/lightbox/admin_config.php',
WT_ROOT . 'modules_v3/lightbox/album.php',
WT_ROOT . 'modules_v3/tree/css/vline.jpg',
// Removed in 1.3.1
WT_ROOT . 'imageflush.php',
WT_ROOT . 'modules_v3/googlemap/wt_v3_pedigree_map.js.php',
WT_ROOT . 'modules_v3/lightbox/js/tip_balloon_RTL.js',
// Removed in 1.3.2
WT_ROOT . 'includes/set_gedcom_defaults.php',
WT_ROOT . 'modules_v3/address_report',
WT_ROOT . 'modules_v3/lightbox/functions/lb_horiz_sort.php',
WT_ROOT . 'modules_v3/random_media/help_text.php',
// Removed in 1.4.0
WT_ROOT . 'imageview.php',
WT_ROOT . 'media/MediaInfo.txt',
WT_ROOT . 'media/thumbs/ThumbsInfo.txt',
WT_ROOT . 'modules_v3/GEDFact_assistant/css/media_0_inverselink.css',
WT_ROOT . 'modules_v3/lightbox/help_text.php',
WT_ROOT . 'modules_v3/lightbox/images/blank.gif',
WT_ROOT . 'modules_v3/lightbox/images/close_1.gif',
WT_ROOT . 'modules_v3/lightbox/images/image_add.gif',
WT_ROOT . 'modules_v3/lightbox/images/image_copy.gif',
WT_ROOT . 'modules_v3/lightbox/images/image_delete.gif',
WT_ROOT . 'modules_v3/lightbox/images/image_edit.gif',
WT_ROOT . 'modules_v3/lightbox/images/image_link.gif',
WT_ROOT . 'modules_v3/lightbox/images/images.gif',
WT_ROOT . 'modules_v3/lightbox/images/image_view.gif',
WT_ROOT . 'modules_v3/lightbox/images/loading.gif',
WT_ROOT . 'modules_v3/lightbox/images/next.gif',
WT_ROOT . 'modules_v3/lightbox/images/nextlabel.gif',
WT_ROOT . 'modules_v3/lightbox/images/norm_2.gif',
WT_ROOT . 'modules_v3/lightbox/images/overlay.png',
WT_ROOT . 'modules_v3/lightbox/images/prev.gif',
WT_ROOT . 'modules_v3/lightbox/images/prevlabel.gif',
WT_ROOT . 'modules_v3/lightbox/images/private.gif',
WT_ROOT . 'modules_v3/lightbox/images/slideshow.jpg',
WT_ROOT . 'modules_v3/lightbox/images/transp80px.gif',
WT_ROOT . 'modules_v3/lightbox/images/zoom_1.gif',
WT_ROOT . 'modules_v3/lightbox/js',
WT_ROOT . 'modules_v3/lightbox/music',
WT_ROOT . 'modules_v3/lightbox/pic',
WT_ROOT . 'themes/_administration/jquery',
WT_ROOT . 'themes/webtrees/chrome.css',
// Removed in 1.4.1
WT_ROOT . 'modules_v3/lightbox/images/image_edit.png',
WT_ROOT . 'modules_v3/lightbox/images/image_view.png',
// Removed in 1.4.2
WT_ROOT . 'modules_v3/lightbox/images/image_view.png',
WT_ROOT . 'modules_v3/top10_pageviews/help_text.php',
WT_ROOT . 'themes/_administration/jquery-ui-1.10.0',
WT_ROOT . 'themes/clouds/jquery-ui-1.10.0',
WT_ROOT . 'themes/colors/jquery-ui-1.10.0',
WT_ROOT . 'themes/fab/jquery-ui-1.10.0',
WT_ROOT . 'themes/minimal/jquery-ui-1.10.0',
WT_ROOT . 'themes/webtrees/jquery-ui-1.10.0',
WT_ROOT . 'themes/xenea/jquery-ui-1.10.0',
// Removed in 1.5.0
WT_ROOT . 'includes/media_reorder.php',
WT_ROOT . 'includes/old_messages.php',
WT_ROOT . 'modules_v3/GEDFact_assistant/_CENS/census_note_decode.php',
WT_ROOT . 'modules_v3/GEDFact_assistant/_CENS/census_asst_date.php',
WT_ROOT . 'modules_v3/googlemap/wt_v3_googlemap.js.php',
WT_ROOT . 'modules_v3/lightbox/functions/lightbox_print_media.php',
WT_ROOT . 'modules_v3/upcoming_events/help_text.php',
WT_ROOT . 'modules_v3/stories/help_text.php',
WT_ROOT . 'modules_v3/user_messages/help_text.php',
WT_ROOT . 'themes/_administration/favicon.png',
WT_ROOT . 'themes/_administration/images',
WT_ROOT . 'themes/_administration/msie.css',
WT_ROOT . 'themes/_administration/style.css',
WT_ROOT . 'themes/clouds/favicon.png',
WT_ROOT . 'themes/clouds/images',
WT_ROOT . 'themes/clouds/msie.css',
WT_ROOT . 'themes/clouds/style.css',
WT_ROOT . 'themes/colors/css',
WT_ROOT . 'themes/colors/favicon.png',
WT_ROOT . 'themes/colors/images',
WT_ROOT . 'themes/colors/ipad.css',
WT_ROOT . 'themes/colors/msie.css',
WT_ROOT . 'themes/fab/favicon.png',
WT_ROOT . 'themes/fab/images',
WT_ROOT . 'themes/fab/msie.css',
WT_ROOT . 'themes/fab/style.css',
WT_ROOT . 'themes/minimal/favicon.png',
WT_ROOT . 'themes/minimal/images',
WT_ROOT . 'themes/minimal/msie.css',
WT_ROOT . 'themes/minimal/style.css',
WT_ROOT . 'themes/webtrees/favicon.png',
WT_ROOT . 'themes/webtrees/images',
WT_ROOT . 'themes/webtrees/msie.css',
WT_ROOT . 'themes/webtrees/style.css',
WT_ROOT . 'themes/xenea/favicon.png',
WT_ROOT . 'themes/xenea/images',
WT_ROOT . 'themes/xenea/msie.css',
WT_ROOT . 'themes/xenea/style.css',
// Removed in 1.5.1
WT_ROOT . 'themes/_administration/css-1.5.0',
WT_ROOT . 'themes/clouds/css-1.5.0',
WT_ROOT . 'themes/colors/css-1.5.0',
WT_ROOT . 'themes/fab/css-1.5.0',
WT_ROOT . 'themes/minimal/css-1.5.0',
WT_ROOT . 'themes/webtrees/css-1.5.0',
WT_ROOT . 'themes/xenea/css-1.5.0',
// Removed in 1.5.2
WT_ROOT . 'themes/_administration/css-1.5.1',
WT_ROOT . 'themes/clouds/css-1.5.1',
WT_ROOT . 'themes/colors/css-1.5.1',
WT_ROOT . 'themes/fab/css-1.5.1',
WT_ROOT . 'themes/minimal/css-1.5.1',
WT_ROOT . 'themes/webtrees/css-1.5.1',
WT_ROOT . 'themes/xenea/css-1.5.1',
// Removed in 1.5.3
WT_ROOT . 'modules_v3/GEDFact_assistant/_CENS/census_asst_help.php',
WT_ROOT . 'modules_v3/googlemap/admin_places.php',
WT_ROOT . 'modules_v3/googlemap/defaultconfig.php',
WT_ROOT . 'modules_v3/googlemap/googlemap.php',
WT_ROOT . 'modules_v3/googlemap/placehierarchy.php',
WT_ROOT . 'modules_v3/googlemap/places_edit.php',
WT_ROOT . 'modules_v3/googlemap/util.js',
WT_ROOT . 'modules_v3/googlemap/wt_v3_places_edit.js.php',
WT_ROOT . 'modules_v3/googlemap/wt_v3_places_edit_overlays.js.php',
WT_ROOT . 'modules_v3/googlemap/wt_v3_street_view.php',
WT_ROOT . 'readme.html',
WT_ROOT . 'themes/_administration/css-1.5.2',
WT_ROOT . 'themes/clouds/css-1.5.2',
WT_ROOT . 'themes/colors/css-1.5.2',
WT_ROOT . 'themes/fab/css-1.5.2',
WT_ROOT . 'themes/minimal/css-1.5.2',
WT_ROOT . 'themes/webtrees/css-1.5.2',
WT_ROOT . 'themes/xenea/css-1.5.2',
// Removed in 1.6.0
WT_ROOT . 'downloadbackup.php',
WT_ROOT . 'modules_v3/ckeditor/ckeditor-4.3.2-custom',
WT_ROOT . 'site-php-version.php',
WT_ROOT . 'themes/_administration/css-1.5.3',
WT_ROOT . 'themes/clouds/css-1.5.3',
WT_ROOT . 'themes/colors/css-1.5.3',
WT_ROOT . 'themes/fab/css-1.5.3',
WT_ROOT . 'themes/minimal/css-1.5.3',
WT_ROOT . 'themes/webtrees/css-1.5.3',
WT_ROOT . 'themes/xenea/css-1.5.3',
// Removed in 1.6.1
WT_ROOT . 'includes/authentication.php',
// Removed in 1.6.2
WT_ROOT . 'themes/_administration/css-1.6.0',
WT_ROOT . 'themes/_administration/jquery-ui-1.10.3',
WT_ROOT . 'themes/clouds/css-1.6.0',
WT_ROOT . 'themes/clouds/jquery-ui-1.10.3',
WT_ROOT . 'themes/colors/css-1.6.0',
WT_ROOT . 'themes/colors/jquery-ui-1.10.3',
WT_ROOT . 'themes/fab/css-1.6.0',
WT_ROOT . 'themes/fab/jquery-ui-1.10.3',
WT_ROOT . 'themes/minimal/css-1.6.0',
WT_ROOT . 'themes/minimal/jquery-ui-1.10.3',
WT_ROOT . 'themes/webtrees/css-1.6.0',
WT_ROOT . 'themes/webtrees/jquery-ui-1.10.3',
WT_ROOT . 'themes/xenea/css-1.6.0',
WT_ROOT . 'themes/xenea/jquery-ui-1.10.3',
WT_ROOT . 'themes/_administration/css-1.6.0',
WT_ROOT . 'themes/_administration/jquery-ui-1.10.3',
// Removed in 1.7.0
WT_ROOT . 'admin_site_other.php',
WT_ROOT . 'includes/config_data.php',
WT_ROOT . 'includes/db_schema',
WT_ROOT . 'includes/fonts',
WT_ROOT . 'includes/functions',
WT_ROOT . 'includes/hitcount.php',
WT_ROOT . 'includes/reportheader.php',
WT_ROOT . 'includes/specialchars.php',
WT_ROOT . 'js',
WT_ROOT . 'language/en_GB.mo', // Replaced with en-GB.mo
WT_ROOT . 'language/en_US.mo', // Replaced with en-US.mo
WT_ROOT . 'language/pt_BR.mo', // Replaced with pt-BR.mo
WT_ROOT . 'language/zh_CN.mo', // Replaced with zh-Hans.mo
WT_ROOT . 'language/extra',
WT_ROOT . 'library',
WT_ROOT . 'modules_v3/batch_update/admin_batch_update.php',
WT_ROOT . 'modules_v3/batch_update/plugins',
WT_ROOT . 'modules_v3/charts/help_text.php',
WT_ROOT . 'modules_v3/ckeditor/ckeditor-4.4.1-custom',
WT_ROOT . 'modules_v3/clippings/clippings_ctrl.php',
WT_ROOT . 'modules_v3/clippings/help_text.php',
WT_ROOT . 'modules_v3/faq/help_text.php',
WT_ROOT . 'modules_v3/gedcom_favorites/db_schema',
WT_ROOT . 'modules_v3/gedcom_news/db_schema',
WT_ROOT . 'modules_v3/googlemap/db_schema',
WT_ROOT . 'modules_v3/googlemap/help_text.php',
WT_ROOT . 'modules_v3/html/help_text.php',
WT_ROOT . 'modules_v3/logged_in/help_text.php',
WT_ROOT . 'modules_v3/review_changes/help_text.php',
WT_ROOT . 'modules_v3/todo/help_text.php',
WT_ROOT . 'modules_v3/tree/class_treeview.php',
WT_ROOT . 'modules_v3/user_blog/db_schema',
WT_ROOT . 'modules_v3/yahrzeit/help_text.php',
WT_ROOT . 'save.php',
WT_ROOT . 'themes/_administration/css-1.6.2',
WT_ROOT . 'themes/_administration/templates',
WT_ROOT . 'themes/_administration/header.php',
WT_ROOT . 'themes/_administration/footer.php',
WT_ROOT . 'themes/clouds/css-1.6.2',
WT_ROOT . 'themes/clouds/templates',
WT_ROOT . 'themes/clouds/header.php',
WT_ROOT . 'themes/clouds/footer.php',
WT_ROOT . 'themes/colors/css-1.6.2',
WT_ROOT . 'themes/colors/templates',
WT_ROOT . 'themes/colors/header.php',
WT_ROOT . 'themes/colors/footer.php',
WT_ROOT . 'themes/fab/css-1.6.2',
WT_ROOT . 'themes/fab/templates',
WT_ROOT . 'themes/fab/header.php',
WT_ROOT . 'themes/fab/footer.php',
WT_ROOT . 'themes/minimal/css-1.6.2',
WT_ROOT . 'themes/minimal/templates',
WT_ROOT . 'themes/minimal/header.php',
WT_ROOT . 'themes/minimal/footer.php',
WT_ROOT . 'themes/webtrees/css-1.6.2',
WT_ROOT . 'themes/webtrees/templates',
WT_ROOT . 'themes/webtrees/header.php',
WT_ROOT . 'themes/webtrees/footer.php',
WT_ROOT . 'themes/xenea/css-1.6.2',
WT_ROOT . 'themes/xenea/templates',
WT_ROOT . 'themes/xenea/header.php',
WT_ROOT . 'themes/xenea/footer.php',
// Removed in 1.7.2
WT_ROOT . 'assets/js-1.7.0',
WT_ROOT . 'packages/bootstrap-3.3.4',
WT_ROOT . 'packages/bootstrap-datetimepicker-4.0.0',
WT_ROOT . 'packages/ckeditor-4.4.7-custom',
WT_ROOT . 'packages/font-awesome-4.3.0',
WT_ROOT . 'packages/jquery-1.11.2',
WT_ROOT . 'packages/jquery-2.1.3',
WT_ROOT . 'packages/moment-2.10.3',
// Removed in 1.7.3
WT_ROOT . 'includes/php_53_compatibility.php',
WT_ROOT . 'modules_v3/GEDFact_assistant/census/date.js',
WT_ROOT . 'modules_v3/GEDFact_assistant/census/dynamicoptionlist.js',
WT_ROOT . 'packages/jquery-cookie-1.4.1/jquery.cookie.js',
// Removed in 1.7.4
WT_ROOT . 'assets/js-1.7.2',
WT_ROOT . 'themes/_administration/css-1.7.0',
WT_ROOT . 'themes/clouds/css-1.7.0',
WT_ROOT . 'themes/colors/css-1.7.0',
WT_ROOT . 'themes/fab/css-1.7.0',
WT_ROOT . 'themes/minimal/css-1.7.0',
WT_ROOT . 'themes/webtrees/css-1.7.0',
WT_ROOT . 'themes/xenea/css-1.7.0',
WT_ROOT . 'packages/bootstrap-3.3.5',
WT_ROOT . 'packages/bootstrap-datetimepicker-4.15.35',
WT_ROOT . 'packages/jquery-1.11.3',
WT_ROOT . 'packages/jquery-2.1.4',
WT_ROOT . 'packages/moment-2.10.6',
// Removed in 1.7.5
WT_ROOT . 'themes/_administration/css-1.7.4',
WT_ROOT . 'themes/clouds/css-1.7.4',
WT_ROOT . 'themes/colors/css-1.7.4',
WT_ROOT . 'themes/fab/css-1.7.4',
WT_ROOT . 'themes/minimal/css-1.7.4',
WT_ROOT . 'themes/webtrees/css-1.7.4',
WT_ROOT . 'themes/xenea/css-1.7.4',
// Removed in 1.7.7
WT_ROOT . 'assets/js-1.7.4',
WT_ROOT . 'modules_v3/googlemap/images/css_sprite_facts.png',
WT_ROOT . 'modules_v3/googlemap/images/flag_shadow.png',
WT_ROOT . 'modules_v3/googlemap/images/shadow-left-large.png',
WT_ROOT . 'modules_v3/googlemap/images/shadow-left-small.png',
WT_ROOT . 'modules_v3/googlemap/images/shadow-right-large.png',
WT_ROOT . 'modules_v3/googlemap/images/shadow-right-small.png',
WT_ROOT . 'modules_v3/googlemap/images/shadow50.png',
WT_ROOT . 'modules_v3/googlemap/images/transparent-left-large.png',
WT_ROOT . 'modules_v3/googlemap/images/transparent-left-small.png',
WT_ROOT . 'modules_v3/googlemap/images/transparent-right-large.png',
WT_ROOT . 'modules_v3/googlemap/images/transparent-right-small.png',
// Removed in 1.7.8
WT_ROOT . 'themes/clouds/css-1.7.5',
WT_ROOT . 'themes/colors/css-1.7.5',
WT_ROOT . 'themes/fab/css-1.7.5',
WT_ROOT . 'themes/minimal/css-1.7.5',
WT_ROOT . 'themes/webtrees/css-1.7.5',
WT_ROOT . 'themes/xenea/css-1.7.5',
// Removed in 1.7.9
WT_ROOT . 'assets/js-1.7.7',
);
// Delete old files (if we can).
$files_to_delete = array();
foreach ($old_files as $file) {
if (file_exists($file) && !File::delete($file)) {
$files_to_delete[] = $file;
}
}
$controller = new PageController;
$controller
->restrictAccess(Auth::isManager($WT_TREE))
->setPageTitle(I18N::translate('Control panel') . ' — ' . /* I18N: A summary of the system status */ I18N::translate('Dashboard'))
->pageHeader();
// Check for updates
$latest_version_txt = Functions::fetchLatestVersion();
if (preg_match('/^[0-9.]+\|[0-9.]+\|/', $latest_version_txt)) {
list($latest_version) = explode('|', $latest_version_txt);
} else {
// Cannot determine the latest version
$latest_version = '';
}
$update_available = Auth::isAdmin() && $latest_version && version_compare(WT_VERSION, $latest_version) < 0;
// Total number of users
$total_users = User::count();
// Administrators
$administrators = Database::prepare(
"SELECT SQL_CACHE user_id, real_name FROM `##user` JOIN `##user_setting` USING (user_id) WHERE setting_name='canadmin' AND setting_value='1'"
)->fetchAll();
// Managers
$managers = Database::prepare(
"SELECT SQL_CACHE user_id, real_name FROM `##user` JOIN `##user_gedcom_setting` USING (user_id)" .
" WHERE setting_name = 'canedit' AND setting_value='admin'" .
" GROUP BY user_id, real_name" .
" ORDER BY real_name"
)->fetchAll();
// Moderators
$moderators = Database::prepare(
"SELECT SQL_CACHE user_id, real_name FROM `##user` JOIN `##user_gedcom_setting` USING (user_id)" .
" WHERE setting_name = 'canedit' AND setting_value='accept'" .
" GROUP BY user_id, real_name" .
" ORDER BY real_name"
)->fetchAll();
// Number of users who have not verified their email address
$unverified = Database::prepare(
"SELECT SQL_CACHE user_id, real_name FROM `##user` JOIN `##user_setting` USING (user_id)" .
" WHERE setting_name = 'verified' AND setting_value = '0'" .
" ORDER BY real_name"
)->fetchAll();
// Number of users whose accounts are not approved by an administrator
$unapproved = Database::prepare(
"SELECT SQL_CACHE user_id, real_name FROM `##user` JOIN `##user_setting` USING (user_id)" .
" WHERE setting_name = 'verified_by_admin' AND setting_value = '0'" .
" ORDER BY real_name"
)->fetchAll();
// Users currently logged in
$logged_in = Database::prepare(
"SELECT SQL_NO_CACHE DISTINCT user_id, real_name FROM `##user` JOIN `##session` USING (user_id)" .
" ORDER BY real_name"
)->fetchAll();
// Count of records
$individuals = Database::prepare(
"SELECT SQL_CACHE gedcom_id, COUNT(i_id) AS count FROM `##gedcom` LEFT JOIN `##individuals` ON gedcom_id = i_file GROUP BY gedcom_id"
)->fetchAssoc();
$families = Database::prepare(
"SELECT SQL_CACHE gedcom_id, COUNT(f_id) AS count FROM `##gedcom` LEFT JOIN `##families` ON gedcom_id = f_file GROUP BY gedcom_id"
)->fetchAssoc();
$sources = Database::prepare(
"SELECT SQL_CACHE gedcom_id, COUNT(s_id) AS count FROM `##gedcom` LEFT JOIN `##sources` ON gedcom_id = s_file GROUP BY gedcom_id"
)->fetchAssoc();
$media = Database::prepare(
"SELECT SQL_CACHE gedcom_id, COUNT(m_id) AS count FROM `##gedcom` LEFT JOIN `##media` ON gedcom_id = m_file GROUP BY gedcom_id"
)->fetchAssoc();
$repositories = Database::prepare(
"SELECT SQL_CACHE gedcom_id, COUNT(o_id) AS count FROM `##gedcom` LEFT JOIN `##other` ON gedcom_id = o_file AND o_type = 'REPO' GROUP BY gedcom_id"
)->fetchAssoc();
$changes = Database::prepare(
"SELECT SQL_CACHE g.gedcom_id, COUNT(change_id) AS count FROM `##gedcom` AS g LEFT JOIN `##change` AS c ON g.gedcom_id = c.gedcom_id AND status = 'pending' GROUP BY g.gedcom_id"
)->fetchAssoc();
// Server warnings
// Note that security support for 5.6 ends after security support for 7.0
$server_warnings = array();
if (
PHP_VERSION_ID < 50500 ||
PHP_VERSION_ID < 50600 && date('Y-m-d') >= '2016-07-10' ||
PHP_VERSION_ID < 70000 && date('Y-m-d') >= '2018-12-31' ||
PHP_VERSION_ID >= 70000 && PHP_VERSION_ID < 70100 && date('Y-m-d') >= '2018-12-03'
) {
$server_warnings[] =
I18N::translate('Your web server is using PHP version %s, which is no longer receiving security updates. You should upgrade to a later version as soon as possible.', PHP_VERSION) .
'<br><a href="https://php.net/supported-versions.php">https://php.net/supported-versions.php</a>';
} elseif (
PHP_VERSION_ID < 50600 ||
PHP_VERSION_ID < 70000 && date('Y-m-d') >= '2016-12-31' ||
PHP_VERSION_ID < 70100 && date('Y-m-d') >= '2017-12-03'
) {
$server_warnings[] =
I18N::translate('Your web server is using PHP version %s, which is no longer maintained. You should upgrade to a later version.', PHP_VERSION) .
'<br><a href="https://php.net/supported-versions.php">https://php.net/supported-versions.php</a>';
}
?>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<p>
<?php echo I18N::translate('These pages provide access to all the preferences and management tools for this webtrees site.'); ?>
</p>
<div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
<!-- SERVER WARNINGS -->
<?php if ($server_warnings): ?>
<div class="panel panel-danger">
<div class="panel-heading" role="tab" id="server-heading">
<h2 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#server-panel" aria-expanded="true" aria-controls="server-panel">
<?php echo I18N::translate('Server information'); ?>
</a>
</h2>
</div>
<div id="server-panel" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="server-heading">
<div class="panel-body">
<?php foreach ($server_warnings as $server_warning): ?>
<p>
<?php echo $server_warning; ?>
</p>
<?php endforeach; ?>
</div>
</div>
</div>
<?php endif; ?>
<!-- WEBTREES VERSION -->
<div class="panel <?php echo Auth::isAdmin() && $update_available ? 'panel-danger' : 'panel-primary'; ?>">
<div class="panel-heading" role="tab" id="webtrees-version-heading">
<h2 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#webtrees-version-panel" aria-expanded="true" aria-controls="webtrees-version-panel">
<?php echo WT_WEBTREES, ' ', WT_VERSION; ?>
</a>
</h2>
</div>
<div id="webtrees-version-panel" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="webtrees-version-heading">
<div class="panel-body">
<p>
<?php echo /* I18N: %s is a URL/link to the project website */ I18N::translate('Support and documentation can be found at %s.', '<a href="https://webtrees.net/">webtrees.net</a>'); ?>
</p>
<?php if (Auth::isAdmin()): ?>
<p>
<?php if ($latest_version === ''): ?>
<?php echo I18N::translate('No upgrade information is available.'); ?>
<?php elseif ($update_available): ?>
<?php echo I18N::translate('A new version of webtrees is available.'); ?>
<a href="admin_site_upgrade.php" class="error">
<?php echo /* I18N: %s is a version number */ I18N::translate('Upgrade to webtrees %s.', Filter::escapeHtml($latest_version)); ?>
</a>
<?php else: ?>
<?php echo I18N::translate('This is the latest version of webtrees. No upgrade is available.'); ?>
<?php endif; ?>
</p>
<?php endif; ?>
</div>
</div>
</div>
<!-- USERS -->
<?php if (Auth::isAdmin()): ?>
<div class="panel <?php echo $unapproved || $unverified ? 'panel-danger' : 'panel-primary'; ?>">
<div class="panel-heading" role="tab" id="users-heading">
<h2 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#users-panel" aria-expanded="false" aria-controls="users-panel">
<?php echo I18N::translate('Users'); ?>
</a>
</h2>
</div>
<div id="users-panel" class="panel-collapse collapse" role="tabpanel" aria-labelledby="users-heading">
<div class="panel-body">
<table class="table table-condensed">
<caption class="sr-only">
<?php echo I18N::translate('Users'); ?>
</caption>
<tbody>
<tr>
<th class="col-xs-3">
<?php echo I18N::translate('Total number of users'); ?>
</th>
<td class="col-xs-9">
<a href="admin_users.php">
<?php echo I18N::number($total_users); ?>
</a>
</td>
</tr>
<tr>
<th>
<?php echo I18N::translate('Administrators'); ?>
</th>
<td>
<?php foreach ($administrators as $n => $user): ?>
<?php echo $n ? I18N::$list_separator : ''; ?>
<a href="admin_users.php?action=edit&user_id=<?php echo $user->user_id; ?>" dir="auto">
<?php echo Filter::escapeHtml($user->real_name); ?>
</a>
<?php endforeach; ?>
</td>
</tr>
<tr>
<th>
<?php echo I18N::translate('Managers'); ?>
</th>
<td>
<?php foreach ($managers as $n => $user): ?>
<?php echo $n ? I18N::$list_separator : ''; ?>
<a href="admin_users.php?action=edit&user_id=<?php echo $user->user_id; ?>" dir="auto">
<?php echo Filter::escapeHtml($user->real_name); ?>
</a>
<?php endforeach; ?>
</td>
</tr>
<tr>
<th>
<?php echo I18N::translate('Moderators'); ?>
</th>
<td>
<?php foreach ($moderators as $n => $user): ?>
<?php echo $n ? I18N::$list_separator : ''; ?>
<a href="admin_users.php?action=edit&user_id=<?php echo $user->user_id; ?>" dir="auto">
<?php echo Filter::escapeHtml($user->real_name); ?>
</a>
<?php endforeach; ?>
</td>
</tr>
<tr class="<?php echo $unverified ? 'danger' : ''; ?>">
<th>
<?php echo I18N::translate('Not verified by the user'); ?>
</th>
<td>
<?php foreach ($unverified as $n => $user): ?>
<?php echo $n ? I18N::$list_separator : ''; ?>
<a href="admin_users.php?action=edit&user_id=<?php echo $user->user_id; ?>" dir="auto">
<?php echo Filter::escapeHtml($user->real_name); ?>
</a>
<?php endforeach; ?>
</td>
</tr>
<tr class="<?php echo $unapproved ? 'danger' : ''; ?>">
<th>
<?php echo I18N::translate('Not approved by an administrator'); ?>
</th>
<td>
<?php foreach ($unapproved as $n => $user): ?>
<?php echo $n ? I18N::$list_separator : ''; ?>
<a href="admin_users.php?action=edit&user_id=<?php echo $user->user_id; ?>" dir="auto">
<?php echo Filter::escapeHtml($user->real_name); ?>
</a>
<?php endforeach; ?>
</td>
</tr>
<tr>
<th>
<?php echo I18N::translate('Users who are signed in'); ?>
</th>
<td>
<?php foreach ($logged_in as $n => $user): ?>
<?php echo $n ? I18N::$list_separator : ''; ?>
<a href="admin_users.php?action=edit&user_id=<?php echo $user->user_id; ?>" dir="auto">
<?php echo Filter::escapeHtml($user->real_name); ?>
</a>
<?php endforeach; ?>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<?php endif; ?>
<!-- FAMILY TREES -->
<div class="panel <?php echo array_sum($changes) ? 'panel-danger' : 'panel-primary'; ?>">
<div class="panel-heading" role="tab" id="trees-heading">
<h2 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#trees-panel" aria-expanded="false" aria-controls="trees-panel">
<?php echo I18N::translate('Family trees'); ?>
</a>
</h2>
</div>
<div id="trees-panel" class="panel-collapse collapse" role="tabpanel" aria-labelledby="trees-heading">
<div class="panel-body">
<table class="table table-condensed">
<caption class="sr-only">
<?php echo I18N::translate('Family trees'); ?>
</caption>
<thead>
<tr>
<th class="col-xs-5"><?php echo I18N::translate('Family tree'); ?></th>
<th class="col-xs-2 text-right flip"><?php echo I18N::translate('Pending changes'); ?></th>
<th class="col-xs-1 text-right flip"><?php echo I18N::translate('Individuals'); ?></th>
<th class="col-xs-1 text-right flip"><?php echo I18N::translate('Families'); ?></th>
<th class="col-xs-1 text-right flip"><?php echo I18N::translate('Sources'); ?></th>
<th class="col-xs-1 text-right flip"><?php echo I18N::translate('Repositories'); ?></th>
<th class="col-xs-1 text-right flip"><?php echo I18N::translate('Media'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach (Tree::getAll() as $tree): ?>
<tr class="<?php echo $changes[$tree->getTreeId()] ? 'danger' : ''; ?>">
<td>
<a href="index.php?ctype=gedcom&amp;ged=<?php echo $tree->getNameUrl(); ?>">
<?php echo $tree->getNameHtml(); ?>
-
<?php echo $tree->getTitleHtml(); ?>
</a>
</td>
<td class="text-right flip">
<?php if ($changes[$tree->getTreeId()]): ?>
<a onclick="window.open('edit_changes.php', '_blank', chan_window_specs); return false;" href="#">
<?php echo I18N::number($changes[$tree->getTreeId()]); ?>
<span class="sr-only"><?php echo I18N::translate('Pending changes'); ?> <?php echo $tree->getTitleHtml(); ?></span>
</a>
<?php else: ?>
-
<?php endif; ?>
</td>
<td class="text-right flip">
<?php if ($individuals[$tree->getTreeId()]): ?>
<a href="indilist.php?ged=<?php echo $tree->getNameUrl(); ?>">
<?php echo I18N::number($individuals[$tree->getTreeId()]); ?>
<span class="sr-only"><?php echo I18N::translate('Individuals'); ?> <?php echo $tree->getTitleHtml(); ?></span>
</a>
<?php else: ?>
-
<?php endif; ?>
</td>
<td class="text-right flip">
<?php if ($families[$tree->getTreeId()]): ?>
<a href="famlist.php?ged=<?php echo $tree->getNameUrl(); ?>">
<?php echo I18N::number($families[$tree->getTreeId()]); ?>
<span class="sr-only"><?php echo I18N::translate('Families'); ?> <?php echo $tree->getTitleHtml(); ?></span>
</a>
<?php else: ?>
-
<?php endif; ?>
</td>
<td class="text-right flip">
<?php if ($sources[$tree->getTreeId()]): ?>
<a href="sourcelist.php?ged=<?php echo $tree->getNameUrl(); ?>">
<?php echo I18N::number($sources[$tree->getTreeId()]); ?>
<span class="sr-only"><?php echo I18N::translate('Sources'); ?> <?php echo $tree->getTitleHtml(); ?></span>
</a>
<?php else: ?>
-
<?php endif; ?>
</td>
<td class="text-right flip">
<?php if ($repositories[$tree->getTreeId()]): ?>
<a href="repolist.php?ged=<?php echo $tree->getNameUrl(); ?>">
<?php echo I18N::number($repositories[$tree->getTreeId()]); ?>
<span class="sr-only"><?php echo I18N::translate('Repositories'); ?> <?php echo $tree->getTitleHtml(); ?></span>
</a>
<?php else: ?>
-
<?php endif; ?>
</td>
<td class="text-right flip">
<?php if ($media[$tree->getTreeId()]): ?>
<a href="medialist.php?ged=<?php echo $tree->getNameUrl(); ?>">
<?php echo I18N::number($media[$tree->getTreeId()]); ?>
<span class="sr-only"><?php echo I18N::translate('Media objects'); ?> <?php echo $tree->getTitleHtml(); ?></span>
</a>
<?php else: ?>
-
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
<tfoot>
<tr>
<td>
<?php echo I18N::translate('Total'); ?>
-
<?php echo I18N::plural('%s family tree', '%s family trees', count(Tree::getAll()), I18N::number(count(Tree::getAll()))); ?>
</td>
<td class="text-right flip">
<?php echo I18N::number(array_sum($changes)); ?>
</td>
<td class="text-right flip">
<?php echo I18N::number(array_sum($individuals)); ?>
</td>
<td class="text-right flip">
<?php echo I18N::number(array_sum($families)); ?>
</td>
<td class="text-right flip">
<?php echo I18N::number(array_sum($sources)); ?>
</td>
<td class="text-right flip">
<?php echo I18N::number(array_sum($repositories)); ?>
</td>
<td class="text-right flip">
<?php echo I18N::number(array_sum($media)); ?>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
<!-- OLD FILES -->
<?php if (Auth::isAdmin() && $files_to_delete): ?>
<div class="panel panel-danger">
<div class="panel-heading" role="tab" id="old-files-heading">
<h2 class="panel-title">
<a class="collapsed" data-toggle="collapse" data-parent="#accordion" href="#old-files-panel" aria-expanded="false" aria-controls="old-files-panel">
<?php echo I18N::translate('Old files found'); ?>
</a>
</h2>
</div>
<div id="old-files-panel" class="panel-collapse collapse" role="tabpanel" aria-labelledby="old-files-heading">
<div class="panel-body">
<p>
<?php echo I18N::translate('Files have been found from a previous version of webtrees. Old files can sometimes be a security risk. You should delete them.'); ?>
</p>
<ul class="list-unstyled">
<?php foreach ($files_to_delete as $file_to_delete): ?>
<li dir="ltr"><code><?php echo Filter::escapeHtml($file_to_delete); ?></code></li>
<?php endforeach ?>
</ul>
</div>
</div>
</div>
<?php endif; ?>
</div>

View file

@ -1,698 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
use Fisharebest\Webtrees\Controller\AjaxController;
use Fisharebest\Webtrees\Controller\PageController;
use Fisharebest\Webtrees\Functions\FunctionsEdit;
define('WT_SCRIPT_NAME', 'admin_media.php');
require './includes/session.php';
// type of file/object to include
$files = Filter::get('files', 'local|external|unused', 'local');
// family tree setting MEDIA_DIRECTORY
$media_folders = all_media_folders();
$media_folder = Filter::get('media_folder', null, ''); // MySQL needs an empty string, not NULL
// User folders may contain special characters. Restrict to actual folders.
if (!array_key_exists($media_folder, $media_folders)) {
$media_folder = reset($media_folders);
}
// prefix to filename
$media_paths = media_paths($media_folder);
$media_path = Filter::get('media_path', null, ''); // MySQL needs an empty string, not NULL
// User paths may contain special characters. Restrict to actual paths.
if (!array_key_exists($media_path, $media_paths)) {
$media_path = reset($media_paths);
}
// subfolders within $media_path
$subfolders = Filter::get('subfolders', 'include|exclude', 'include');
$action = Filter::get('action');
////////////////////////////////////////////////////////////////////////////////
// POST callback for file deletion
////////////////////////////////////////////////////////////////////////////////
$delete_file = Filter::post('delete');
if ($delete_file) {
$controller = new AjaxController;
// Only delete valid (i.e. unused) media files
$media_folder = Filter::post('media_folder', null, ''); // MySQL needs an empty string, not NULL
$disk_files = all_disk_files($media_folder, '', 'include', '');
if (in_array($delete_file, $disk_files)) {
$tmp = WT_DATA_DIR . $media_folder . $delete_file;
try {
unlink($tmp);
FlashMessages::addMessage(I18N::translate('The file %s has been deleted.', Html::filename($tmp)), 'success');
} catch (\ErrorException $ex) {
FlashMessages::addMessage(I18N::translate('The file %s could not be deleted.', Html::filename($tmp)) . '<hr><samp dir="ltr">' . $ex->getMessage() . '</samp>', 'danger');
}
// Delete any corresponding thumbnail
$tmp = WT_DATA_DIR . $media_folder . 'thumbs/' . $delete_file;
if (file_exists($tmp)) {
try {
unlink($tmp);
FlashMessages::addMessage(I18N::translate('The file %s has been deleted.', Html::filename($tmp)), 'success');
} catch (\ErrorException $ex) {
FlashMessages::addMessage(I18N::translate('The file %s could not be deleted.', Html::filename($tmp)) . '<hr><samp dir="ltr">' . $ex->getMessage() . '</samp>', 'danger');
}
}
} else {
// File no longer exists? Maybe it was already deleted or renamed.
}
$controller->pageHeader();
return;
}
////////////////////////////////////////////////////////////////////////////////
// GET callback for server-side pagination
////////////////////////////////////////////////////////////////////////////////
switch ($action) {
case 'load_json':
$search = Filter::get('search');
$search = $search['value'];
$start = Filter::getInteger('start');
$length = Filter::getInteger('length');
switch ($files) {
case 'local':
// Filtered rows
$SELECT1 =
"SELECT SQL_CACHE SQL_CALC_FOUND_ROWS TRIM(LEADING :media_path_1 FROM m_filename) AS media_path, m_id AS xref, m_titl, m_file AS gedcom_id, m_gedcom AS gedcom" .
" FROM `##media`" .
" JOIN `##gedcom_setting` ON (m_file = gedcom_id AND setting_name = 'MEDIA_DIRECTORY')" .
" JOIN `##gedcom` USING (gedcom_id)" .
" WHERE setting_value = :media_folder" .
" AND m_filename LIKE CONCAT(:media_path_2, '%')" .
" AND (SUBSTRING_INDEX(m_filename, '/', -1) LIKE CONCAT('%', :search_1, '%')" .
" OR m_titl LIKE CONCAT('%', :search_2, '%'))" .
" AND m_filename NOT LIKE 'http://%'" .
" AND m_filename NOT LIKE 'https://%'";
$ARGS1 = array(
'media_path_1' => $media_path,
'media_folder' => $media_folder,
'media_path_2' => Filter::escapeLike($media_path),
'search_1' => Filter::escapeLike($search),
'search_2' => Filter::escapeLike($search),
);
// Unfiltered rows
$SELECT2 =
"SELECT SQL_CACHE COUNT(*)" .
" FROM `##media`" .
" JOIN `##gedcom_setting` ON (m_file = gedcom_id AND setting_name = 'MEDIA_DIRECTORY')" .
" WHERE setting_value = :media_folder" .
" AND m_filename LIKE CONCAT(:media_path_3, '%')" .
" AND m_filename NOT LIKE 'http://%'" .
" AND m_filename NOT LIKE 'https://%'";
$ARGS2 = array(
'media_folder' => $media_folder,
'media_path_3' => $media_path,
);
if ($subfolders == 'exclude') {
$SELECT1 .= " AND m_filename NOT LIKE CONCAT(:media_path_4, '%/%')";
$ARGS1['media_path_4'] = Filter::escapeLike($media_path);
$SELECT2 .= " AND m_filename NOT LIKE CONCAT(:media_path_4, '%/%')";
$ARGS2['media_path_4'] = Filter::escapeLike($media_path);
}
$order = Filter::getArray('order');
$SELECT1 .= " ORDER BY ";
if ($order) {
foreach ($order as $key => $value) {
if ($key > 0) {
$SELECT1 .= ',';
}
// Datatables numbers columns 0, 1, 2
// MySQL numbers columns 1, 2, 3
switch ($value['dir']) {
case 'asc':
$SELECT1 .= ":col_" . $key . " ASC";
break;
case 'desc':
$SELECT1 .= ":col_" . $key . " DESC";
break;
}
$ARGS1['col_' . $key] = 1 + $value['column'];
}
} else {
$SELECT1 = " 1 ASC";
}
if ($length > 0) {
$SELECT1 .= " LIMIT :length OFFSET :start";
$ARGS1['length'] = $length;
$ARGS1['start'] = $start;
}
$rows = Database::prepare($SELECT1)->execute($ARGS1)->fetchAll();
// Total filtered/unfiltered rows
$recordsFiltered = Database::prepare("SELECT FOUND_ROWS()")->fetchOne();
$recordsTotal = Database::prepare($SELECT2)->execute($ARGS2)->fetchOne();
$data = array();
foreach ($rows as $row) {
$media = Media::getInstance($row->xref, Tree::findById($row->gedcom_id), $row->gedcom);
$data[] = array(
mediaFileInfo($media_folder, $media_path, $row->media_path),
$media->displayImage(),
mediaObjectInfo($media),
);
}
break;
case 'external':
// Filtered rows
$SELECT1 =
"SELECT SQL_CACHE SQL_CALC_FOUND_ROWS m_filename, m_id AS xref, m_titl, m_file AS gedcom_id, m_gedcom AS gedcom" .
" FROM `##media`" .
" WHERE (m_filename LIKE 'http://%' OR m_filename LIKE 'https://%')" .
" AND (m_filename LIKE CONCAT('%', :search_1, '%') OR m_titl LIKE CONCAT('%', :search_2, '%'))";
$ARGS1 = array(
'search_1' => Filter::escapeLike($search),
'search_2' => Filter::escapeLike($search),
);
// Unfiltered rows
$SELECT2 =
"SELECT SQL_CACHE COUNT(*)" .
" FROM `##media`" .
" WHERE (m_filename LIKE 'http://%' OR m_filename LIKE 'https://%')";
$ARGS2 = array();
$order = Filter::getArray('order');
$SELECT1 .= " ORDER BY ";
if ($order) {
foreach ($order as $key => $value) {
if ($key > 0) {
$SELECT1 .= ',';
}
// Datatables numbers columns 0, 1, 2
// MySQL numbers columns 1, 2, 3
switch ($value['dir']) {
case 'asc':
$SELECT1 .= ":col_" . $key . " ASC";
break;
case 'desc':
$SELECT1 .= ":col_" . $key . " DESC";
break;
}
$ARGS1['col_' . $key] = 1 + $value['column'];
}
} else {
$SELECT1 = " 1 ASC";
}
if ($length > 0) {
$SELECT1 .= " LIMIT :length OFFSET :start";
$ARGS1['length'] = $length;
$ARGS1['start'] = $start;
}
$rows = Database::prepare($SELECT1)->execute($ARGS1)->fetchAll();
// Total filtered/unfiltered rows
$recordsFiltered = Database::prepare("SELECT FOUND_ROWS()")->fetchOne();
$recordsTotal = Database::prepare($SELECT2)->execute($ARGS2)->fetchOne();
$data = array();
foreach ($rows as $row) {
$media = Media::getInstance($row->xref, Tree::findById($row->gedcom_id), $row->gedcom);
$data[] = array(
GedcomTag::getLabelValue('URL', $row->m_filename),
$media->displayImage(),
mediaObjectInfo($media),
);
}
break;
case 'unused':
// Which trees use this media folder?
$media_trees = Database::prepare(
"SELECT gedcom_name, gedcom_name" .
" FROM `##gedcom`" .
" JOIN `##gedcom_setting` USING (gedcom_id)" .
" WHERE setting_name='MEDIA_DIRECTORY' AND setting_value = :media_folder AND gedcom_id > 0"
)->execute(array(
'media_folder' => $media_folder,
))->fetchAssoc();
$disk_files = all_disk_files($media_folder, $media_path, $subfolders, $search);
$db_files = all_media_files($media_folder, $media_path, $subfolders, $search);
// All unused files
$unused_files = array_diff($disk_files, $db_files);
$recordsTotal = count($unused_files);
// Filter unused files
if ($search) {
$unused_files = array_filter($unused_files, function ($x) use ($search) { return strpos($x, $search) !== false; });
}
$recordsFiltered = count($unused_files);
// Sort files - only option is column 0
sort($unused_files);
$order = Filter::get('order');
if ($order && $order[0]['dir'] === 'desc') {
$unused_files = array_reverse($unused_files);
}
// Paginate unused files
$unused_files = array_slice($unused_files, $start, $length);
$data = array();
foreach ($unused_files as $unused_file) {
$full_path = WT_DATA_DIR . $media_folder . $media_path . $unused_file;
$thumb_path = WT_DATA_DIR . $media_folder . 'thumbs/' . $media_path . $unused_file;
if (!file_exists($thumb_path)) {
$thumb_path = $full_path;
}
try {
$imgsize = getimagesize($thumb_path);
// We cant create a URL (not in public_html) or use the media firewall (no such object)
// so just the base64-encoded image inline.
if ($imgsize === false) {
// not an image
$img = '-';
} else {
$img = '<img src="data:' . $imgsize['mime'] . ';base64,' . base64_encode(file_get_contents($thumb_path)) . '" class="thumbnail" ' . $imgsize[3] . '" style="max-width:100px;height:auto;">';
}
} catch (\ErrorException $ex) {
// Not an image, or not a valid image?
$img = '-';
}
// Is there a pending record for this file?
$exists_pending = Database::prepare(
"SELECT 1 FROM `##change` WHERE status='pending' AND new_gedcom LIKE CONCAT('%\n1 FILE ', :unused_file, '\n%')"
)->execute(array(
'unused_file' => Filter::escapeLike($unused_file),
))->fetchOne();
// Form to create new media object in each tree
$create_form = '';
if (!$exists_pending) {
foreach ($media_trees as $media_tree) {
$create_form .=
'<p><a href="" onclick="window.open(\'addmedia.php?action=showmediaform&amp;ged=' . rawurlencode($media_tree) . '&amp;filename=' . rawurlencode($unused_file) . '\', \'_blank\', edit_window_specs); return false;">' . I18N::translate('Create') . '</a> — ' . Filter::escapeHtml($media_tree) . '<p>';
}
}
$conf = I18N::translate('Are you sure you want to delete “%s”?', Filter::escapeJs($unused_file));
$delete_link =
'<p><a onclick="if (confirm(\'' . Filter::escapeJs($conf) . '\')) jQuery.post(\'admin_media.php\',{delete:\'' . Filter::escapeJs($media_path . $unused_file) . '\',media_folder:\'' . Filter::escapeJs($media_folder) . '\'},function(){location.reload();})" href="#">' . I18N::translate('Delete') . '</a></p>';
$data[] = array(
mediaFileInfo($media_folder, $media_path, $unused_file) . $delete_link,
$img,
$create_form,
);
}
break;
default:
throw new \DomainException('Invalid action');
}
header('Content-type: application/json');
// See http://www.datatables.net/usage/server-side
echo json_encode(array(
'draw' => Filter::getInteger('draw'), // String, but always an integer
'recordsTotal' => $recordsTotal,
'recordsFiltered' => $recordsFiltered,
'data' => $data,
));
return;
}
/**
* A unique list of media folders, from all trees.
*
* @return string[]
*/
function all_media_folders() {
return Database::prepare(
"SELECT SQL_CACHE setting_value, setting_value" .
" FROM `##gedcom_setting`" .
" WHERE setting_name='MEDIA_DIRECTORY' AND gedcom_id > 0" .
" GROUP BY 1" .
" ORDER BY 1"
)->execute(array())->fetchAssoc();
}
/**
* Generate a list of media paths (within a media folder) used by all media objects.
*
* @param string $media_folder
*
* @return string[]
*/
function media_paths($media_folder) {
$media_paths = Database::prepare(
"SELECT SQL_CACHE LEFT(m_filename, CHAR_LENGTH(m_filename) - CHAR_LENGTH(SUBSTRING_INDEX(m_filename, '/', -1))) AS media_path" .
" FROM `##media`" .
" JOIN `##gedcom_setting` ON (m_file = gedcom_id AND setting_name = 'MEDIA_DIRECTORY')" .
" WHERE setting_value = :media_folder" .
" AND m_filename NOT LIKE 'http://%'" .
" AND m_filename NOT LIKE 'https://%'" .
" GROUP BY 1" .
" ORDER BY 1"
)->execute(array(
'media_folder' => $media_folder,
))->fetchOneColumn();
if (!$media_paths || reset($media_paths) != '') {
// Always include a (possibly empty) top-level folder
array_unshift($media_paths, '');
}
return array_combine($media_paths, $media_paths);
}
/**
* Search a folder (and optional subfolders) for filenames that match a search pattern.
*
* @param string $dir
* @param bool $recursive
* @param string $filter
*
* @return string[]
*/
function scan_dirs($dir, $recursive, $filter) {
$files = array();
// $dir comes from the database. The actual folder may not exist.
if (is_dir($dir)) {
foreach (scandir($dir) as $path) {
if (is_dir($dir . $path)) {
// What if there are user-defined subfolders “thumbs” or “watermarks”?
if ($path != '.' && $path != '..' && $path != 'thumbs' && $path != 'watermark' && $recursive) {
foreach (scan_dirs($dir . $path . '/', $recursive, $filter) as $subpath) {
$files[] = $path . '/' . $subpath;
}
}
} elseif (!$filter || stripos($path, $filter) !== false) {
$files[] = $path;
}
}
}
return $files;
}
/**
* Fetch a list of all files on disk
*
* @param string $media_folder Location of root folder
* @param string $media_path Any subfolder
* @param string $subfolders Include or exclude subfolders
* @param string $filter Filter files whose name contains this test
*
* @return string[]
*/
function all_disk_files($media_folder, $media_path, $subfolders, $filter) {
return scan_dirs(WT_DATA_DIR . $media_folder . $media_path, $subfolders == 'include', $filter);
}
/**
* Fetch a list of all files on in the database.
*
* The subfolders parameter is not implemented. However, as we
* currently use this function as an exclusion list, it is harmless
* to always include sub-folders.
*
* @param string $media_folder
* @param string $media_path
* @param string $subfolders
* @param string $filter
*
* @return string[]
*/
function all_media_files($media_folder, $media_path, $subfolders, $filter) {
return Database::prepare(
"SELECT SQL_CACHE SQL_CALC_FOUND_ROWS TRIM(LEADING :media_path_1 FROM m_filename) AS media_path, 'OBJE' AS type, m_titl, m_id AS xref, m_file AS ged_id, m_gedcom AS gedrec, m_filename" .
" FROM `##media`" .
" JOIN `##gedcom_setting` ON (m_file = gedcom_id AND setting_name = 'MEDIA_DIRECTORY')" .
" JOIN `##gedcom` USING (gedcom_id)" .
" WHERE setting_value = :media_folder" .
" AND m_filename LIKE CONCAT(:media_path_2, '%')" .
" AND (SUBSTRING_INDEX(m_filename, '/', -1) LIKE CONCAT('%', :filter_1, '%')" .
" OR m_titl LIKE CONCAT('%', :filter_2, '%'))" .
" AND m_filename NOT LIKE 'http://%'" .
" AND m_filename NOT LIKE 'https://%'"
)->execute(array(
'media_path_1' => $media_path,
'media_folder' => $media_folder,
'media_path_2' => Filter::escapeLike($media_path),
'filter_1' => Filter::escapeLike($filter),
'filter_2' => Filter::escapeLike($filter),
))->fetchOneColumn();
}
/**
* Generate some useful information and links about a media file.
*
* @param string $media_folder
* @param string $media_path
* @param string $file
*
* @return string
*/
function mediaFileInfo($media_folder, $media_path, $file) {
$html = '<dl>';
$html .= '<dt>' . I18N::translate('Filename') . '</dt>';
$html .= '<dd>' . Filter::escapeHtml($file) . '</dd>';
$full_path = WT_DATA_DIR . $media_folder . $media_path . $file;
if ($file && file_exists($full_path)) {
try {
$size = filesize($full_path);
$size = (int) (($size + 1023) / 1024); // Round up to next KB
$size = /* I18N: size of file in KB */ I18N::translate('%s KB', I18N::number($size));
$html .= '<dt>' . I18N::translate('File size') . '</dt>';
$html .= '<dd>' . $size . '</dd>';
try {
$imgsize = getimagesize($full_path);
$html .= '<dt>' . I18N::translate('Image dimensions') . '</dt>';
$html .= '<dd>' . /* I18N: image dimensions, width × height */
I18N::translate('%1$s × %2$s pixels', I18N::number($imgsize['0']), I18N::number($imgsize['1'])) . '</dd>';
} catch (\ErrorException $ex) {
// Not an image, or not a valid image?
}
$html .= '</dl>';
} catch (\ErrorException $ex) {
$html .= '</dl>';
$html .= '<div class="alert alert-danger">' . I18N::translate('This media file exists, but cannot be accessed.') . '</div>';
}
} else {
$html .= '</dl>';
$html .= '<div class="alert alert-danger">' . I18N::translate('This media file does not exist.') . '</div>';
}
return $html;
}
/**
* Generate some useful information and links about a media object.
*
* @param Media $media
*
* @return string HTML
*/
function mediaObjectInfo(Media $media) {
$xref = $media->getXref();
$gedcom = $media->getTree()->getName();
$html =
'<div class="btn-group"><button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><i class="fa fa-pencil"></i> <span class="caret"></span></button><ul class="dropdown-menu" role="menu">' .
'<li><a href="#" onclick="window.open(\'addmedia.php?action=editmedia&amp;pid=' . $xref . '&ged=' . Filter::escapeJs($gedcom) . '\', \'_blank\', edit_window_specs);"><i class="fa fa-fw fa-pencil"></i> ' . I18N::translate('Edit') . '</a></li>' .
'<li><a href="#" onclick="return delete_record(\'' . I18N::translate('Are you sure you want to delete “%s”?', Filter::escapeJs(Filter::unescapeHtml($media->getFullName()))) . '\', \'' . $media->getXref() . '\', \'' . Filter::escapeJs($gedcom) . '\');"><i class="fa fa-fw fa-trash-o"></i> ' . I18N::translate('Delete') . '</a></li>' .
'<li><a href="#" onclick="return ilinkitem(\'' . $media->getXref() . '\', \'person\', WT_GEDCOM)"><i class="fa fa-fw fa-link"></i> ' . I18N::translate('Link this media object to an individual') . '</a></li>' .
'<li><a href="#" onclick="return ilinkitem(\'' . $media->getXref() . '\', \'family\', WT_GEDCOM)"><i class="fa fa-fw fa-link"></i> ' . I18N::translate('Link this media object to a family') . '</a></li>' .
'<li><a href="#" onclick="return ilinkitem(\'' . $media->getXref() . '\', \'source\', WT_GEDCOM)"><i class="fa fa-fw fa-link"></i> ' . I18N::translate('Link this media object to a source') . '</a></li>';
if (Module::getModuleByName('GEDFact_assistant')) {
$html .= '<li><a href="#" onclick="return ilinkitem(\'' . $media->getXref() . '\', \'manage\', WT_GEDCOM)"><i class="fa fa-fw fa-link"></i> ' . I18N::translate('Manage the links') . '</a></li>';
}
$html .=
'</ul></div> ' .
'<b><a href="' . $media->getHtmlUrl() . '">' . $media->getFullName() . '</a></b>' .
'<div><i>' . Filter::escapeHtml($media->getNote()) . '</i></div>';
$html .= '<br>';
$linked = array();
foreach ($media->linkedIndividuals('OBJE') as $link) {
$linked[] = '<a href="' . $link->getHtmlUrl() . '">' . $link->getFullName() . '</a>';
}
foreach ($media->linkedFamilies('OBJE') as $link) {
$linked[] = '<a href="' . $link->getHtmlUrl() . '">' . $link->getFullName() . '</a>';
}
foreach ($media->linkedSources('OBJE') as $link) {
$linked[] = '<a href="' . $link->getHtmlUrl() . '">' . $link->getFullName() . '</a>';
}
foreach ($media->linkedNotes('OBJE') as $link) {
// Invalid GEDCOM - you cannot link a NOTE to an OBJE
$linked[] = '<a href="' . $link->getHtmlUrl() . '">' . $link->getFullName() . '</a>';
}
foreach ($media->linkedRepositories('OBJE') as $link) {
// Invalid GEDCOM - you cannot link a REPO to an OBJE
$linked[] = '<a href="' . $link->getHtmlUrl() . '">' . $link->getFullName() . '</a>';
}
if ($linked) {
$html .= '<ul>';
foreach ($linked as $link) {
$html .= '<li>' . $link . '</li>';
}
$html .= '</ul>';
} else {
$html .= '<div class="alert alert-danger">' . I18N::translate('This media object is not linked to any other record.') . '</div>';
}
return $html;
}
////////////////////////////////////////////////////////////////////////////////
// Start here
////////////////////////////////////////////////////////////////////////////////
// Preserve the pagination/filtering/sorting between requests, so that the
// browsers back button works. Pagination is dependent on the currently
// selected folder.
$table_id = md5($files . $media_folder . $media_path . $subfolders);
$controller = new PageController;
$controller
->restrictAccess(Auth::isAdmin())
->setPageTitle(I18N::translate('Manage media'))
->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
->addExternalJavascript(WT_DATATABLES_BOOTSTRAP_JS_URL)
->pageHeader()
->addInlineJavascript('
jQuery("#media-table-' . $table_id . '").dataTable({
processing: true,
serverSide: true,
ajax: "' . WT_BASE_URL . WT_SCRIPT_NAME . '?action=load_json&files=' . $files . '&media_folder=' . $media_folder . '&media_path=' . $media_path . '&subfolders=' . $subfolders . '",
' . I18N::datatablesI18N(array(5, 10, 20, 50, 100, 500, 1000, -1)) . ',
autoWidth:false,
pageLength: 10,
pagingType: "full_numbers",
stateSave: true,
stateDuration: 300,
columns: [
{},
{ sortable: false },
{ sortable: ' . ($files === 'unused' ? 'false' : 'true') . ' }
]
});
');
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li class="active"><?php echo $controller->getPageTitle(); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<form>
<table class="table table-bordered table-condensed">
<thead>
<tr>
<th><?php echo I18N::translate('Media files'); ?></th>
<th><?php echo I18N::translate('Media folders'); ?></th>
</tr>
</thead>
<tbody>
<tr>
<td>
<label>
<input type="radio" name="files" value="local" <?php echo $files === 'local' ? 'checked' : ''; ?> onchange="this.form.submit();">
<?php echo /* I18N: “Local files” are stored on this computer */ I18N::translate('Local files'); ?>
</label>
<br>
<label>
<input type="radio" name="files" value="external" <?php echo $files === 'external' ? 'checked' : ''; ?> onchange="this.form.submit();">
<?php echo /* I18N: “External files” are stored on other computers */ I18N::translate('External files'); ?>
</label>
<br>
<label>
<input type="radio" name="files" value="unused" <?php echo $files === 'unused' ? 'checked' : ''; ?> onchange="this.form.submit();">
<?php echo I18N::translate('Unused files'); ?>
</label>
</td>
<td>
<?php if ($files === 'local' || $files === 'unused'): ?>
<div dir="ltr">
<?php if (count($media_folders) > 1): ?>
<?php echo WT_DATA_DIR, FunctionsEdit::selectEditControl('media_folder', $media_folders, null, $media_folder, 'onchange="this.form.submit();"'); ?>
<?php else: ?>
<?php echo WT_DATA_DIR, Filter::escapeHtml($media_folder); ?>
<input type="hidden" name="media_folder" value="<?php echo Filter::escapeHtml($media_folder); ?>">
<?php endif; ?>
</div>
<?php if (count($media_paths) > 1): ?>
<?php echo FunctionsEdit::selectEditControl('media_path', $media_paths, null, $media_path, 'onchange="this.form.submit();"'); ?>
<?php else: ?>
<?php echo Filter::escapeHtml($media_path); ?>
<input type="hidden" name="media_path" value="<?php echo Filter::escapeHtml($media_path); ?>">
<?php endif; ?>
<label>
<input type="radio" name="subfolders" value="include" <?php echo $subfolders === 'include' ? 'checked' : ''; ?> onchange="this.form.submit();">
<?php echo I18N::translate('Include subfolders'); ?>
</label>
<br>
<label>
<input type="radio" name="subfolders" value="exclude" <?php echo $subfolders === 'exclude' ? ' checked' : ''; ?> onchange="this.form.submit();">
<?php echo I18N::translate('Exclude subfolders'); ?>
</label>
<?php elseif ($files === 'external'): ?>
<?php echo I18N::translate('External media files have a URL instead of a filename.'); ?>
<input type="hidden" name="media_folder" value="<?php echo Filter::escapeHtml($media_folder); ?>">
<input type="hidden" name="media_path" value="<?php echo Filter::escapeHtml($media_path); ?>">
<?php endif; ?>
</td>
</tr>
</tbody>
</table>
</form>
<br>
<br>
<table class="table table-bordered table-condensed" id="media-table-<?php echo $table_id; ?>">
<thead>
<tr>
<th><?php echo I18N::translate('Media file'); ?></th>
<th><?php echo I18N::translate('Media'); ?></th>
<th><?php echo I18N::translate('Media object'); ?></th>
</tr>
</thead>
<tbody>
</tbody>
</table>

View file

@ -1,264 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
/**
* Defined in session.php
*
* @global Tree $WT_TREE
*/
global $WT_TREE;
use Fisharebest\Webtrees\Controller\PageController;
use Fisharebest\Webtrees\Functions\Functions;
use Fisharebest\Webtrees\Query\QueryMedia;
define('WT_SCRIPT_NAME', 'admin_media_upload.php');
require './includes/session.php';
$MEDIA_DIRECTORY = $WT_TREE->getPreference('MEDIA_DIRECTORY');
$controller = new PageController;
$controller
->restrictAccess(Auth::isManager($WT_TREE))
->setPageTitle(I18N::translate('Upload media files'));
$action = Filter::post('action');
if ($action == "upload") {
for ($i = 1; $i < 6; $i++) {
if (!empty($_FILES['mediafile' . $i]["name"]) || !empty($_FILES['thumbnail' . $i]["name"])) {
$folder = Filter::post('folder' . $i);
// Validate the media folder
$folderName = str_replace('\\', '/', $folder);
$folderName = trim($folderName, '/');
if ($folderName == '.') {
$folderName = '';
}
if ($folderName) {
$folderName .= '/';
// Not allowed to use “../”
if (strpos('/' . $folderName, '/../') !== false) {
FlashMessages::addMessage('Folder names are not allowed to include “../”');
break;
}
}
// Make sure the media folder exists
if (!is_dir(WT_DATA_DIR . $MEDIA_DIRECTORY)) {
if (File::mkdir(WT_DATA_DIR . $MEDIA_DIRECTORY)) {
FlashMessages::addMessage(I18N::translate('The folder %s has been created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY)));
} else {
FlashMessages::addMessage(I18N::translate('The folder %s does not exist, and it could not be created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY)), 'danger');
break;
}
}
// Managers can create new media paths (subfolders). Users must use existing folders.
if ($folderName && !is_dir(WT_DATA_DIR . $MEDIA_DIRECTORY . $folderName)) {
if (Auth::isManager($WT_TREE)) {
if (File::mkdir(WT_DATA_DIR . $MEDIA_DIRECTORY . $folderName)) {
FlashMessages::addMessage(I18N::translate('The folder %s has been created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY . $folderName)));
} else {
FlashMessages::addMessage(I18N::translate('The folder %s does not exist, and it could not be created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY . $folderName)), 'danger');
break;
}
} else {
// Regular users should not have seen this option - so no need for an error message.
break;
}
}
// The media folder exists. Now create a thumbnail folder to match it.
if (!is_dir(WT_DATA_DIR . $MEDIA_DIRECTORY . 'thumbs/' . $folderName)) {
if (!File::mkdir(WT_DATA_DIR . $MEDIA_DIRECTORY . 'thumbs/' . $folderName)) {
FlashMessages::addMessage(I18N::translate('The folder %s does not exist, and it could not be created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY . 'thumbs/' . $folderName)));
break;
}
}
// A thumbnail file with no main image?
if (!empty($_FILES['thumbnail' . $i]['name']) && empty($_FILES['mediafile' . $i]['name'])) {
// Assume the user used the wrong field, and treat this as a main image
$_FILES['mediafile' . $i] = $_FILES['thumbnail' . $i];
unset($_FILES['thumbnail' . $i]);
}
// Thumbnail files must contain images.
if (!empty($_FILES['thumbnail' . $i]['name']) && !preg_match('/^image\/(png|gif|jpeg)/', $_FILES['thumbnail' . $i]['type'])) {
FlashMessages::addMessage(I18N::translate('Thumbnail files must contain images.'));
break;
}
// User-specified filename?
$filename = Filter::post('filename' . $i);
// Use the name of the uploaded file?
if (!$filename && !empty($_FILES['mediafile' . $i]['name'])) {
$filename = $_FILES['mediafile' . $i]['name'];
}
// Validate the media path and filename
if (preg_match('/([\/\\\\<>])/', $filename, $match)) {
// Local media files cannot contain certain special characters
FlashMessages::addMessage(I18N::translate('Filenames are not allowed to contain the character “%s”.', $match[1]));
$filename = '';
break;
} elseif (preg_match('/(\.(php|pl|cgi|bash|sh|bat|exe|com|htm|html|shtml))$/i', $filename, $match)) {
// Do not allow obvious script files.
FlashMessages::addMessage(I18N::translate('Filenames are not allowed to have the extension “%s”.', $match[1]));
$filename = '';
break;
} elseif (!$filename) {
FlashMessages::addMessage(I18N::translate('No media file was provided.'));
break;
} else {
$fileName = $filename;
}
// Now copy the file to the correct location.
if (!empty($_FILES['mediafile' . $i]['name'])) {
$serverFileName = WT_DATA_DIR . $MEDIA_DIRECTORY . $folderName . $fileName;
if (file_exists($serverFileName)) {
FlashMessages::addMessage(I18N::translate('The file %s already exists. Use another filename.', $folderName . $fileName));
$filename = '';
break;
}
if (move_uploaded_file($_FILES['mediafile' . $i]['tmp_name'], $serverFileName)) {
FlashMessages::addMessage(I18N::translate('The file %s has been uploaded.', Html::filename($serverFileName)));
Log::addMediaLog('Media file ' . $serverFileName . ' uploaded');
} else {
FlashMessages::addMessage(
I18N::translate('There was an error uploading your file.') .
'<br>' .
Functions::fileUploadErrorText($_FILES['mediafile' . $i]['error'])
);
$filename = '';
break;
}
// Now copy the (optional thumbnail)
if (!empty($_FILES['thumbnail' . $i]['name']) && preg_match('/^image\/(png|gif|jpeg)/', $_FILES['thumbnail' . $i]['type'], $match)) {
$extension = $match[1];
$thumbFile = preg_replace('/\.[a-z0-9]{3,5}$/', '.' . $extension, $fileName);
$serverFileName = WT_DATA_DIR . $MEDIA_DIRECTORY . 'thumbs/' . $folderName . $thumbFile;
if (move_uploaded_file($_FILES['thumbnail' . $i]['tmp_name'], $serverFileName)) {
FlashMessages::addMessage(I18N::translate('The file %s has been uploaded.', Html::filename($serverFileName)));
Log::addMediaLog('Thumbnail file ' . $serverFileName . ' uploaded');
}
}
}
}
}
}
$controller->pageHeader();
$mediaFolders = QueryMedia::folderListAll();
// Determine file size limit
$filesize = ini_get('upload_max_filesize');
if (empty($filesize)) {
$filesize = "2M";
}
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li class="active"><?php echo $controller->getPageTitle(); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<p>
<?php echo I18N::translate('Upload one or more media files from your local computer. Media files can be pictures, video, audio, or other formats.'); ?>
<?php echo I18N::translate('Maximum upload size: '), $filesize; ?>
</p>
<?php
// Print the form
echo '<form name="uploadmedia" enctype="multipart/form-data" method="post" action="', WT_SCRIPT_NAME, '">';
echo '<input type="hidden" name="action" value="upload">';
// Print 5 forms for uploading images
for ($i = 1; $i < 6; $i++) {
echo '<table class="upload_media">';
echo '<tr><th>', I18N::translate('Media file'), ' ', $i, '</th></tr>';
echo '<tr><td>';
echo I18N::translate('Media file to upload');
echo '</td>';
echo '<td>';
echo '<input name="mediafile', $i, '" type="file" size="40">';
echo '</td></tr>';
echo '<tr><td>';
echo I18N::translate('Thumbnail to upload');
echo '</td>';
echo '<td>';
echo '<input name="thumbnail', $i, '" type="file" size="40">';
if ($i === 1) {
echo '<p class="small text-muted">', I18N::translate('Choose the thumbnail image that you want to upload. Although thumbnails can be generated automatically for images, you may wish to generate your own thumbnail, especially for other media types. For example, you can provide a still image from a video, or a photograph of the individual who made an audio recording.'), '</p>';
}
echo '</td></tr>';
if (Auth::isManager($WT_TREE)) {
echo '<tr><td>';
echo I18N::translate('Filename on server');
echo '</td>';
echo '<td>';
echo '<input name="filename', $i, '" type="text" size="40">';
if ($i == 1) {
echo '<p class="small text-muted">', I18N::translate('Do not change to keep original filename.'), "</p>";
echo '<p class="small text-muted">', I18N::translate('The media file you are uploading can be, and probably should be, named differently on the server than it is on your local computer. This is so because often the local filename has meaning to you but is much less meaningful to others visiting this website. Consider also the possibility that you and someone else both try to upload different files called “granny.jpg“.<br><br>In this field, you specify the new name of the file you are uploading. The name you enter here will also be used to name the thumbnail, which can be uploaded separately or generated automatically. You do not need to enter the filename extension (jpg, gif, pdf, doc, etc.)<br><br>Leave this field blank to keep the original name of the file you have uploaded from your local computer.'), '</p>';
}
echo '</td></tr>';
} else {
echo '<tr style="display:none;"><td><input type="hidden" name="filename', $i, '" value=""></td></tr>';
}
if (Auth::isManager($WT_TREE)) {
echo '<tr><td>';
echo I18N::translate('Folder name on server');
echo '</td>';
echo '<td>';
echo '<select name="folder_list', $i, '" onchange="document.uploadmedia.folder', $i, '.value=this.options[this.selectedIndex].value;">';
echo '<option';
echo ' value="/"> ', I18N::translate('&lt;select&gt;'), ' </option>';
if (Auth::isAdmin()) {
echo '<option value="other" disabled>', I18N::translate('Other folder… please type in'), "</option>";
}
foreach ($mediaFolders as $f) {
echo '<option value="', Filter::escapeHtml($f), '">', Filter::escapeHtml($f), "</option>";
}
echo "</select>";
if (Auth::isAdmin()) {
echo '<br><input name="folder', $i, '" type="text" size="40" value="">';
} else {
echo '<input name="folder', $i, '" type="hidden" value="">';
}
if ($i === 1) {
echo '<p class="small text-muted">', I18N::translate('If you have a large number of media files, you can organize them into folders and subfolders.'), '</p>';
}
echo '</td></tr>';
} else {
echo '<tr style="display:none;"><td><input name="folder', $i, '" type="hidden" value=""></td></tr>';
}
echo '</table>';
}
// Print the Submit button for uploading the media
echo '<input type="submit" value="', /* I18N: A button label. */ I18N::translate('upload'), '">';
echo '</form>';

View file

@ -1,113 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
/**
* Defined in session.php
*
* @global Tree $WT_TREE
*/
global $WT_TREE;
use Fisharebest\Webtrees\Controller\PageController;
use Fisharebest\Webtrees\Functions\FunctionsEdit;
use Fisharebest\Webtrees\Module\ModuleConfigInterface;
define('WT_SCRIPT_NAME', 'admin_module_blocks.php');
require 'includes/session.php';
$controller = new PageController;
$controller
->restrictAccess(Auth::isAdmin())
->setPageTitle(I18N::translate('Blocks'));
$action = Filter::post('action');
$modules = Module::getAllModulesByComponent('block');
if ($action === 'update_mods' && Filter::checkCsrf()) {
foreach ($modules as $module) {
foreach (Tree::getAll() as $tree) {
$access_level = Filter::post('access-' . $module->getName() . '-' . $tree->getTreeId(), WT_REGEX_INTEGER, $module->defaultAccessLevel());
Database::prepare(
"REPLACE INTO `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'block', ?)"
)->execute(array($module->getName(), $tree->getTreeId(), $access_level));
}
}
header('Location: ' . WT_BASE_URL . WT_SCRIPT_NAME);
return;
}
$controller
->pageHeader();
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li><a href="admin_modules.php"><?php echo I18N::translate('Module administration'); ?></a></li>
<li class="active"><?php echo $controller->getPageTitle(); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<form method="post">
<input type="hidden" name="action" value="update_mods">
<?php echo Filter::getCsrf(); ?>
<table class="table table-bordered">
<thead>
<tr>
<th class="col-xs-2"><?php echo I18N::translate('Block'); ?></th>
<th class="col-xs-5"><?php echo I18N::translate('Description'); ?></th>
<th class="col-xs-5"><?php echo I18N::translate('Access level'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($modules as $module_name => $module): ?>
<tr>
<td class="col-xs-2">
<?php if ($module instanceof ModuleConfigInterface): ?>
<a href="<?php echo $module->getConfigLink(); ?>"><?php echo $module->getTitle(); ?> <i class="fa fa-cogs"></i></a>
<?php else: ?>
<?php echo $module->getTitle(); ?>
<?php endif; ?>
</td>
<td class="col-xs-5"><?php echo $module->getDescription(); ?></td>
<td class="col-xs-5">
<table class="table">
<tbody>
<?php foreach (Tree::getAll() as $tree): ?>
<tr>
<td>
<?php echo $tree->getTitleHtml(); ?>
</td>
<td>
<?php echo FunctionsEdit::editFieldAccessLevel('access-' . $module->getName() . '-' . $tree->getTreeId(), $module->getAccessLevel($tree, 'block')); ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<button class="btn btn-primary" type="submit">
<i class="fa fa-check"></i>
<?php echo I18N::translate('save'); ?>
</button>
</form>

View file

@ -1,106 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
use Fisharebest\Webtrees\Controller\PageController;
use Fisharebest\Webtrees\Functions\FunctionsEdit;
use Fisharebest\Webtrees\Module\ModuleConfigInterface;
define('WT_SCRIPT_NAME', 'admin_module_charts.php');
require 'includes/session.php';
$controller = new PageController;
$controller
->restrictAccess(Auth::isAdmin())
->setPageTitle(I18N::translate('Charts'));
$action = Filter::post('action');
$modules = Module::getAllModulesByComponent('chart');
if ($action === 'update_mods' && Filter::checkCsrf()) {
foreach ($modules as $module) {
foreach (Tree::getAll() as $tree) {
$access_level = Filter::post('access-' . $module->getName() . '-' . $tree->getTreeId(), WT_REGEX_INTEGER, $module->defaultAccessLevel());
Database::prepare(
"REPLACE INTO `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'chart', ?)"
)->execute(array($module->getName(), $tree->getTreeId(), $access_level));
}
}
header('Location: ' . WT_BASE_URL . WT_SCRIPT_NAME);
return;
}
$controller
->pageHeader();
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li><a href="admin_modules.php"><?php echo I18N::translate('Module administration'); ?></a></li>
<li class="active"><?php echo $controller->getPageTitle(); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<form method="post">
<input type="hidden" name="action" value="update_mods">
<?php echo Filter::getCsrf(); ?>
<table class="table table-bordered">
<thead>
<tr>
<th class="col-xs-2"><?php echo I18N::translate('Chart'); ?></th>
<th class="col-xs-5"><?php echo I18N::translate('Description'); ?></th>
<th class="col-xs-5"><?php echo I18N::translate('Access level'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($modules as $module_name => $module): ?>
<tr>
<td class="col-xs-2">
<?php if ($module instanceof ModuleConfigInterface): ?>
<a href="<?php echo $module->getConfigLink(); ?>"><?php echo $module->getTitle(); ?> <i class="fa fa-cogs"></i></a>
<?php else: ?>
<?php echo $module->getTitle(); ?>
<?php endif; ?>
</td>
<td class="col-xs-5"><?php echo $module->getDescription(); ?></td>
<td class="col-xs-5">
<table class="table">
<tbody>
<?php foreach (Tree::getAll() as $tree): ?>
<tr>
<td>
<?php echo $tree->getTitleHtml(); ?>
</td>
<td>
<?php echo FunctionsEdit::editFieldAccessLevel('access-' . $module->getName() . '-' . $tree->getTreeId(), $module->getAccessLevel($tree, 'chart')); ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<button class="btn btn-primary" type="submit">
<i class="fa fa-check"></i>
<?php echo I18N::translate('save'); ?>
</button>
</form>

View file

@ -1,131 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
use Fisharebest\Webtrees\Controller\PageController;
use Fisharebest\Webtrees\Functions\FunctionsEdit;
use Fisharebest\Webtrees\Module\ModuleConfigInterface;
define('WT_SCRIPT_NAME', 'admin_module_menus.php');
require 'includes/session.php';
$controller = new PageController;
$controller
->restrictAccess(Auth::isAdmin())
->setPageTitle(I18N::translate('Menus'));
$action = Filter::post('action');
$modules = Module::getAllModulesByComponent('menu');
if ($action === 'update_mods' && Filter::checkCsrf()) {
foreach ($modules as $module) {
foreach (Tree::getAll() as $tree) {
$access_level = Filter::post('access-' . $module->getName() . '-' . $tree->getTreeId(), WT_REGEX_INTEGER, $module->defaultAccessLevel());
Database::prepare(
"REPLACE INTO `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'menu', ?)"
)->execute(array($module->getName(), $tree->getTreeId(), $access_level));
}
$order = Filter::post('order-' . $module->getName());
Database::prepare(
"UPDATE `##module` SET menu_order = ? WHERE module_name = ?"
)->execute(array($order, $module->getName()));
}
header('Location: ' . WT_BASE_URL . WT_SCRIPT_NAME);
return;
}
$controller
->addInlineJavascript('
jQuery("#module_table").sortable({
items: ".sortme",
forceHelperSize: true,
forcePlaceholderSize: true,
opacity: 0.7,
cursor: "move",
axis: "y",
update: function(event, ui) {
jQuery("input", jQuery(this)).each(
function (index, element) {
element.value = index + 1;
}
);
}
});
')
->pageHeader();
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li><a href="admin_modules.php"><?php echo I18N::translate('Module administration'); ?></a></li>
<li class="active"><?php echo $controller->getPageTitle(); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<form method="post">
<input type="hidden" name="action" value="update_mods">
<?php echo Filter::getCsrf(); ?>
<table id="module_table" class="table table-bordered">
<thead>
<tr>
<th class="col-xs-1"><?php echo I18N::translate('Menu'); ?></th>
<th class="col-xs-5"><?php echo I18N::translate('Description'); ?></th>
<th class="col-xs-1"><?php echo I18N::translate('Order'); ?></th>
<th class="col-xs-5"><?php echo I18N::translate('Access level'); ?></th>
</tr>
</thead>
<tbody>
<?php $order = 0; ?>
<?php foreach ($modules as $module): ?>
<?php $order++; ?>
<tr class="sortme">
<td class="col-xs-1">
<?php if ($module instanceof ModuleConfigInterface): ?>
<a href="<?php echo $module->getConfigLink(); ?>"><?php echo $module->getTitle(); ?> <i class="fa fa-cogs"></i></a>
<?php else: ?>
<?php echo $module->getTitle(); ?>
<?php endif; ?>
</td>
<td class="col-xs-5"><?php echo $module->getDescription(); ?></td>
<td class="col-xs-1"><input type="text" size="3" value="<?php echo $order; ?>" name="order-<?php echo $module->getName(); ?>"></td>
<td class="col-xs-5">
<table class="table">
<tbody>
<?php foreach (Tree::getAll() as $tree): ?>
<tr>
<td>
<?php echo $tree->getTitleHtml(); ?>
</td>
<td>
<?php echo FunctionsEdit::editFieldAccessLevel('access-' . $module->getName() . '-' . $tree->getTreeId(), $module->getAccessLevel($tree, 'menu')); ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<button class="btn btn-primary" type="submit">
<i class="fa fa-check"></i>
<?php echo I18N::translate('save'); ?>
</button>
</form>

View file

@ -1,106 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
use Fisharebest\Webtrees\Controller\PageController;
use Fisharebest\Webtrees\Functions\FunctionsEdit;
use Fisharebest\Webtrees\Module\ModuleConfigInterface;
define('WT_SCRIPT_NAME', 'admin_module_reports.php');
require 'includes/session.php';
$controller = new PageController;
$controller
->restrictAccess(Auth::isAdmin())
->setPageTitle(I18N::translate('Reports'));
$action = Filter::post('action');
$modules = Module::getAllModulesByComponent('report');
if ($action === 'update_mods' && Filter::checkCsrf()) {
foreach ($modules as $module) {
foreach (Tree::getAll() as $tree) {
$access_level = Filter::post('access-' . $module->getName() . '-' . $tree->getTreeId(), WT_REGEX_INTEGER, $module->defaultAccessLevel());
Database::prepare(
"REPLACE INTO `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'report', ?)"
)->execute(array($module->getName(), $tree->getTreeId(), $access_level));
}
}
header('Location: ' . WT_BASE_URL . WT_SCRIPT_NAME);
return;
}
$controller
->pageHeader();
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li><a href="admin_modules.php"><?php echo I18N::translate('Module administration'); ?></a></li>
<li class="active"><?php echo $controller->getPageTitle(); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<form method="post">
<input type="hidden" name="action" value="update_mods">
<?php echo Filter::getCsrf(); ?>
<table class="table table-bordered">
<thead>
<tr>
<th class="col-xs-2"><?php echo I18N::translate('Report'); ?></th>
<th class="col-xs-5"><?php echo I18N::translate('Description'); ?></th>
<th class="col-xs-5"><?php echo I18N::translate('Access level'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($modules as $module_name => $module): ?>
<tr>
<td class="col-xs-2">
<?php if ($module instanceof ModuleConfigInterface): ?>
<a href="<?php echo $module->getConfigLink(); ?>"><?php echo $module->getTitle(); ?> <i class="fa fa-cogs"></i></a>
<?php else: ?>
<?php echo $module->getTitle(); ?>
<?php endif; ?>
</td>
<td class="col-xs-5"><?php echo $module->getDescription(); ?></td>
<td class="col-xs-5">
<table class="table">
<tbody>
<?php foreach (Tree::getAll() as $tree): ?>
<tr>
<td>
<?php echo $tree->getTitleHtml(); ?>
</td>
<td>
<?php echo FunctionsEdit::editFieldAccessLevel('access-' . $module->getName() . '-' . $tree->getTreeId(), $module->getAccessLevel($tree, 'report')); ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<button class="btn btn-primary" type="submit">
<i class="fa fa-check"></i>
<?php echo I18N::translate('save'); ?>
</button>
</form>

View file

@ -1,131 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
use Fisharebest\Webtrees\Controller\PageController;
use Fisharebest\Webtrees\Functions\FunctionsEdit;
use Fisharebest\Webtrees\Module\ModuleConfigInterface;
define('WT_SCRIPT_NAME', 'admin_module_sidebar.php');
require 'includes/session.php';
$controller = new PageController;
$controller
->restrictAccess(Auth::isAdmin())
->setPageTitle(I18N::translate('Sidebars'));
$action = Filter::post('action');
$modules = Module::getAllModulesByComponent('sidebar');
if ($action === 'update_mods' && Filter::checkCsrf()) {
foreach ($modules as $module) {
foreach (Tree::getAll() as $tree) {
$access_level = Filter::post('access-' . $module->getName() . '-' . $tree->getTreeId(), WT_REGEX_INTEGER, $module->defaultAccessLevel());
Database::prepare(
"REPLACE INTO `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'sidebar', ?)"
)->execute(array($module->getName(), $tree->getTreeId(), $access_level));
}
$order = Filter::post('order-' . $module->getName());
Database::prepare(
"UPDATE `##module` SET sidebar_order = ? WHERE module_name = ?"
)->execute(array($order, $module->getName()));
}
header('Location: ' . WT_BASE_URL . WT_SCRIPT_NAME);
return;
}
$controller
->addInlineJavascript('
jQuery("#module_table").sortable({
items: ".sortme",
forceHelperSize: true,
forcePlaceholderSize: true,
opacity: 0.7,
cursor: "move",
axis: "y",
update: function(event, ui) {
jQuery("input", jQuery(this)).each(
function (index, element) {
element.value = index + 1;
}
);
}
});
')
->pageHeader();
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li><a href="admin_modules.php"><?php echo I18N::translate('Module administration'); ?></a></li>
<li class="active"><?php echo $controller->getPageTitle(); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<form method="post">
<input type="hidden" name="action" value="update_mods">
<?php echo Filter::getCsrf(); ?>
<table id="module_table" class="table table-bordered">
<thead>
<tr>
<th class="col-xs-1"><?php echo I18N::translate('Sidebar'); ?></th>
<th class="col-xs-5"><?php echo I18N::translate('Description'); ?></th>
<th class="col-xs-1"><?php echo I18N::translate('Order'); ?></th>
<th class="col-xs-5"><?php echo I18N::translate('Access level'); ?></th>
</tr>
</thead>
<tbody>
<?php $order = 0; ?>
<?php foreach ($modules as $module): ?>
<?php $order++; ?>
<tr class="sortme">
<td class="col-xs-1">
<?php if ($module instanceof ModuleConfigInterface): ?>
<a href="<?php echo $module->getConfigLink(); ?>"><?php echo $module->getTitle(); ?> <i class="fa fa-cogs"></i></a>
<?php else: ?>
<?php echo $module->getTitle(); ?>
<?php endif; ?>
</td>
<td class="col-xs-5"><?php echo $module->getDescription(); ?></td>
<td class="col-xs-1"><input type="text" size="3" value="<?php echo $order; ?>" name="order-<?php echo $module->getName(); ?>"></td>
<td class="col-xs-5">
<table class="table">
<tbody>
<?php foreach (Tree::getAll() as $tree): ?>
<tr>
<td>
<?php echo $tree->getTitleHtml(); ?>
</td>
<td>
<?php echo FunctionsEdit::editFieldAccessLevel('access-' . $module->getName() . '-' . $tree->getTreeId(), $module->getAccessLevel($tree, 'sidebar')); ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<button class="btn btn-primary" type="submit">
<i class="fa fa-check"></i>
<?php echo I18N::translate('save'); ?>
</button>
</form>

View file

@ -1,131 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
use Fisharebest\Webtrees\Controller\PageController;
use Fisharebest\Webtrees\Functions\FunctionsEdit;
use Fisharebest\Webtrees\Module\ModuleConfigInterface;
define('WT_SCRIPT_NAME', 'admin_module_tabs.php');
require 'includes/session.php';
$controller = new PageController;
$controller
->restrictAccess(Auth::isAdmin())
->setPageTitle(I18N::translate('Tabs'));
$action = Filter::post('action');
$modules = Module::getAllModulesByComponent('tab');
if ($action === 'update_mods' && Filter::checkCsrf()) {
foreach ($modules as $module) {
foreach (Tree::getAll() as $tree) {
$access_level = Filter::post('access-' . $module->getName() . '-' . $tree->getTreeId(), WT_REGEX_INTEGER, $module->defaultAccessLevel());
Database::prepare(
"REPLACE INTO `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'tab', ?)"
)->execute(array($module->getName(), $tree->getTreeId(), $access_level));
}
$order = Filter::post('order-' . $module->getName());
Database::prepare(
"UPDATE `##module` SET tab_order=? WHERE module_name=?"
)->execute(array($order, $module->getName()));
}
header('Location: ' . WT_BASE_URL . WT_SCRIPT_NAME);
return;
}
$controller
->addInlineJavascript('
jQuery("#module_table").sortable({
items: ".sortme",
forceHelperSize: true,
forcePlaceholderSize: true,
opacity: 0.7,
cursor: "move",
axis: "y",
update: function(event, ui) {
jQuery("input", jQuery(this)).each(
function (index, element) {
element.value = index + 1;
}
);
}
});
')
->pageHeader();
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li><a href="admin_modules.php"><?php echo I18N::translate('Module administration'); ?></a></li>
<li class="active"><?php echo $controller->getPageTitle(); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<form method="post">
<input type="hidden" name="action" value="update_mods">
<?php echo Filter::getCsrf(); ?>
<table id="module_table" class="table table-bordered">
<thead>
<tr>
<th class="col-xs-1"><?php echo I18N::translate('Tab'); ?></th>
<th class="col-xs-5"><?php echo I18N::translate('Description'); ?></th>
<th class="col-xs-1"><?php echo I18N::translate('Order'); ?></th>
<th class="col-xs-5"><?php echo I18N::translate('Access level'); ?></th>
</tr>
</thead>
<tbody>
<?php $order = 0; ?>
<?php foreach ($modules as $module): ?>
<?php $order++; ?>
<tr class="sortme">
<td class="col-xs-1">
<?php if ($module instanceof ModuleConfigInterface): ?>
<a href="<?php echo $module->getConfigLink(); ?>"><?php echo $module->getTitle(); ?> <i class="fa fa-cogs"></i></a>
<?php else: ?>
<?php echo $module->getTitle(); ?>
<?php endif; ?>
</td>
<td class="col-xs-5"><?php echo $module->getDescription(); ?></td>
<td class="col-xs-1"><input type="text" size="3" value="<?php echo $order; ?>" name="order-<?php echo $module->getName(); ?>"></td>
<td class="col-xs-5">
<table class="table">
<tbody>
<?php foreach (Tree::getAll() as $tree): ?>
<tr>
<td>
<?php echo $tree->getTitleHtml(); ?>
</td>
<td>
<?php echo FunctionsEdit::editFieldAccessLevel('access-' . $module->getName() . '-' . $tree->getTreeId(), $module->getAccessLevel($tree, 'tab')); ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<button class="btn btn-primary" type="submit">
<i class="fa fa-check"></i>
<?php echo I18N::translate('save'); ?>
</button>
</form>

View file

@ -1,263 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
use Fisharebest\Webtrees\Controller\PageController;
use Fisharebest\Webtrees\Functions\FunctionsEdit;
use Fisharebest\Webtrees\Module\AbstractModule;
use Fisharebest\Webtrees\Module\ModuleBlockInterface;
use Fisharebest\Webtrees\Module\ModuleChartInterface;
use Fisharebest\Webtrees\Module\ModuleConfigInterface;
use Fisharebest\Webtrees\Module\ModuleMenuInterface;
use Fisharebest\Webtrees\Module\ModuleReportInterface;
use Fisharebest\Webtrees\Module\ModuleSidebarInterface;
use Fisharebest\Webtrees\Module\ModuleTabInterface;
use Fisharebest\Webtrees\Module\ModuleThemeInterface;
define('WT_SCRIPT_NAME', 'admin_modules.php');
require 'includes/session.php';
$controller = new PageController;
$controller
->restrictAccess(Auth::isAdmin())
->setPageTitle(I18N::translate('Module administration'));
$modules = Module::getInstalledModules('disabled');
$module_status = Database::prepare("SELECT module_name, status FROM `##module`")->fetchAssoc();
uasort($modules, function (AbstractModule $x, AbstractModule $y) {
return I18N::strcasecmp($x->getTitle(), $y->getTitle());
});
if (Filter::post('action') === 'update_mods' && Filter::checkCsrf()) {
foreach ($modules as $module) {
$new_status = Filter::post('status-' . $module->getName(), '[01]');
if ($new_status !== null) {
$new_status = $new_status ? 'enabled' : 'disabled';
$old_status = $module_status[$module->getName()];
if ($new_status !== $old_status) {
Database::prepare("UPDATE `##module` SET status=? WHERE module_name=?")->execute(array($new_status, $module->getName()));
if ($new_status === 'disabled') {
FlashMessages::addMessage(I18N::translate('The module “%s” has been disabled.', $module->getTitle()), 'success');
} else {
FlashMessages::addMessage(I18N::translate('The module “%s” has been enabled.', $module->getTitle()), 'success');
}
}
}
}
header('Location: ' . WT_BASE_URL . 'admin_modules.php');
return;
}
if (Filter::post('action') === 'delete' && Filter::checkCsrf()) {
$module_name = Filter::post('module_name');
Database::prepare(
"DELETE `##block_setting`" .
" FROM `##block_setting`" .
" JOIN `##block` USING (block_id)" .
" JOIN `##module` USING (module_name)" .
" WHERE module_name=?"
)->execute(array($module_name));
Database::prepare(
"DELETE `##block`" .
" FROM `##block`" .
" JOIN `##module` USING (module_name)" .
" WHERE module_name=?"
)->execute(array($module_name));
Database::prepare("DELETE FROM `##module_setting` WHERE module_name=?")->execute(array($module_name));
Database::prepare("DELETE FROM `##module_privacy` WHERE module_name=?")->execute(array($module_name));
Database::prepare("DELETE FROM `##module` WHERE module_name=?")->execute(array($module_name));
FlashMessages::addMessage(I18N::translate('The preferences for the module “%s” have been deleted.', $module_name), 'success');
header('Location: ' . WT_BASE_URL . 'admin_modules.php');
return;
}
// The module cant be found on disk?
// Don't delete it automatically. It may be temporarily missing, after a re-installation, etc.
foreach ($module_status as $module_name => $status) {
if (!array_key_exists($module_name, $modules)) {
$html =
I18N::translate('Preferences exist for the module “%s”, but this module no longer exists.', '<span dir="ltr">' . $module_name . '</span>') .
'<form method="post" class="form-inline">' .
Filter::getCsrf() .
'<input type="hidden" name="action" value="delete">' .
'<input type="hidden" name="module_name" value="' . $module_name . '">' .
'<button type="submit" class="btn btn-link">' . I18N::translate('Delete the preferences for this module.') . '</button>' .
'</form>';
FlashMessages::addMessage($html, 'warning');
}
}
$controller
->pageHeader()
->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
->addExternalJavascript(WT_DATATABLES_BOOTSTRAP_JS_URL)
->addInlineJavascript('
function reindexMods(id) {
jQuery("#" + id + " input").each(
function (index, value) {
value.value = index+1;
});
}
jQuery("#installed_table").dataTable( {
paging: false,
' . I18N::datatablesI18N() . ',
sorting: [[ 1, "asc" ]],
columns : [
{ sortable: false, class: "center" },
null,
null,
{ class: "center" },
{ class: "center" },
{ class: "center" },
{ class: "center" },
{ class: "center" },
{ class: "center" },
{ class: "center", visible: false } // The Module system does not yet include themes
]
});
');
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li class="active"><?php echo $controller->getPageTitle(); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<form method="post" action="<?php echo WT_SCRIPT_NAME; ?>">
<input type="hidden" name="action" value="update_mods">
<?php echo Filter::getCsrf(); ?>
<table class="table table-bordered table-hover table-condensed table-module-administration">
<caption class="sr-only">
<?php echo I18N::translate('Module administration'); ?>
</caption>
<thead>
<tr>
<th><?php echo I18N::translate('Enabled'); ?></th>
<th><?php echo I18N::translate('Module'); ?></th>
<th><?php echo I18N::translate('Description'); ?></th>
<th class="hidden-xs"><a href="admin_module_menus.php"><?php echo I18N::translate('Menus'); ?></a></th>
<th class="hidden-xs"><a href="admin_module_tabs.php"><?php echo I18N::translate('Tabs'); ?></a></th>
<th class="hidden-xs"><a href="admin_module_sidebar.php"><?php echo I18N::translate('Sidebars'); ?></a></th>
<th class="hidden-xs"><a href="admin_module_blocks.php"><?php echo I18N::translate('Blocks'); ?></a></th>
<th class="hidden-xs"><a href="admin_module_charts.php"><?php echo I18N::translate('Charts'); ?></a></th>
<th class="hidden-xs"><a href="admin_module_reports.php"><?php echo I18N::translate('Reports'); ?></a></th>
<th class="hidden"><?php echo I18N::translate('Themes'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($modules as $module_name => $module): ?>
<tr>
<td class="text-center">
<?php echo FunctionsEdit::twoStateCheckbox('status-' . $module->getName(), $module_status[$module_name] === 'enabled') ?>
</td>
<td>
<?php if ($module instanceof ModuleConfigInterface): ?>
<a href="<?php echo $module->getConfigLink() ?>">
<?php echo $module->getTitle() ?> <i class="fa fa-cogs"></i>
</a>
<?php else: ?>
<?php echo $module->getTitle() ?>
<?php endif; ?>
<?php if (!in_array($module->getName(), Module::getCoreModuleNames())): ?>
<br>
<?php endif; ?>
</td>
<td>
<?php echo '', $module->getDescription() ?>
<?php if (!in_array($module->getName(), Module::getCoreModuleNames())): ?>
<br>
<i class="fa fa-asterisk"></i>
<?php echo I18N::translate('Custom module') ?>
<?php if ($module::CUSTOM_VERSION): ?>
- <?php echo I18N::translate('Version') ?> <?php echo $module::CUSTOM_VERSION ?>
<?php endif; ?>
<?php if ($module::CUSTOM_WEBSITE): ?>
- <a href="<?php echo $module::CUSTOM_WEBSITE ?>">
<?php echo $module::CUSTOM_WEBSITE ?>
</a>
<?php endif; ?>
<?php endif; ?>
</td>
<td class="text-center text-muted hidden-xs">
<?php if ($module instanceof ModuleMenuInterface): ?>
<i class="fa fa-list-ul" title="<?php echo I18N::translate('Menu') ?>"></i>
<?php else: ?>
-
<?php endif; ?>
</td>
<td class="text-center text-muted hidden-xs">
<?php if ($module instanceof ModuleTabInterface): ?>
<i class="fa fa-folder" title="<?php echo I18N::translate('Tab') ?>"></i>
<?php else: ?>
-
<?php endif; ?>
</td>
<td class="text-center text-muted hidden-xs">
<?php if ($module instanceof ModuleSidebarInterface): ?>
<i class="fa fa-th-large" title="<?php echo I18N::translate('Sidebar') ?>"></i>
<?php else: ?>
-
<?php endif; ?>
</td>
<td class="text-center text-muted hidden-xs">
<?php if ($module instanceof ModuleBlockInterface): ?>
<?php if ($module->isUserBlock()): ?>
<i class="fa fa-user" title="<?php echo I18N::translate('My page') ?>"></i>
<?php endif; ?>
<?php if ($module->isUserBlock()): ?>
<i class="fa fa-tree" title="<?php echo I18N::translate('Home page') ?>"></i>
<?php endif; ?>
<?php else: ?>
-
<?php endif; ?>
</td>
<td class="text-center text-muted hidden-xs">
<?php if ($module instanceof ModuleChartInterface): ?>
<i class="fa fa-share-alt" title="<?php echo I18N::translate('Chart') ?>"></i>
<?php else: ?>
-
<?php endif; ?>
</td>
<td class="text-center text-muted hidden-xs">
<?php if ($module instanceof ModuleReportInterface): ?>
<i class="fa fa-file" title="<?php echo I18N::translate('Report') ?>"></i>
<?php else: ?>
-
<?php endif; ?>
</td>
<td class="text-center text-muted hidden">
<?php if ($module instanceof ModuleThemeInterface): ?>
<i class="fa fa-check" title="<?php echo I18N::translate('Theme') ?>"></i>
<?php else: ?>
-
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<button class="btn btn-primary" type="submit">
<i class="fa fa-check"></i>
<?php echo I18N::translate('save'); ?></button>
</form>

File diff suppressed because it is too large Load diff

View file

@ -1,385 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
use Fisharebest\Webtrees\Controller\PageController;
use Fisharebest\Webtrees\Functions\FunctionsEdit;
use PDO;
define('WT_SCRIPT_NAME', 'admin_site_access.php');
require './includes/session.php';
$rules_display = array(
'unknown' => I18N::translate('unknown'),
'allow' => /* I18N: An access rule - allow access to the site */ I18N::translate('allow'),
'deny' => /* I18N: An access rule - deny access to the site */ I18N::translate('deny'),
'robot' => /* I18N: http://en.wikipedia.org/wiki/Web_crawler */ I18N::translate('robot'),
);
$rules_edit = array(
'unknown' => I18N::translate('unknown'),
'allow' => /* I18N: An access rule - allow access to the site */ I18N::translate('allow'),
'deny' => /* I18N: An access rule - deny access to the site */ I18N::translate('deny'),
'robot' => /* I18N: http://en.wikipedia.org/wiki/Web_crawler */ I18N::translate('robot'),
);
// Form actions
switch (Filter::post('action')) {
case 'save':
if (Filter::checkCsrf()) {
$site_access_rule_id = Filter::postInteger('site_access_rule_id');
$ip_address_start = Filter::post('ip_address_start', WT_REGEX_IPV4);
$ip_address_end = Filter::post('ip_address_end', WT_REGEX_IPV4);
$user_agent_pattern = Filter::post('user_agent_pattern');
$rule = Filter::post('rule', 'allow|deny|robot');
$comment = Filter::post('comment');
$user_agent_string = Filter::server('HTTP_USER_AGENT');
$ip_address = WT_CLIENT_IP;
if ($ip_address_start !== null && $ip_address_end !== null && $user_agent_pattern !== null && $rule !== null) {
// This doesn't work with named placeholders. The :user_agent_string parameter is not recognised.
$oops = $rule !== 'allow' && Database::prepare(
"SELECT INET_ATON(:ip_address) BETWEEN INET_ATON(:ip_address_start) AND INET_ATON(:ip_address_end)" .
" AND :user_agent_string LIKE :user_agent_pattern"
)->execute(array(
'ip_address' => $ip_address,
'ip_address_start' => $ip_address_start,
'ip_address_end' => $ip_address_end,
'user_agent_string' => $user_agent_string,
'user_agent_pattern' => $user_agent_pattern,
))->fetchOne();
if ($oops) {
FlashMessages::addMessage(I18N::translate('You cannot create a rule which would prevent yourself from accessing the website.'), 'danger');
} elseif ($site_access_rule_id === null) {
Database::prepare(
"INSERT INTO `##site_access_rule` (ip_address_start, ip_address_end, user_agent_pattern, rule, comment) VALUES (INET_ATON(:ip_address_start), INET_ATON(:ip_address_end), :user_agent_pattern, :rule, :comment)"
)->execute(array(
'ip_address_start' => $ip_address_start,
'ip_address_end' => $ip_address_end,
'user_agent_pattern' => $user_agent_pattern,
'rule' => $rule,
'comment' => $comment,
));
FlashMessages::addMessage(I18N::translate('The website access rule has been created.'), 'success');
} else {
Database::prepare(
"UPDATE `##site_access_rule` SET ip_address_start = INET_ATON(:ip_address_start), ip_address_end = INET_ATON(:ip_address_end), user_agent_pattern = :user_agent_pattern, rule = :rule, comment = :comment WHERE site_access_rule_id = :site_access_rule_id"
)->execute(array(
'ip_address_start' => $ip_address_start,
'ip_address_end' => $ip_address_end,
'user_agent_pattern' => $user_agent_pattern,
'rule' => $rule,
'comment' => $comment,
'site_access_rule_id' => $site_access_rule_id,
));
FlashMessages::addMessage(I18N::translate('The website access rule has been updated.'), 'success');
}
}
}
header('Location: ' . WT_BASE_URL . WT_SCRIPT_NAME);
return;
case 'delete':
if (Filter::checkCsrf()) {
$site_access_rule_id = Filter::postInteger('site_access_rule_id');
Database::prepare(
"DELETE FROM `##site_access_rule` WHERE site_access_rule_id = :site_access_rule_id"
)->execute(array(
'site_access_rule_id' => $site_access_rule_id,
));
FlashMessages::addMessage(I18N::translate('The website access rule has been deleted.'), 'success');
}
header('Location: ' . WT_BASE_URL . WT_SCRIPT_NAME);
return;
}
// Delete any "unknown" visitors that are now "known".
// This could happen every time we create/update a rule.
Database::exec(
"DELETE unknown" .
" FROM `##site_access_rule` AS unknown" .
" JOIN `##site_access_rule` AS known ON (unknown.user_agent_pattern LIKE known.user_agent_pattern)" .
" WHERE unknown.rule='unknown' AND known.rule<>'unknown'" .
" AND unknown.ip_address_start BETWEEN known.ip_address_start AND known.ip_address_end"
);
$controller = new PageController;
$controller
->restrictAccess(Auth::isAdmin())
->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
->addExternalJavascript(WT_DATATABLES_BOOTSTRAP_JS_URL)
->setPageTitle(I18N::translate('Website access rules'));
$action = Filter::get('action');
switch ($action) {
case 'load':
// AJAX callback for datatables
$search = Filter::get('search');
$search = $search['value'];
$start = Filter::getInteger('start');
$length = Filter::getInteger('length');
$sql =
"SELECT SQL_CACHE SQL_CALC_FOUND_ROWS" .
" '', INET_NTOA(ip_address_start), ip_address_start, INET_NTOA(ip_address_end), ip_address_end, user_agent_pattern, rule, comment, site_access_rule_id" .
" FROM `##site_access_rule`";
$args = array();
if ($search) {
$sql .=
" WHERE (INET_ATON(:search_1) BETWEEN ip_address_start AND ip_address_end" .
" OR INET_NTOA(ip_address_start) LIKE CONCAT('%', :search_2, '%')" .
" OR INET_NTOA(ip_address_end) LIKE CONCAT('%', :search_3, '%')" .
" OR user_agent_pattern LIKE CONCAT('%', :search_4, '%')" .
" OR comment LIKE CONCAT('%', :search_5, '%'))";
$args['search_1'] = Filter::escapeLike($search);
$args['search_2'] = Filter::escapeLike($search);
$args['search_3'] = Filter::escapeLike($search);
$args['search_4'] = Filter::escapeLike($search);
$args['search_5'] = Filter::escapeLike($search);
}
$order = Filter::getArray('order');
$sql .= ' ORDER BY';
if ($order) {
foreach ($order as $key => $value) {
if ($key > 0) {
$sql .= ',';
}
// Datatables numbers columns 0, 1, 2
// MySQL numbers columns 1, 2, 3
switch ($value['dir']) {
case 'asc':
$sql .= " :col_" . $key . " ASC";
break;
case 'desc':
$sql .= " :col_" . $key . " DESC";
break;
}
$args['col_' . $key] = 1 + $value['column'];
}
} else {
$sql .= ' 1 ASC';
}
if ($length > 0) {
$sql .= " LIMIT :length OFFSET :start";
$args['length'] = $length;
$args['start'] = $start;
}
// This becomes a JSON list, not a JSON array, so we need numeric keys.
$data = Database::prepare($sql)->execute($args)->fetchAll(PDO::FETCH_NUM);
// Reformat the data for display
foreach ($data as &$datum) {
$site_access_rule_id = $datum[8];
$datum[0] = '<div class="btn-group"><button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><i class="fa fa-pencil"></i> <span class="caret"></span></button><ul class="dropdown-menu" role="menu"><li><a href="?action=edit&amp;site_access_rule_id=' . $site_access_rule_id . '"><i class="fa fa-fw fa-pencil"></i> ' . I18N::translate('Edit') . '</a></li><li class="divider"><li><a href="#" onclick="if (confirm(\'' . I18N::translate('Are you sure you want to delete “%s”?', Filter::escapeJs($datum[5])) . '\')) delete_site_access_rule(' . $site_access_rule_id . '); return false;"><i class="fa fa-fw fa-trash-o"></i> ' . I18N::translate('Delete') . '</a></li></ul></div>';
$datum[5] = '<span dir="ltr">' . $datum[5] . '</span>';
$datum[6] = $rules_display[$datum[6]];
$datum[7] = '<span dir="auto">' . $datum[7] . '</span>';
}
// Total filtered/unfiltered rows
$recordsFiltered = Database::prepare("SELECT FOUND_ROWS()")->fetchOne();
$recordsTotal = Database::prepare("SELECT COUNT(*) FROM `##site_access_rule`")->fetchOne();
header('Content-type: application/json');
// See http://www.datatables.net/usage/server-side
echo json_encode(array(
'draw' => Filter::getInteger('draw'),
'recordsTotal' => $recordsTotal,
'recordsFiltered' => $recordsFiltered,
'data' => $data,
));
break;
case 'edit':
case 'create':
if (Filter::get('action') === 'edit') {
$controller->setPageTitle(I18N::translate('Edit a website access rule'));
} else {
$controller->setPageTitle(I18N::translate('Create a website access rule'));
}
$controller->pageHeader();
$site_access_rule = Database::prepare(
"SELECT site_access_rule_id, INET_NTOA(ip_address_start) AS ip_address_start, INET_NTOA(ip_address_end) AS ip_address_end, user_agent_pattern, rule, comment" .
" FROM `##site_access_rule` WHERE site_access_rule_id = :site_access_rule_id"
)->execute(array(
'site_access_rule_id' => Filter::getInteger('site_access_rule_id'),
))->fetchOneRow();
$site_access_rule_id = $site_access_rule ? $site_access_rule->site_access_rule_id : null;
$ip_address_start = $site_access_rule ? $site_access_rule->ip_address_start : '0.0.0.0';
$ip_address_end = $site_access_rule ? $site_access_rule->ip_address_end : '255.255.255.255';
$user_agent_pattern = $site_access_rule ? $site_access_rule->user_agent_pattern : '%';
$rule = $site_access_rule ? $site_access_rule->rule : 'allow';
$comment = $site_access_rule ? $site_access_rule->comment : '';
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li><a href="admin_site_access.php"><?php echo I18N::translate('Website access rules'); ?></a></li>
<li class="active"><?php echo $controller->getPageTitle(); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<form method="post" class="form form-horizontal">
<input type="hidden" name="action" value="save">
<input type="hidden" name="site_access_rule_id" value="<?php echo $site_access_rule_id; ?>">
<?php echo Filter::getCsrf(); ?>
<!-- IP_ADDRESS_START -->
<div class="form-group">
<label class="control-label col-sm-3" for="ip_address_start">
<?php echo I18N::translate('Start IP address'); ?>
</label>
<div class="col-sm-9">
<input class="form-control" type="text" id="ip_address_start" name="ip_address_start" required pattern="<?php echo WT_REGEX_IPV4; ?>" value="<?php echo Filter::escapeHtml($ip_address_start); ?>">
</div>
</div>
<!-- IP_ADDRESS_END -->
<div class="form-group">
<label class="control-label col-sm-3" for="ip_address_end">
<?php echo I18N::translate('End IP address'); ?>
</label>
<div class="col-sm-9">
<input class="form-control" type="text" id="ip_address_end" name="ip_address_end" required pattern="<?php echo WT_REGEX_IPV4; ?>" value="<?php echo Filter::escapeHtml($ip_address_end); ?>">
</div>
</div>
<!-- USER_AGENT_PATTERN -->
<div class="form-group">
<label class="control-label col-sm-3" for="user_agent_pattern">
<?php echo I18N::translate('User-agent string'); ?>
</label>
<div class="col-sm-9">
<input class="form-control" type="text" id="user_agent_pattern" name="user_agent_pattern" required value="<?php echo Filter::escapeHtml($user_agent_pattern); ?>" maxlength="255" dir="ltr">
<p class="small text-muted">
<?php echo I18N::translate('The “%” character is a wildcard, and will match zero or more other characters.'); ?>
</p>
</div>
</div>
<!-- RULE -->
<div class="form-group">
<label class="control-label col-sm-3" for="rule">
<?php echo /* I18N: A configuration setting */ I18N::translate('Rule'); ?>
</label>
<div class="col-sm-9">
<?php echo FunctionsEdit::selectEditControl('rule', $rules_edit, null, $rule, 'class="form-control"'); ?>
</div>
</div>
<!-- COMMENT -->
<div class="form-group">
<label class="control-label col-sm-3" for="comment">
<?php echo I18N::translate('Comment'); ?>
</label>
<div class="col-sm-9">
<input class="form-control" type="text" id="comment" name="comment" value="<?php echo Filter::escapeHtml($comment); ?>" maxlength="255" dir="auto">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9">
<button type="submit" class="btn btn-primary">
<?php echo I18N::translate('save'); ?>
</button>
</div>
</div>
</form>
<?php
break;
default:
$controller
->pageHeader()
->addInlineJavascript('
jQuery.fn.dataTableExt.oSort["unicode-asc" ]=function(a,b) {return a.replace(/<[^<]*>/, "").localeCompare(b.replace(/<[^<]*>/, ""))};
jQuery.fn.dataTableExt.oSort["unicode-desc"]=function(a,b) {return b.replace(/<[^<]*>/, "").localeCompare(a.replace(/<[^<]*>/, ""))};
jQuery(".table-site-access-rules").dataTable({
ajax: "' . WT_BASE_URL . WT_SCRIPT_NAME . '?action=load",
serverSide: true,
' . I18N::datatablesI18N() . ',
processing: true,
stateSave: true,
stateDuration: 180,
sorting: [[1, "asc"]],
columns: [
/* 0 <edit> */ { sortable: false },
/* 1 ip_address_start */ { dataSort: 2, class: "ip_address" },
/* 2 ip_address_start (sort) */ { type: "num", visible: false },
/* 3 ip_address_end */ { dataSort: 4, class: "ip_address" },
/* 4 ip_address_end (sort) */ { type: "num", visible: false },
/* 5 user_agent_pattern */ { class: "ua_string" },
/* 6 comment */ { },
/* 7 rule */ { }
]
});
');
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li class="active"><?php echo $controller->getPageTitle(); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<p><?php echo /* I18N: http://en.wikipedia.org/wiki/User_agent */ I18N::translate('Restrict access to the website, using IP addresses and user-agent strings.'); ?></p>
<table class="table table-hover table-condensed table-bordered table-site-access-rules">
<caption>
<?php echo I18N::translate('The following rules are used to decide whether a visitor is a human being (allow full access), a search-engine robot (allow restricted access) or an unwanted crawler (deny all access).'); ?>
</caption>
<thead>
<tr>
<th><?php echo I18N::translate('Edit'); ?></th>
<th><?php echo /* I18N …of a range of addresses */ I18N::translate('Start IP address'); ?></th>
<th>-</th>
<th><?php echo /* I18N …of a range of addresses */ I18N::translate('End IP address'); ?></th>
<th>-</th>
<th><?php echo /* I18N: http://en.wikipedia.org/wiki/User_agent_string */ I18N::translate('User-agent string'); ?></th>
<th><?php echo /* I18N: noun */ I18N::translate('Rule'); ?></th>
<th><?php echo I18N::translate('Comment'); ?></th>
</tr>
</thead>
</table>
<!-- Implement the delete action -->
<form class="hide" method="post" id="delete-form">
<?php echo Filter::getCsrf(); ?>
<input type="hidden" name="site_access_rule_id" id="site-access-rule-id" value="">
<input type="hidden" name="action" value="delete">
</form>
<script>
function delete_site_access_rule(site_access_rule_id) {
document.getElementById("site-access-rule-id").value = site_access_rule_id;
document.getElementById("delete-form").submit();
}
</script>
<?php
break;
}

View file

@ -1,396 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
use Fisharebest\Algorithm\MyersDiff;
use Fisharebest\Webtrees\Controller\PageController;
use Fisharebest\Webtrees\Functions\FunctionsEdit;
use PDO;
/**
* Defined in session.php
*
* @global Tree $WT_TREE
*/
global $WT_TREE;
define('WT_SCRIPT_NAME', 'admin_site_change.php');
require './includes/session.php';
$controller = new PageController;
$controller
->restrictAccess(Auth::isManager($WT_TREE))
->setPageTitle(I18N::translate('Changes'));
$earliest = Database::prepare("SELECT IFNULL(DATE(MIN(change_time)), CURDATE()) FROM `##change`")->execute(array())->fetchOne();
$latest = Database::prepare("SELECT IFNULL(DATE(MAX(change_time)), CURDATE()) FROM `##change`")->execute(array())->fetchOne();
// Filtering
$action = Filter::get('action');
$from = Filter::get('from', '\d\d\d\d-\d\d-\d\d', $earliest);
$to = Filter::get('to', '\d\d\d\d-\d\d-\d\d', $latest);
$type = Filter::get('type', 'accepted|rejected|pending');
$oldged = Filter::get('oldged');
$newged = Filter::get('newged');
$xref = Filter::get('xref', WT_REGEX_XREF);
$user = Filter::get('user');
$search = Filter::get('search');
$search = isset($search['value']) ? $search['value'] : null;
$statuses = array(
'accepted' => /* I18N: the status of an edit accepted/rejected/pending */ I18N::translate('accepted'),
'rejected' => /* I18N: the status of an edit accepted/rejected/pending */ I18N::translate('rejected'),
'pending' => /* I18N: the status of an edit accepted/rejected/pending */ I18N::translate('pending'),
);
if (Auth::isAdmin()) {
// Administrators can see all logs
$gedc = Filter::get('gedc');
} else {
// Managers can only see logs relating to this gedcom
$gedc = $WT_TREE->getName();
}
$sql_select =
"SELECT SQL_CACHE SQL_CALC_FOUND_ROWS change_id, change_time, status, xref, old_gedcom, new_gedcom, IFNULL(user_name, '<none>') AS user_name, IFNULL(gedcom_name, '<none>') AS gedcom_name" .
" FROM `##change`" .
" LEFT JOIN `##user` USING (user_id)" . // user may be deleted
" LEFT JOIN `##gedcom` USING (gedcom_id)"; // gedcom may be deleted
$where = " WHERE 1";
$args = array();
if ($search) {
$where .= " AND (old_gedcom LIKE CONCAT('%', :search_1, '%') OR new_gedcom LIKE CONCAT('%', :search_2, '%'))";
$args['search_1'] = $search;
$args['search_2'] = $search;
}
if ($from) {
$where .= " AND change_time >= :from";
$args['from'] = $from;
}
if ($to) {
$where .= " AND change_time < TIMESTAMPADD(DAY, 1 , :to)"; // before end of the day
$args['to'] = $to;
}
if ($type) {
$where .= " AND status = :status";
$args['status'] = $type;
}
if ($oldged) {
$where .= " AND old_gedcom LIKE CONCAT('%', :old_ged, '%')";
$args['old_ged'] = $oldged;
}
if ($newged) {
$where .= " AND new_gedcom LIKE CONCAT('%', :new_ged, '%')";
$args['new_ged'] = $newged;
}
if ($xref) {
$where .= " AND xref = :xref";
$args['xref'] = $xref;
}
if ($user) {
$where .= " AND user_name LIKE CONCAT('%', :user, '%')";
$args['user'] = $user;
}
if ($gedc) {
$where .= " AND gedcom_name LIKE CONCAT('%', :gedc, '%')";
$args['gedc'] = $gedc;
}
switch ($action) {
case 'delete':
$sql_delete =
"DELETE `##change` FROM `##change`" .
" LEFT JOIN `##user` USING (user_id)" . // user may be deleted
" LEFT JOIN `##gedcom` USING (gedcom_id)"; // gedcom may be deleted
Database::prepare($sql_delete . $where)->execute($args);
break;
case 'export':
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="webtrees-changes.csv"');
$rows = Database::prepare($sql_select . $where . ' ORDER BY change_id')->execute($args)->fetchAll();
foreach ($rows as $row) {
echo
'"', $row->change_time, '",',
'"', $row->status, '",',
'"', $row->xref, '",',
'"', str_replace('"', '""', $row->old_gedcom), '",',
'"', str_replace('"', '""', $row->new_gedcom), '",',
'"', str_replace('"', '""', $row->user_name), '",',
'"', str_replace('"', '""', $row->gedcom_name), '"',
"\n";
}
return;
case 'load_json':
$start = Filter::getInteger('start');
$length = Filter::getInteger('length');
$order = Filter::getArray('order');
if ($order) {
$order_by = " ORDER BY ";
foreach ($order as $key => $value) {
if ($key > 0) {
$order_by .= ',';
}
// Datatables numbers columns 0, 1, 2
// MySQL numbers columns 1, 2, 3
switch ($value['dir']) {
case 'asc':
$order_by .= (1 + $value['column']) . " ASC ";
break;
case 'desc':
$order_by .= (1 + $value['column']) . " DESC ";
break;
}
}
} else {
$order_by = " ORDER BY 1 DESC";
}
if ($length) {
Auth::user()->setPreference('admin_site_change_page_size', $length);
$limit = " LIMIT :limit OFFSET :offset";
$args['limit'] = $length;
$args['offset'] = $start;
} else {
$limit = "";
}
// This becomes a JSON list, not array, so need to fetch with numeric keys.
$rows = Database::prepare($sql_select . $where . $order_by . $limit)->execute($args)->fetchAll(PDO::FETCH_OBJ);
// Total filtered/unfiltered rows
$recordsFiltered = (int) Database::prepare("SELECT FOUND_ROWS()")->fetchOne();
$recordsTotal = (int) Database::prepare("SELECT COUNT(*) FROM `##change`")->fetchOne();
$data = array();
$algorithm = new MyersDiff;
foreach ($rows as $row) {
$old_lines = preg_split('/[\n]+/', $row->old_gedcom, -1, PREG_SPLIT_NO_EMPTY);
$new_lines = preg_split('/[\n]+/', $row->new_gedcom, -1, PREG_SPLIT_NO_EMPTY);
$differences = $algorithm->calculate($old_lines, $new_lines);
$diff_lines = array();
foreach ($differences as $difference) {
switch ($difference[1]) {
case MyersDiff::DELETE:
$diff_lines[] = '<del>' . $difference[0] . '</del>';
break;
case MyersDiff::INSERT:
$diff_lines[] = '<ins>' . $difference[0] . '</ins>';
break;
default:
$diff_lines[] = $difference[0];
}
}
// Only convert valid xrefs to links
$record = GedcomRecord::getInstance($row->xref, Tree::findByName($gedc));
$data[] = array(
$row->change_id,
$row->change_time,
I18N::translate($row->status),
$record ? '<a href="' . $record->getHtmlUrl() . '">' . $record->getXref() . '</a>' : $row->xref,
'<div class="gedcom-data" dir="ltr">' .
preg_replace_callback('/@(' . WT_REGEX_XREF . ')@/',
function ($match) use ($gedc) {
$record = GedcomRecord::getInstance($match[1], Tree::findByName($gedc));
return $record ? '<a href="#" onclick="return edit_raw(\'' . $match[1] . '\');">' . $match[0] . '</a>' : $match[0];
},
implode("\n", $diff_lines)
) .
'</div>',
$row->user_name,
$row->gedcom_name,
);
}
header('Content-type: application/json');
// See http://www.datatables.net/usage/server-side
echo json_encode(array(
'draw' => Filter::getInteger('draw'),
'recordsTotal' => $recordsTotal,
'recordsFiltered' => $recordsFiltered,
'data' => $data,
));
return;
}
$controller
->pageHeader()
->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
->addExternalJavascript(WT_DATATABLES_BOOTSTRAP_JS_URL)
->addExternalJavascript(WT_MOMENT_JS_URL)
->addExternalJavascript(WT_BOOTSTRAP_DATETIMEPICKER_JS_URL)
->addInlineJavascript('
jQuery(".table-site-changes").dataTable( {
processing: true,
serverSide: true,
ajax: "' . WT_BASE_URL . WT_SCRIPT_NAME . '?action=load_json&from=' . $from . '&to=' . $to . '&type=' . $type . '&oldged=' . rawurlencode($oldged) . '&newged=' . rawurlencode($newged) . '&xref=' . rawurlencode($xref) . '&user=' . rawurlencode($user) . '&gedc=' . rawurlencode($gedc) . '",
' . I18N::datatablesI18N(array(10, 20, 50, 100, 500, 1000, -1)) . ',
sorting: [[ 0, "desc" ]],
pageLength: ' . Auth::user()->getPreference('admin_site_change_page_size', 10) . ',
columns: [
/* change_id */ { visible: false },
/* Timestamp */ { sort: 0 },
/* Status */ { },
/* Record */ { },
/* Data */ {sortable: false},
/* User */ { },
/* Family tree */ { }
]
});
jQuery("#from, #to").parent("div").datetimepicker({
format: "YYYY-MM-DD",
minDate: "' . $earliest . '",
maxDate: "' . $latest . '",
locale: "' . WT_LOCALE . '",
useCurrent: false,
icons: {
time: "fa fa-clock-o",
date: "fa fa-calendar",
up: "fa fa-arrow-up",
down: "fa fa-arrow-down",
previous: "fa fa-arrow-' . (I18N::direction() === 'rtl' ? 'right' : 'left') . '",
next: "fa fa-arrow-' . (I18N::direction() === 'rtl' ? 'left' : 'right') . '",
today: "fa fa-trash-o",
clear: "fa fa-trash-o"
}
});
');
$users_array = array();
foreach (User::all() as $tmp_user) {
$users_array[$tmp_user->getUserName()] = $tmp_user->getUserName();
}
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel') ?></a></li>
<li><a href="admin_trees_manage.php"><?php echo I18N::translate('Manage family trees') ?></a></li>
<li class="active"><?php echo $controller->getPageTitle() ?></li>
</ol>
<h1><?php echo $controller->getPageTitle() ?></h1>
<form class="form" name="logs">
<input type="hidden" name="action" value="show">
<div class="row">
<div class="form-group col-xs-6 col-md-3">
<label for="from">
<?php echo /* I18N: label for the start of a date range (from x to y) */ I18N::translate('From') ?>
</label>
<div class="input-group date">
<input type="text" autocomplete="off" class="form-control" id="from" name="from" value="<?php echo Filter::escapeHtml($from) ?>">
<span class="input-group-addon"><span class="fa fa-calendar"></span></span>
</div>
</div>
<div class="form-group col-xs-6 col-md-3">
<label for="to">
<?php echo /* I18N: label for the end of a date range (from x to y) */ I18N::translate('To') ?>
</label>
<div class="input-group date">
<input type="text" autocomplete="off" class="form-control" id="to" name="to" value="<?php echo Filter::escapeHtml($to) ?>">
<span class="input-group-addon"><span class="fa fa-calendar"></span></span>
</div>
</div>
<div class="form-group col-xs-6 col-md-3">
<label for="type">
<?php echo I18N::translate('Status') ?>
</label>
<?php echo FunctionsEdit::selectEditControl('type', $statuses, '', $type, 'class="form-control"') ?>
</div>
<div class="form-group col-xs-6 col-md-3">
<label for="xref">
<?php echo I18N::translate('Record') ?>
</label>
<input class="form-control" type="text" id="xref" name="xref" value="<?php echo Filter::escapeHtml($xref) ?>">
</div>
</div>
<div class="row">
<div class="form-group col-xs-6 col-md-3">
<label for="oldged">
<?php echo I18N::translate('Old data') ?>
</label>
<input class="form-control" type="text" id="oldged" name="oldged" value="<?php echo Filter::escapeHtml($oldged) ?>">
</div>
<div class="form-group col-xs-6 col-md-3">
<label for="newged">
<?php echo I18N::translate('New data') ?>
</label>
<input class="form-control" type="text" id="newged" name="newged" value="<?php echo Filter::escapeHtml($newged) ?>">
</div>
<div class="form-group col-xs-6 col-md-3">
<label for="user">
<?php echo I18N::translate('User') ?>
</label>
<?php echo FunctionsEdit::selectEditControl('user', $users_array, '', $user, 'class="form-control"') ?>
</div>
<div class="form-group col-xs-6 col-md-3">
<label for="gedc">
<?php echo I18N::translate('Family tree') ?>
</label>
<?php echo FunctionsEdit::selectEditControl('gedc', Tree::getNameList(), '', $gedc, Auth::isAdmin() ? 'class="form-control"' : 'disabled class="form-control"') ?>
</div>
</div>
<div class="row text-center">
<button type="submit" class="btn btn-primary">
<?php echo I18N::translate('search') ?>
</button>
<button type="submit" class="btn btn-primary" onclick="document.logs.action.value='export';return true;" <?php echo $action === 'show' ? '' : 'disabled' ?>>
<?php echo /* I18N: A button label. */ I18N::translate('download') ?>
</button>
<button type="submit" class="btn btn-primary" onclick="if (confirm('<?php echo I18N::translate('Permanently delete these records?') ?>')) {document.logs.action.value='delete'; return true;} else {return false;}" <?php echo $action === 'show' ? '' : 'disabled' ?>>
<?php echo I18N::translate('delete') ?>
</button>
</div>
</form>
<?php if ($action): ?>
<table class="table table-bordered table-condensed table-hover table-site-changes">
<caption class="sr-only">
<?php echo $controller->getPageTitle() ?>
</caption>
<thead>
<tr>
<th></th>
<th><?php echo I18N::translate('Timestamp') ?></th>
<th><?php echo I18N::translate('Status') ?></th>
<th><?php echo I18N::translate('Record') ?></th>
<th><?php echo I18N::translate('Data') ?></th>
<th><?php echo I18N::translate('User') ?></th>
<th><?php echo I18N::translate('Family tree') ?></th>
</tr>
</thead>
</table>
<?php endif ?>

View file

@ -1,117 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
use Fisharebest\Webtrees\Controller\PageController;
use Rhumsaa\Uuid\Uuid;
define('WT_SCRIPT_NAME', 'admin_site_clean.php');
require './includes/session.php';
$to_delete = Filter::postArray('to_delete');
if ($to_delete && Filter::checkCsrf()) {
foreach ($to_delete as $path) {
$is_dir = is_dir(WT_DATA_DIR . $path);
if (File::delete(WT_DATA_DIR . $path)) {
if ($is_dir) {
FlashMessages::addMessage(I18N::translate('The folder %s has been deleted.', Filter::escapeHtml($path)), 'success');
} else {
FlashMessages::addMessage(I18N::translate('The file %s has been deleted.', Filter::escapeHtml($path)), 'success');
}
} else {
if ($is_dir) {
FlashMessages::addMessage(I18N::translate('The folder %s could not be deleted.', Filter::escapeHtml($path)), 'danger');
} else {
FlashMessages::addMessage(I18N::translate('The file %s could not be deleted.', Filter::escapeHtml($path)), 'danger');
}
}
}
header('Location: ' . WT_BASE_URL . WT_SCRIPT_NAME);
return;
}
$controller = new PageController;
$controller
->restrictAccess(Auth::isAdmin())
->setPageTitle(/* I18N: The “Data folder” is a configuration setting */ I18N::translate('Clean up data folder'))
->pageHeader();
$do_not_delete = array('index.php', 'config.ini.php', 'language');
// If we are storing the media in the data folder (this is the default), then dont delete it.
foreach (Tree::getAll() as $tree) {
$MEDIA_DIRECTORY = $tree->getPreference('MEDIA_DIRECTORY');
if (substr($MEDIA_DIRECTORY, 0, 3) != '../') {
// Just need to add the first part of the path
$tmp = explode('/', $MEDIA_DIRECTORY);
$do_not_delete[] = $tmp[0];
}
}
$locked_icon = '<i class="fa fa-ban text-danger"></i>';
$dir = dir(WT_DATA_DIR);
$entries = array();
while (false !== ($entry = $dir->read())) {
if ($entry[0] != '.') {
$entries[] = $entry;
}
}
sort($entries);
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li class="active"><?php echo $controller->getPageTitle(); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<p>
<?php echo I18N::translate('Files marked with %s are required for proper operation and cannot be removed.', $locked_icon); ?>
</p>
<form method="post">
<?php echo Filter::getCsrf(); ?>
<fieldset>
<legend class="sr-only"><?php echo $controller->getPageTitle(); ?></legend>
<ul class="fa-ul">
<?php
foreach ($entries as $entry) {
if (in_array($entry, $do_not_delete)) {
echo '<li><i class="fa-li fa fa-ban text-danger"></i>', Filter::escapeHtml($entry), '</li>';
} else {
$id = 'input-' . Uuid::uuid4();
echo '<li><i class="fa-li fa fa-trash-o"></i>';
echo '<label for="', $id, '">';
echo '<input type="checkbox" id="', $id, '" name="to_delete[]" value="', Filter::escapeHtml($entry), '"> ';
echo Filter::escapeHtml($entry);
echo '</label></li>';
}
}
$dir->close();
?>
</ul>
</fieldset>
<button class="btn btn-danger" type="submit">
<i class="fa fa-trash-o"></i>
<?php echo /* I18N: A button label. */ I18N::translate('delete'); ?>
</button>
</form>

View file

@ -1,655 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
use Fisharebest\Webtrees\Controller\PageController;
use Fisharebest\Webtrees\Functions\FunctionsEdit;
define('WT_SCRIPT_NAME', 'admin_site_config.php');
require './includes/session.php';
$controller = new PageController;
$controller->restrictAccess(Auth::isAdmin());
switch (Filter::post('action')) {
case 'site':
if (Filter::checkCsrf()) {
$INDEX_DIRECTORY = Filter::post('INDEX_DIRECTORY');
if (substr($INDEX_DIRECTORY, -1) !== '/') {
$INDEX_DIRECTORY .= '/';
}
if (File::mkdir($INDEX_DIRECTORY)) {
Site::setPreference('INDEX_DIRECTORY', $INDEX_DIRECTORY);
} else {
FlashMessages::addMessage(I18N::translate('The folder %s does not exist, and it could not be created.', Filter::escapeHtml($INDEX_DIRECTORY)), 'danger');
}
Site::setPreference('MEMORY_LIMIT', Filter::post('MEMORY_LIMIT'));
Site::setPreference('MAX_EXECUTION_TIME', Filter::post('MAX_EXECUTION_TIME'));
Site::setPreference('ALLOW_USER_THEMES', Filter::postBool('ALLOW_USER_THEMES'));
Site::setPreference('THEME_DIR', Filter::post('THEME_DIR'));
Site::setPreference('ALLOW_CHANGE_GEDCOM', Filter::postBool('ALLOW_CHANGE_GEDCOM'));
Site::setPreference('SESSION_TIME', Filter::post('SESSION_TIME'));
Site::setPreference('SERVER_URL', Filter::post('SERVER_URL'));
Site::setPreference('TIMEZONE', Filter::post('TIMEZONE'));
FlashMessages::addMessage(I18N::translate('The website preferences have been updated.'), 'success');
}
header('Location: ' . WT_BASE_URL . 'admin.php');
return;
case 'email':
if (Filter::checkCsrf()) {
Site::setPreference('SMTP_ACTIVE', Filter::post('SMTP_ACTIVE'));
Site::setPreference('SMTP_FROM_NAME', Filter::post('SMTP_FROM_NAME'));
Site::setPreference('SMTP_HOST', Filter::post('SMTP_HOST'));
Site::setPreference('SMTP_PORT', Filter::post('SMTP_PORT'));
Site::setPreference('SMTP_AUTH', Filter::post('SMTP_AUTH'));
Site::setPreference('SMTP_AUTH_USER', Filter::post('SMTP_AUTH_USER'));
Site::setPreference('SMTP_SSL', Filter::post('SMTP_SSL'));
Site::setPreference('SMTP_HELO', Filter::post('SMTP_HELO'));
if (Filter::post('SMTP_AUTH_PASS')) {
Site::setPreference('SMTP_AUTH_PASS', Filter::post('SMTP_AUTH_PASS'));
}
FlashMessages::addMessage(I18N::translate('The website preferences have been updated.'), 'success');
}
header('Location: ' . WT_BASE_URL . 'admin.php');
return;
case 'login':
if (Filter::checkCsrf()) {
Site::setPreference('LOGIN_URL', Filter::post('LOGIN_URL'));
Site::setPreference('WELCOME_TEXT_AUTH_MODE', Filter::post('WELCOME_TEXT_AUTH_MODE'));
Site::setPreference('WELCOME_TEXT_AUTH_MODE_' . WT_LOCALE, Filter::post('WELCOME_TEXT_AUTH_MODE_4'));
Site::setPreference('USE_REGISTRATION_MODULE', Filter::post('USE_REGISTRATION_MODULE'));
Site::setPreference('SHOW_REGISTER_CAUTION', Filter::post('SHOW_REGISTER_CAUTION'));
FlashMessages::addMessage(I18N::translate('The website preferences have been updated.'), 'success');
}
header('Location: ' . WT_BASE_URL . 'admin.php');
return;
case 'tracking':
if (Filter::checkCsrf()) {
Site::setPreference('BING_WEBMASTER_ID', Filter::post('BING_WEBMASTER_ID'));
Site::setPreference('GOOGLE_WEBMASTER_ID', Filter::post('GOOGLE_WEBMASTER_ID'));
Site::setPreference('GOOGLE_ANALYTICS_ID', Filter::post('GOOGLE_ANALYTICS_ID'));
Site::setPreference('PIWIK_URL', Filter::post('PIWIK_URL'));
Site::setPreference('PIWIK_SITE_ID', Filter::post('PIWIK_SITE_ID'));
Site::setPreference('STATCOUNTER_PROJECT_ID', Filter::post('STATCOUNTER_PROJECT_ID'));
Site::setPreference('STATCOUNTER_SECURITY_ID', Filter::post('STATCOUNTER_SECURITY_ID'));
FlashMessages::addMessage(I18N::translate('The website preferences have been updated.'), 'success');
}
header('Location: ' . WT_BASE_URL . 'admin.php');
return;
case 'languages':
if (Filter::checkCsrf()) {
Site::setPreference('LANGUAGES', implode(',', Filter::postArray('LANGUAGES')));
FlashMessages::addMessage(I18N::translate('The website preferences have been updated.'), 'success');
}
header('Location: ' . WT_BASE_URL . 'admin.php');
return;
}
// Lists of options for <select> controls.
$SMTP_SSL_OPTIONS = array(
'none' => I18N::translate('none'),
/* I18N: Secure Sockets Layer - a secure communications protocol*/ 'ssl' => I18N::translate('ssl'),
/* I18N: Transport Layer Security - a secure communications protocol */ 'tls' => I18N::translate('tls'),
);
$SMTP_ACTIVE_OPTIONS = array(
'internal' => I18N::translate('Use PHP mail to send messages'),
'external' => I18N::translate('Use SMTP to send messages'),
);
$WELCOME_TEXT_AUTH_MODE_OPTIONS = array(
0 => I18N::translate('No predefined text'),
1 => I18N::translate('Predefined text that states all users can request a user account'),
2 => I18N::translate('Predefined text that states admin will decide on each request for a user account'),
3 => I18N::translate('Predefined text that states only family members can request a user account'),
4 => I18N::translate('Choose user defined welcome text typed below'),
);
$language_tags = array();
foreach (I18N::activeLocales() as $active_locale) {
$language_tags[] = $active_locale->languageTag();
}
switch (Filter::get('action')) {
case 'site':
$controller->setPageTitle(I18N::translate('Website preferences'));
break;
case 'email':
$controller->setPageTitle(I18N::translate('Sending email'));
break;
case 'login':
$controller->setPageTitle(I18N::translate('Sign-in and registration'));
break;
case 'tracking':
$controller->setPageTitle(/* I18N: e.g. http://www.google.com/analytics */ I18N::translate('Tracking and analytics'));
break;
case 'languages':
$controller->setPageTitle(I18N::translate('Languages'));
break;
default:
header('Location: ' . WT_BASE_URL . 'admin.php');
return;
}
$controller->pageHeader();
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel') ?></a></li>
<li class="active"><?php echo $controller->getPageTitle() ?></li>
</ol>
<h1><?php echo $controller->getPageTitle() ?></h1>
<form method="post" class="form-horizontal">
<?php echo Filter::getCsrf() ?>
<?php if (Filter::get('action') === 'site'): ?>
<input type="hidden" name="action" value="site">
<!-- INDEX_DIRECTORY -->
<div class="form-group">
<label for="INDEX_DIRECTORY" class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Data folder') ?>
</label>
<div class="col-sm-9">
<input type="text" class="form-control" dir="ltr" id="INDEX_DIRECTORY" name="INDEX_DIRECTORY" value="<?php echo Filter::escapeHtml(Site::getPreference('INDEX_DIRECTORY')) ?>" maxlength="255" placeholder="data/" required>
<p class="small text-muted">
<?php echo /* I18N: Help text for the "Data folder" site configuration setting */ I18N::translate('This folder will be used by webtrees to store media files, GEDCOM files, temporary files, etc. These files may contain private data, and should not be made available over the internet.') ?>
</p>
<p class="small text-muted">
<?php echo /* I18N: “Apache” is a software program. */ I18N::translate('To protect this private data, webtrees uses an Apache configuration file (.htaccess) which blocks all access to this folder. If your web-server does not support .htaccess files, and you cannot restrict access to this folder, then you can select another folder, away from your web documents.') ?>
</p>
<p class="small text-muted">
<?php echo I18N::translate('If you select a different folder, you must also move all files (except config.ini.php, index.php, and .htaccess) from the existing folder to the new folder.') ?>
</p>
<p class="small text-muted">
<?php echo I18N::translate('The folder can be specified in full (e.g. /home/user_name/webtrees_data/) or relative to the installation folder (e.g. ../../webtrees_data/).') ?>
</p>
</div>
</div>
<!-- MEMORY_LIMIT -->
<div class="form-group">
<label for="MEMORY_LIMIT" class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Memory limit') ?>
</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="MEMORY_LIMIT" name="MEMORY_LIMIT" value="<?php echo Filter::escapeHtml(Site::getPreference('MEMORY_LIMIT')) ?>" pattern="[0-9]+[KMG]" placeholder="<?php echo get_cfg_var('memory_limit') ?>" maxlength="255">
<p class="small text-muted">
<?php echo /* I18N: %s is an amount of memory, such as 32MB */ I18N::translate('By default, your server allows scripts to use %s of memory.', get_cfg_var('memory_limit')) ?>
<?php echo I18N::translate('You can request a higher or lower limit, although the server may ignore this request.') ?>
<?php echo I18N::translate('Leave this blank to use the default value.') ?>
</p>
</div>
</div>
<!-- MAX_EXECUTION_TIME -->
<div class="form-group">
<label for="MAX_EXECUTION_TIME" class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('PHP time limit') ?>
</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="MAX_EXECUTION_TIME" name="MAX_EXECUTION_TIME" value="<?php echo Filter::escapeHtml(Site::getPreference('MAX_EXECUTION_TIME')) ?>" pattern="[0-9]*" placeholder="<?php echo get_cfg_var('max_execution_time') ?>" maxlength="255">
<p class="small text-muted">
<?php echo I18N::plural(
'By default, your server allows scripts to run for %s second.',
'By default, your server allows scripts to run for %s seconds.',
get_cfg_var('max_execution_time'), I18N::number(get_cfg_var('max_execution_time')));
?>
<?php echo I18N::translate('You can request a higher or lower limit, although the server may ignore this request.') ?>
<?php echo I18N::translate('Leave this blank to use the default value.') ?>
</p>
</div>
</div>
<!-- TIMEZONE -->
<div class="form-group">
<label for="TIMEZONE" class="col-sm-3 control-label">
<?php echo I18N::translate('Time zone') ?>
</label>
<div class="col-sm-9">
<?php echo FunctionsEdit::selectEditControl('TIMEZONE', array_combine(\DateTimeZone::listIdentifiers(), \DateTimeZone::listIdentifiers()), null, Site::getPreference('TIMEZONE') ?: 'UTC', 'class="form-control"') ?>
<p class="small text-muted">
<?php echo I18N::translate('The time zone is required for date calculations, such as knowing todays date.') ?>
</p>
</div>
</div>
<!-- THEME_DIR -->
<div class="form-group">
<label for="THEME_DIR" class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Default theme') ?>
</label>
<div class="col-sm-9">
<?php echo FunctionsEdit::selectEditControl('THEME_DIR', Theme::themeNames(), null, Site::getPreference('THEME_DIR'), 'class="form-control"') ?>
<p class="small text-muted">
<?php echo /* I18N: Help text for the "Default theme" site configuration setting */ I18N::translate('You can change the appearance of webtrees using “themes”. Each theme has a different style, layout, color scheme, etc.') ?>
</p>
<p class="small text-muted">
<?php echo I18N::translate('Themes can be selected at three levels: user, family tree, and website. User preferences take priority over family tree preferences, which in turn take priority over the website preferences. Selecting “default theme” at one level will use the theme at the next level.') ?>
</p>
</div>
</div>
<!-- ALLOW_USER_THEMES -->
<fieldset class="form-group">
<legend class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Allow users to select their own theme') ?>
</legend>
<div class="col-sm-9">
<?php echo FunctionsEdit::editFieldYesNo('ALLOW_USER_THEMES', Site::getPreference('ALLOW_USER_THEMES'), 'class="radio-inline"') ?>
<p class="small text-muted">
</p>
</div>
</fieldset>
<!-- ALLOW_CHANGE_GEDCOM -->
<fieldset class="form-group">
<legend class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Show list of family trees') ?>
</legend>
<div class="col-sm-9">
<?php echo FunctionsEdit::editFieldYesNo('ALLOW_CHANGE_GEDCOM', Site::getPreference('ALLOW_CHANGE_GEDCOM'), 'class="radio-inline"') ?>
<p class="small text-muted">
<?php echo /* I18N: Help text for the “Show list of family trees” site configuration setting */ I18N::translate('For websites with more than one family tree, this option will show the list of family trees in the main menu, the search pages, etc.') ?>
</p>
</div>
</fieldset>
<!-- SESSION_TIME -->
<div class="form-group">
<label for="SESSION_TIME" class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Session timeout') ?>
</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="SESSION_TIME" name="SESSION_TIME" value="<?php echo Filter::escapeHtml(Site::getPreference('SESSION_TIME')) ?>" pattern="[0-9]*" placeholder="7200" maxlength="255">
<p class="small text-muted">
<?php echo /* I18N: Help text for the “Session timeout” site configuration setting */ I18N::translate('The time in seconds that a webtrees session remains active before requiring a new sign-in. The default is 7200, which is 2 hours.') ?>
<?php echo I18N::translate('Leave this blank to use the default value.') ?>
</p>
</div>
</div>
<!-- SERVER_URL -->
<div class="form-group">
<label for="SERVER_URL" class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Website URL') ?>
</label>
<div class="col-sm-9">
<?php echo FunctionsEdit::selectEditControl('SERVER_URL', array(WT_BASE_URL => WT_BASE_URL), '', Site::getPreference('SERVER_URL'), 'class="form-control"') ?>
<p class="small text-muted">
<?php echo /* I18N: Help text for the "Website URL" site configuration setting */ I18N::translate('If your website can be reached using more than one URL, such as <b>http://www.example.com/webtrees/</b> and <b>http://webtrees.example.com/</b>, you can specify the preferred URL. Requests for the other URLs will be redirected to the preferred one.') ?>
<?php echo I18N::translate('Leave this blank to use the default value.') ?>
</p>
</div>
</div>
<?php elseif (Filter::get('action') === 'email'): ?>
<input type="hidden" name="action" value="email">
<!-- SMTP_ACTIVE -->
<div class="form-group">
<label for="SMTP_ACTIVE" class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Messages') ?>
</label>
<div class="col-sm-9">
<?php echo FunctionsEdit::selectEditControl('SMTP_ACTIVE', $SMTP_ACTIVE_OPTIONS, null, Site::getPreference('SMTP_ACTIVE'), 'class="form-control"') ?>
<p class="small text-muted">
<?php echo /* I18N: Help text for the “Messages” site configuration setting */ I18N::translate('webtrees needs to send emails, such as password reminders and website notifications. To do this, it can use this servers built in PHP mail facility (which is not always available) or an external SMTP (mail-relay) service, for which you will need to provide the connection details.') ?>
</p>
</div>
</div>
<!-- SMTP_FROM_NAME -->
<div class="form-group">
<label for="SMTP_FROM_NAME" class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Sender name') ?>
</label>
<div class="col-sm-9">
<input type="email" class="form-control" id="SMTP_FROM_NAME" name="SMTP_FROM_NAME" value="<?php echo Filter::escapeHtml(Site::getPreference('SMTP_FROM_NAME')) ?>" placeholder="no-reply@localhost" maxlength="255">
<p class="small text-muted">
<?php echo /* I18N: Help text for the “Sender name” site configuration setting */ I18N::translate('This name is used in the “From” field, when sending automatic emails from this server.') ?>
</p>
</div>
</div>
<h2><?php echo I18N::translate('SMTP mail server') ?></h2>
<!-- SMTP_HOST -->
<div class="form-group">
<label for="SMTP_HOST" class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Server name') ?>
</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="SMTP_HOST" name="SMTP_HOST" value="<?php echo Filter::escapeHtml(Site::getPreference('SMTP_HOST')) ?>" placeholder="smtp.example.com" maxlength="255" pattern="[a-z0-9-]+(\.[a-z0-9-]+)*">
<p class="small text-muted">
<?php echo /* I18N: Help text for the “Server name” site configuration setting */ I18N::translate('This is the name of the SMTP server. “localhost” means that the mail service is running on the same computer as your web server.') ?>
</p>
</div>
</div>
<!-- SMTP_PORT -->
<div class="form-group">
<label for="SMTP_PORT" class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Port number') ?>
</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="SMTP_PORT" name="SMTP_PORT" value="<?php echo Filter::escapeHtml(Site::getPreference('SMTP_PORT')) ?>" pattern="[0-9]*" placeholder="25" maxlength="5">
<p class="small text-muted">
<?php echo /* I18N: Help text for the "Port number" site configuration setting */ I18N::translate('By default, SMTP works on port 25.') ?>
</p>
</div>
</div>
<!-- SMTP_AUTH -->
<fieldset class="form-group">
<legend class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Use password') ?>
</legend>
<div class="col-sm-9">
<?php echo FunctionsEdit::editFieldYesNo('SMTP_AUTH', Site::getPreference('SMTP_AUTH'), 'class="radio-inline"') ?>
<p class="small text-muted">
<?php echo /* I18N: Help text for the “Use password” site configuration setting */ I18N::translate('Most SMTP servers require a password.') ?>
</p>
</div>
</fieldset>
<!-- SMTP_AUTH_USER -->
<div class="form-group">
<label for="SMTP_AUTH_USER" class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Username') ?>
</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="SMTP_AUTH_USER" name="SMTP_AUTH_USER" value="<?php echo Filter::escapeHtml(Site::getPreference('SMTP_AUTH_USER')) ?>" maxlength="255">
<p class="small text-muted">
<?php echo /* I18N: Help text for the "Username" site configuration setting */ I18N::translate('The username required for authentication with the SMTP server.') ?>
</p>
</div>
</div>
<!-- SMTP_AUTH_PASS -->
<div class="form-group">
<label for="SMTP_AUTH_PASS" class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Password') ?>
</label>
<div class="col-sm-9">
<input type="password" class="form-control" id="SMTP_AUTH_PASS" name="SMTP_AUTH_PASS" value="">
<p class="small text-muted">
<?php echo /* I18N: Help text for the "Password" site configuration setting */ I18N::translate('The password required for authentication with the SMTP server.') ?>
</p>
</div>
</div>
<!-- SMTP_SSL -->
<div class="form-group">
<label for="SMTP_SSL" class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Secure connection') ?>
</label>
<div class="col-sm-9">
<?php echo FunctionsEdit::selectEditControl('SMTP_SSL', $SMTP_SSL_OPTIONS, null, Site::getPreference('SMTP_SSL'), 'class="form-control"') ?>
<p class="small text-muted">
<?php echo /* I18N: Help text for the “Secure connection” site configuration setting */ I18N::translate('Most servers do not use secure connections.') ?>
</p>
</div>
</div>
<!-- SMTP_HELO -->
<div class="form-group">
<label for="SMTP_HELO" class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Sending server name') ?>
</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="SMTP_HELO" name="SMTP_HELO" value="<?php echo Filter::escapeHtml(Site::getPreference('SMTP_HELO')) ?>" placeholder="localhost" maxlength="255" pattern="[a-z0-9-]+(\.[a-z0-9-]+)*">
<p class="small text-muted">
<?php echo /* I18N: Help text for the "Sending server name" site configuration setting */ I18N::translate('Many mail servers require that the sending server identifies itself correctly, using a valid domain name.') ?>
</p>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9">
<p class="small text-muted">
<?php echo Theme::theme()->htmlAlert(I18N::translate('To use a Google mail account, use the following settings: server=smtp.gmail.com, port=587, security=tls, username=xxxxx@gmail.com, password=[your gmail password]') . '<br>' . I18N::translate('You must also enable “less secure applications” in your Google account.') . ' <a href="https://www.google.com/settings/security/lesssecureapps">https://www.google.com/settings/security/lesssecureapps</a>', 'info', false) ?>
</p>
</div>
</div>
<?php elseif (Filter::get('action') === 'login'): ?>
<input type="hidden" name="action" value="login">
<!-- LOGIN_URL -->
<div class="form-group">
<label for="LOGIN_URL" class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Sign-in URL') ?>
</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="LOGIN_URL" name="LOGIN_URL" value="<?php echo Filter::escapeHtml(Site::getPreference('LOGIN_URL')) ?>" maxlength="255">
<p class="small text-muted">
<?php echo /* I18N: Help text for the "Login URL" site configuration setting */ I18N::translate('You only need to enter a Sign-in URL if you want to redirect to a different website or location when your users sign in. This is very useful if you need to switch from http to https when your users sign in. Include the full URL to <i>login.php</i>. For example, https://www.yourserver.com/webtrees/login.php .') ?>
</p>
</div>
</div>
<!-- WELCOME_TEXT_AUTH_MODE -->
<div class="form-group">
<label for="WELCOME_TEXT_AUTH_MODE" class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Welcome text on sign-in page') ?>
</label>
<div class="col-sm-9">
<?php echo FunctionsEdit::selectEditControl('WELCOME_TEXT_AUTH_MODE', $WELCOME_TEXT_AUTH_MODE_OPTIONS, null, Site::getPreference('WELCOME_TEXT_AUTH_MODE'), 'class="form-control"') ?>
<p class="small text-muted">
</p>
</div>
</div>
<!-- LOGIN_URL -->
<div class="form-group">
<label for="WELCOME_TEXT_AUTH_MODE_4" class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Custom welcome text') ?>
</label>
<div class="col-sm-9">
<textarea class="form-control" maxlength="2000" id="WELCOME_TEXT_AUTH_MODE_4" name="WELCOME_TEXT_AUTH_MODE_4" rows="4"><?php echo Filter::escapeHtml(Site::getPreference('WELCOME_TEXT_AUTH_MODE_' . WT_LOCALE)) ?></textarea>
<p class="small text-muted">
<?php echo /* I18N: Help text for the "Custom welcome text" site configuration setting */ I18N::translate('To set this text for other languages, you must switch to that language, and visit this page again.') ?>
</p>
</div>
</div>
<!-- USE_REGISTRATION_MODULE -->
<fieldset class="form-group">
<legend class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Allow visitors to request a new user account') ?>
</legend>
<div class="col-sm-9">
<?php echo FunctionsEdit::editFieldYesNo('USE_REGISTRATION_MODULE', Site::getPreference('USE_REGISTRATION_MODULE'), 'class="radio-inline"') ?>
<p class="small text-muted">
<?php echo I18N::translate('The new user will be asked to confirm their email address before the account is created.') ?>
<?php echo I18N::translate('Details of the new user will be sent to the genealogy contact for the corresponding family tree.') ?>
<?php echo I18N::translate('An administrator must approve the new user account and select an access level before the user can sign in.') ?>
</p>
</div>
</fieldset>
<!-- SHOW_REGISTER_CAUTION -->
<fieldset class="form-group">
<legend class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Show acceptable use agreement on “Request a new user account” page') ?>
</legend>
<div class="col-sm-9">
<?php echo FunctionsEdit::editFieldYesNo('SHOW_REGISTER_CAUTION', Site::getPreference('SHOW_REGISTER_CAUTION'), 'class="radio-inline"') ?>
<p class="small text-muted">
</p>
</div>
</fieldset>
<?php elseif (Filter::get('action') === 'tracking'): ?>
<input type="hidden" name="action" value="tracking">
<p>
<?php echo I18N::translate('If you use one of the following tracking and analytics services, webtrees can add the tracking codes automatically.') ?>
</p>
<h2><a href="https://http://www.bing.com/toolbox/webmaster/">Bing Webmaster Tools</a></h2>
<!-- BING_WEBMASTER_ID -->
<div class="form-group">
<label for="BING_WEBMASTER_ID" class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Site verification code') ?>
<span class="sr-only">Google Webmaster Tools</span>
</label>
<div class="col-sm-9">
<input
type="text" class="form-control"
id="BING_WEBMASTER_ID" name="BING_WEBMASTER_ID" <?php echo dirname(parse_url(WT_BASE_URL, PHP_URL_PATH)) === '/' ? '' : 'disabled' ?>
value="<?php echo Filter::escapeHtml(Site::getPreference('BING_WEBMASTER_ID')) ?>"
maxlength="255" pattern="[0-9a-zA-Z+=/_:.!-]*"
>
<p class="small text-muted">
<?php echo /* I18N: Help text for the "Site verification code for Google Webmaster Tools" site configuration setting */ I18N::translate('Site verification codes do not work when webtrees is installed in a subfolder.') ?>
</p>
</div>
</div>
<h2><a href="https://www.google.com/webmasters/">Google Webmaster Tools</a></h2>
<!-- GOOGLE_WEBMASTER_ID -->
<div class="form-group">
<label for="GOOGLE_WEBMASTER_ID" class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Site verification code') ?>
<span class="sr-only">Google Webmaster Tools</span>
</label>
<div class="col-sm-9">
<input
type="text" class="form-control"
id="GOOGLE_WEBMASTER_ID" name="GOOGLE_WEBMASTER_ID" <?php echo dirname(parse_url(WT_BASE_URL, PHP_URL_PATH)) === '/' ? '' : 'disabled' ?>
value="<?php echo Filter::escapeHtml(Site::getPreference('GOOGLE_WEBMASTER_ID')) ?>"
maxlength="255" pattern="[0-9a-zA-Z+=/_:.!-]*"
>
<p class="small text-muted">
<?php echo /* I18N: Help text for the "Site verification code for Google Webmaster Tools" site configuration setting */ I18N::translate('Site verification codes do not work when webtrees is installed in a subfolder.') ?>
</p>
</div>
</div>
<h2><a href="http://www.google.com/analytics/">Google Analytics</a></h2>
<!-- GOOGLE_ANALYTICS_ID -->
<div class="form-group">
<label for="GOOGLE_ANALYTICS_ID" class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Site identification code') ?>
<span class="sr-only">Google Analytics</span>
</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="GOOGLE_ANALYTICS_ID" name="GOOGLE_ANALYTICS_ID" value="<?php echo Filter::escapeHtml(Site::getPreference('GOOGLE_ANALYTICS_ID')) ?>" placeholder="UA-12345-6" maxlength="255" pattern="UA-[0-9]+-[0-9]+">
<p class="small text-muted">
<?php echo I18N::translate('Tracking and analytics are not added to the control panel.') ?>
</p>
</div>
</div>
<h2><a href="https://piwik.org/">Piwik</a></h2>
<!-- PIWIK_SITE_ID -->
<div class="form-group">
<label for="PIWIK_SITE_ID" class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Site identification code') ?>
</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="PIWIK_SITE_ID" name="PIWIK_SITE_ID" value="<?php echo Filter::escapeHtml(Site::getPreference('PIWIK_SITE_ID')) ?>" maxlength="255" pattern="[0-9]+">
</div>
</div>
<!-- PIWIK_URL -->
<div class="form-group">
<label for="PIWIK_URL" class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('URL') ?>
</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="PIWIK_URL" name="PIWIK_URL" value="<?php echo Filter::escapeHtml(Site::getPreference('PIWIK_URL')) ?>" placeholder="example.com/piwik" maxlength="255">
<p class="small text-muted">
<?php echo I18N::translate('Tracking and analytics are not added to the control panel.') ?>
</p>
</div>
</div>
<h2><a href="https://statcounter.com/">StatCounter</a></h2>
<!-- STATCOUNTER_PROJECT_ID -->
<div class="form-group">
<label for="STATCOUNTER_PROJECT_ID" class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Site identification code') ?>
</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="STATCOUNTER_PROJECT_ID" name="STATCOUNTER_PROJECT_ID" value="<?php echo Filter::escapeHtml(Site::getPreference('STATCOUNTER_PROJECT_ID')) ?>" maxlength="255" pattern="[0-9]+">
</div>
</div>
<!-- STATCOUNTER_SECURITY_ID -->
<div class="form-group">
<label for="STATCOUNTER_SECURITY_ID" class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Security code') ?>
</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="STATCOUNTER_SECURITY_ID" name="STATCOUNTER_SECURITY_ID" value="<?php echo Filter::escapeHtml(Site::getPreference('STATCOUNTER_SECURITY_ID')) ?>" maxlength="255" pattern="[0-9a-zA-Z]+">
<p class="small text-muted">
<?php echo I18N::translate('Tracking and analytics are not added to the control panel.') ?>
</p>
</div>
</div>
<?php elseif (Filter::get('action') === 'languages'): ?>
<input type="hidden" name="action" value="languages">
<p>
<?php echo I18N::translate('Select the languages that will be shown in menus.') ?>
</p>
<fieldset class="form-group">
<legend class="col-sm-3 control-label">
<?php echo /* I18N: A configuration setting */ I18N::translate('Language') ?>
</legend>
<div class="col-sm-9" style="columns: 4 150px;-moz-columns: 4 150px;">
<?php foreach (I18N::installedLocales() as $installed_locale): ?>
<div class="checkbox">
<label title="<?php echo $installed_locale->languageTag() ?>">
<input type="checkbox" name="LANGUAGES[]" value="<?php echo $installed_locale->languageTag() ?>" <?php echo in_array($installed_locale->languageTag(), $language_tags) ? 'checked' : '' ?>>
<?php echo $installed_locale->endonym() ?>
</label>
</div>
<?php endforeach ?>
</div>
</fieldset>
<?php endif ?>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9">
<button type="submit" class="btn btn-primary">
<i class="fa fa-check"></i>
<?php echo I18N::translate('save') ?>
</button>
</div>
</div>
</form>

View file

@ -1,81 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
use Fisharebest\Webtrees\Controller\PageController;
define('WT_SCRIPT_NAME', 'admin_site_info.php');
require './includes/session.php';
$controller = new PageController;
$controller
->restrictAccess(Auth::isAdmin())
->setPageTitle(I18N::translate('Server information'))
->pageHeader();
$variables = Database::prepare("SHOW VARIABLES")->fetchAssoc();
array_walk($variables, function (&$x) { $x = str_replace(',', ', ', $x); });
ob_start();
phpinfo(INFO_ALL & ~INFO_CREDITS & ~INFO_LICENSE);
preg_match('%<body>(.*)</body>%s', ob_get_clean(), $matches);
$html = $matches[1];
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li class="active"><?php echo $controller->getPageTitle(); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<div class="row">
<div class="col-xs-12">
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title">
<?php echo I18N::translate('PHP information'); ?>
</h2>
</div>
<div class="panel-body" dir="ltr">
<div class="php-info">
<?php echo $html; ?>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title">
<?php echo I18N::translate('MySQL variables'); ?>
</h2>
</div>
<div class="panel-body">
<dl>
<?php foreach ($variables as $variable => $value): ?>
<dt><?php echo Filter::escapeHtml($variable); ?></dt>
<dd><?php echo Filter::escapeHtml($value); ?></dd>
<?php endforeach; ?>
</dl>
</div>
</div>
</div>
</div>

View file

@ -1,336 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
use Fisharebest\Webtrees\Controller\PageController;
use Fisharebest\Webtrees\Functions\FunctionsEdit;
use PDO;
/**
* Defined in session.php
*
* @global Tree $WT_TREE
*/
global $WT_TREE;
define('WT_SCRIPT_NAME', 'admin_site_logs.php');
require './includes/session.php';
$controller = new PageController;
$controller
->restrictAccess(Auth::isManager($WT_TREE))
->setPageTitle(I18N::translate('Website logs'));
$earliest = Database::prepare("SELECT IFNULL(DATE(MIN(log_time)), CURDATE()) FROM `##log`")->execute(array())->fetchOne();
$latest = Database::prepare("SELECT IFNULL(DATE(MAX(log_time)), CURDATE()) FROM `##log`")->execute(array())->fetchOne();
// Filtering
$action = Filter::get('action');
$from = Filter::get('from', '\d\d\d\d-\d\d-\d\d', $earliest);
$to = Filter::get('to', '\d\d\d\d-\d\d-\d\d', $latest);
$type = Filter::get('type', 'auth|change|config|debug|edit|error|media|search');
$text = Filter::get('text');
$ip = Filter::get('ip');
$user = Filter::get('user');
$search = Filter::get('search');
$search = isset($search['value']) ? $search['value'] : null;
if (Auth::isAdmin()) {
// Administrators can see all logs
$gedc = Filter::get('gedc');
} else {
// Managers can only see logs relating to this gedcom
$gedc = $WT_TREE->getName();
}
$sql_select =
"SELECT SQL_CACHE SQL_CALC_FOUND_ROWS log_id, log_time, log_type, log_message, ip_address, IFNULL(user_name, '<none>') AS user_name, IFNULL(gedcom_name, '<none>') AS gedcom_name" .
" FROM `##log`" .
" LEFT JOIN `##user` USING (user_id)" . // user may be deleted
" LEFT JOIN `##gedcom` USING (gedcom_id)"; // gedcom may be deleted
$where = " WHERE 1";
$args = array();
if ($search) {
$where .= " AND log_message LIKE CONCAT('%', :search, '%')";
$args['search'] = $search;
}
if ($from) {
$where .= " AND log_time >= :from";
$args['from'] = $from;
}
if ($to) {
$where .= " AND log_time < TIMESTAMPADD(DAY, 1 , :to)"; // before end of the day
$args['to'] = $to;
}
if ($type) {
$where .= " AND log_type = :type";
$args['type'] = $type;
}
if ($text) {
$where .= " AND log_message LIKE CONCAT('%', :text, '%')";
$args['text'] = $text;
}
if ($ip) {
$where .= " AND ip_address LIKE CONCAT('%', :ip, '%')";
$args['ip'] = $ip;
}
if ($user) {
$where .= " AND user_name LIKE CONCAT('%', :user, '%')";
$args['user'] = $user;
}
if ($gedc) {
$where .= " AND gedcom_name LIKE CONCAT('%', :gedc, '%')";
$args['gedc'] = $gedc;
}
switch ($action) {
case 'delete':
$sql_delete =
"DELETE `##log` FROM `##log`" .
" LEFT JOIN `##user` USING (user_id)" . // user may be deleted
" LEFT JOIN `##gedcom` USING (gedcom_id)"; // gedcom may be deleted
Database::prepare($sql_delete . $where)->execute($args);
break;
case 'export':
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="webtrees-logs.csv"');
$rows = Database::prepare($sql_select . $where . ' ORDER BY log_id')->execute($args)->fetchAll();
foreach ($rows as $row) {
echo
'"', $row->log_time, '",',
'"', $row->log_type, '",',
'"', str_replace('"', '""', $row->log_message), '",',
'"', $row->ip_address, '",',
'"', str_replace('"', '""', $row->user_name), '",',
'"', str_replace('"', '""', $row->gedcom_name), '"',
"\n";
}
return;
case 'load_json':
$start = Filter::getInteger('start');
$length = Filter::getInteger('length');
$order = Filter::getArray('order');
if ($order) {
$order_by = " ORDER BY ";
foreach ($order as $key => $value) {
if ($key > 0) {
$order_by .= ',';
}
// Datatables numbers columns 0, 1, 2
// MySQL numbers columns 1, 2, 3
switch ($value['dir']) {
case 'asc':
$order_by .= (1 + $value['column']) . " ASC ";
break;
case 'desc':
$order_by .= (1 + $value['column']) . " DESC ";
break;
}
}
} else {
$order_by = " ORDER BY 1 ASC";
}
if ($length) {
Auth::user()->setPreference('admin_site_log_page_size', $length);
$limit = " LIMIT :limit OFFSET :offset";
$args['limit'] = $length;
$args['offset'] = $start;
} else {
$limit = "";
}
// This becomes a JSON list, not array, so need to fetch with numeric keys.
$data = Database::prepare($sql_select . $where . $order_by . $limit)->execute($args)->fetchAll(PDO::FETCH_NUM);
foreach ($data as &$datum) {
$datum[2] = Filter::escapeHtml($datum[2]);
$datum[3] = '<span dir="auto">' . Filter::escapeHtml($datum[3]) . '</span>';
$datum[4] = '<span dir="auto">' . Filter::escapeHtml($datum[4]) . '</span>';
$datum[5] = '<span dir="auto">' . Filter::escapeHtml($datum[5]) . '</span>';
$datum[6] = '<span dir="auto">' . Filter::escapeHtml($datum[6]) . '</span>';
}
// Total filtered/unfiltered rows
$recordsFiltered = (int) Database::prepare("SELECT FOUND_ROWS()")->fetchOne();
$recordsTotal = (int) Database::prepare("SELECT COUNT(*) FROM `##log`")->fetchOne();
header('Content-type: application/json');
// See http://www.datatables.net/usage/server-side
echo json_encode(array(
'draw' => Filter::getInteger('draw'),
'recordsTotal' => $recordsTotal,
'recordsFiltered' => $recordsFiltered,
'data' => $data,
));
return;
}
$controller
->pageHeader()
->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
->addExternalJavascript(WT_DATATABLES_BOOTSTRAP_JS_URL)
->addExternalJavascript(WT_MOMENT_JS_URL)
->addExternalJavascript(WT_BOOTSTRAP_DATETIMEPICKER_JS_URL)
->addInlineJavascript('
jQuery(".table-site-logs").dataTable( {
processing: true,
serverSide: true,
ajax: "' . WT_BASE_URL . WT_SCRIPT_NAME . '?action=load_json&from=' . $from . '&to=' . $to . '&type=' . $type . '&text=' . rawurlencode($text) . '&ip=' . rawurlencode($ip) . '&user=' . rawurlencode($user) . '&gedc=' . rawurlencode($gedc) . '",
' . I18N::datatablesI18N(array(10, 20, 50, 100, 500, 1000, -1)) . ',
sorting: [[ 0, "desc" ]],
pageLength: ' . Auth::user()->getPreference('admin_site_log_page_size', 10) . ',
columns: [
/* log_id */ { visible: false },
/* Timestamp */ { sort: 0 },
/* Type */ { },
/* message */ { },
/* IP address */ { },
/* User */ { },
/* Family tree */ { }
]
});
jQuery("#from,#to").parent("div").datetimepicker({
format: "YYYY-MM-DD",
minDate: "' . $earliest . '",
maxDate: "' . $latest . '",
locale: "' . WT_LOCALE . '",
icons: {
time: "fa fa-clock-o",
date: "fa fa-calendar",
up: "fa fa-arrow-up",
down: "fa fa-arrow-down",
previous: "fa fa-arrow-' . (I18N::direction() === 'rtl' ? 'right' : 'left') . '",
next: "fa fa-arrow-' . (I18N::direction() === 'rtl' ? 'left' : 'right') . '",
today: "fa fa-trash-o",
clear: "fa fa-trash-o"
}
});
');
$users_array = array();
foreach (User::all() as $tmp_user) {
$users_array[$tmp_user->getUserName()] = $tmp_user->getUserName();
}
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel') ?></a></li>
<li class="active"><?php echo $controller->getPageTitle() ?></li>
</ol>
<h1><?php echo $controller->getPageTitle() ?></h1>
<form class="form" name="logs">
<input type="hidden" name="action" value="show">
<div class="row">
<div class="form-group col-xs-6 col-sm-3">
<label for="from">
<?php echo /* I18N: label for the start of a date range (from x to y) */ I18N::translate('From') ?>
</label>
<div class="input-group date">
<input type="text" autocomplete="off" class="form-control" id="from" name="from" value="<?php echo Filter::escapeHtml($from) ?>" autocomplete="off">
<span class="input-group-addon"><span class="fa fa-calendar"></span></span>
</div>
</div>
<div class="form-group col-xs-6 col-sm-3">
<label for="to">
<?php echo /* I18N: label for the end of a date range (from x to y) */ I18N::translate('To') ?>
</label>
<div class="input-group date">
<input type="text" autocomplete="off" class="form-control" id="to" name="to" value="<?php echo Filter::escapeHtml($to) ?>" autocomplete="off">
<span class="input-group-addon"><span class="fa fa-calendar"></span></span>
</div>
</div>
<div class="form-group col-xs-6 col-sm-2">
<label for="type">
<?php echo I18N::translate('Type') ?>
</label>
<?php echo FunctionsEdit::selectEditControl('type', array('' => '', 'auth' => 'auth', 'config' => 'config', 'debug' => 'debug', 'edit' => 'edit', 'error' => 'error', 'media' => 'media', 'search' => 'search'), null, $type, 'class="form-control"') ?>
</div>
<div class="form-group col-xs-6 col-sm-4">
<label for="ip">
<?php echo I18N::translate('IP address') ?>
</label>
<input class="form-control" type="text" id="ip" name="ip" value="<?php echo Filter::escapeHtml($ip) ?>">
</div>
</div>
<div class="row">
<div class="form-group col-sm-4">
<label for="text">
<?php echo I18N::translate('Message') ?>
</label>
<input class="form-control" type="text" id="text" name="text" value="<?php echo Filter::escapeHtml($text) ?>">
</div>
<div class="form-group col-sm-4">
<label for="user">
<?php echo I18N::translate('User') ?>
</label>
<?php echo FunctionsEdit::selectEditControl('user', $users_array, '', $user, 'class="form-control"') ?>
</div>
<div class="form-group col-sm-4">
<label for="gedc">
<?php echo I18N::translate('Family tree') ?>
</label>
<?php echo FunctionsEdit::selectEditControl('gedc', Tree::getNameList(), '', $gedc, Auth::isAdmin() ? 'class="form-control"' : 'disabled class="form-control"') ?>
</div>
</div>
<div class="row text-center">
<button type="submit" class="btn btn-primary">
<?php echo /* I18N: A button label. */ I18N::translate('search') ?>
</button>
<button type="submit" class="btn btn-primary" onclick="document.logs.action.value='export';return true;" <?php echo $action === 'show' ? '' : 'disabled' ?>>
<?php echo /* I18N: A button label. */ I18N::translate('download') ?>
</button>
<button type="submit" class="btn btn-primary" onclick="if (confirm('<?php echo I18N::translate('Permanently delete these records?') ?>')) {document.logs.action.value='delete'; return true;} else {return false;}" <?php echo $action === 'show' ? '' : 'disabled' ?>>
<?php echo /* I18N: A button label. */ I18N::translate('delete') ?>
</button>
</div>
</form>
<?php if ($action): ?>
<table class="table table-bordered table-condensed table-hover table-site-logs">
<caption class="sr-only">
<?php echo $controller->getPageTitle() ?>
</caption>
<thead>
<tr>
<th></th>
<th><?php echo I18N::translate('Timestamp') ?></th>
<th><?php echo I18N::translate('Type') ?></th>
<th><?php echo I18N::translate('Message') ?></th>
<th><?php echo I18N::translate('IP address') ?></th>
<th><?php echo I18N::translate('User') ?></th>
<th><?php echo I18N::translate('Family tree') ?></th>
</tr>
</thead>
</table>
<?php endif ?>

View file

@ -1,398 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
/**
* Defined in session.php
*
* @global Tree $WT_TREE
*/
global $WT_TREE;
use Fisharebest\Webtrees\Controller\PageController;
use Fisharebest\Webtrees\Functions\FunctionsDb;
use Fisharebest\Webtrees\Functions\FunctionsPrint;
define('WT_SCRIPT_NAME', 'admin_site_merge.php');
require './includes/session.php';
$controller = new PageController;
$controller
->restrictAccess(Auth::isManager($WT_TREE))
->setPageTitle(I18N::translate('Merge records') . ' — ' . $WT_TREE->getTitleHtml())
->addExternalJavascript(WT_AUTOCOMPLETE_JS_URL)
->addInlineJavascript('autocomplete();');
$gid1 = Filter::post('gid1', WT_REGEX_XREF, Filter::get('gid1', WT_REGEX_XREF));
$gid2 = Filter::post('gid2', WT_REGEX_XREF, Filter::get('gid2', WT_REGEX_XREF));
$keep1 = Filter::postArray('keep1');
$keep2 = Filter::postArray('keep2');
$rec1 = GedcomRecord::getInstance($gid1, $WT_TREE);
$rec2 = GedcomRecord::getInstance($gid2, $WT_TREE);
if ($gid1 && !$rec1) {
FlashMessages::addMessage(I18N::translate('%1$s does not exist.', $gid1), 'danger');
}
if ($gid2 && !$rec2) {
FlashMessages::addMessage(I18N::translate('%1$s does not exist.', $gid2), 'danger');
}
if ($rec1 && $rec2 && $rec1->getXref() === $rec2->getXref()) {
FlashMessages::addMessage(I18N::translate('You entered the same IDs. You cannot merge the same records.'), 'danger');
}
if ($rec1 && $rec2 && $rec1::RECORD_TYPE !== $rec2::RECORD_TYPE) {
FlashMessages::addMessage(I18N::translate('Records are not the same type. Cannot merge records that are not the same type.'), 'danger');
}
// Facts found both records
$facts = array();
// Facts found in only one record
$facts1 = array();
$facts2 = array();
if ($rec1) {
foreach ($rec1->getFacts() as $fact) {
if (!$fact->isPendingDeletion() && $fact->getTag() !== 'CHAN') {
$facts1[$fact->getFactId()] = $fact;
}
}
}
if ($rec2) {
foreach ($rec2->getFacts() as $fact) {
if (!$fact->isPendingDeletion() && $fact->getTag() !== 'CHAN') {
$facts2[$fact->getFactId()] = $fact;
}
}
}
foreach ($facts1 as $id1 => $fact1) {
foreach ($facts2 as $id2 => $fact2) {
if ($fact1->getFactId() === $fact2->getFactId()) {
$facts[] = $fact1;
unset($facts1[$id1]);
unset($facts2[$id2]);
}
}
}
if ($rec1 && $rec2 && $rec1->getXref() !== $rec2->getXref() && $rec1::RECORD_TYPE === $rec2::RECORD_TYPE && Filter::post('action') === 'merge' && Filter::checkCsrf()) {
// Use the XREF of the record.
$gid1 = $rec1->getXref();
$gid2 = $rec2->getXref();
$ids = FunctionsDb::fetchAllLinks($gid2, $WT_TREE->getTreeId());
// If we are not auto-accepting, then we can show a link to the pending deletion
if (Auth::user()->getPreference('auto_accept')) {
$record2_name = $rec2->getFullName();
} else {
$record2_name = '<a class="alert-link" href="' . $rec2->getHtmlUrl() . '">' . $rec2->getFullName() . '</a>';
}
foreach ($ids as $id) {
$record = GedcomRecord::getInstance($id, $WT_TREE);
if (!$record->isPendingDeletion()) {
FlashMessages::addMessage(I18N::translate(
/* I18N: The placeholders are the names of individuals, sources, etc. */
'The link from “%1$s” to “%2$s” has been updated.',
'<a class="alert-link" href="' . $record->getHtmlUrl() . '">' . $record->getFullName() . '</a>',
$record2_name
), 'info');
$gedcom = str_replace("@$gid2@", "@$gid1@", $record->getGedcom());
$gedcom = preg_replace(
'/(\n1.*@.+@.*(?:(?:\n[2-9].*)*))((?:\n1.*(?:\n[2-9].*)*)*\1)/',
'$2',
$gedcom
);
$record->updateRecord($gedcom, true);
}
}
// Update any linked user-accounts
Database::prepare(
"UPDATE `##user_gedcom_setting`" .
" SET setting_value=?" .
" WHERE gedcom_id=? AND setting_name='gedcomid' AND setting_value=?"
)->execute(array($gid2, $WT_TREE->getTreeId(), $gid1));
// Merge hit counters
$hits = Database::prepare(
"SELECT page_name, SUM(page_count)" .
" FROM `##hit_counter`" .
" WHERE gedcom_id=? AND page_parameter IN (?, ?)" .
" GROUP BY page_name"
)->execute(array($WT_TREE->getTreeId(), $gid1, $gid2))->fetchAssoc();
foreach ($hits as $page_name => $page_count) {
Database::prepare(
"UPDATE `##hit_counter` SET page_count=?" .
" WHERE gedcom_id=? AND page_name=? AND page_parameter=?"
)->execute(array($page_count, $WT_TREE->getTreeId(), $page_name, $gid1));
}
Database::prepare(
"DELETE FROM `##hit_counter`" .
" WHERE gedcom_id=? AND page_parameter=?"
)->execute(array($WT_TREE->getTreeId(), $gid2));
$gedcom = "0 @" . $rec1->getXref() . "@ " . $rec1::RECORD_TYPE;
foreach ($facts as $fact_id => $fact) {
$gedcom .= "\n" . $fact->getGedcom();
}
foreach ($facts1 as $fact_id => $fact) {
if (in_array($fact_id, $keep1)) {
$gedcom .= "\n" . $fact->getGedcom();
}
}
foreach ($facts2 as $fact_id => $fact) {
if (in_array($fact_id, $keep2)) {
$gedcom .= "\n" . $fact->getGedcom();
}
}
$rec1->updateRecord($gedcom, true);
$rec2->deleteRecord();
FunctionsDb::updateFavorites($gid2, $gid1, $WT_TREE);
FlashMessages::addMessage(I18N::translate(
/* I18N: Records are individuals, sources, etc. */
'The records “%1$s” and “%2$s” have been merged.',
'<a class="alert-link" href="' . $rec1->getHtmlUrl() . '">' . $rec1->getFullName() . '</a>',
$record2_name
), 'success');
header('Location: ' . WT_BASE_URL . Filter::post('url', 'admin_trees_duplicates\.php', WT_SCRIPT_NAME));
return;
}
$controller->pageHeader();
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li><a href="admin_trees_manage.php"><?php echo I18N::translate('Manage family trees'); ?></a></li>
<li class="active"><?php echo $controller->getPageTitle(); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<?php if ($rec1 && $rec2 && $rec1->getXref() !== $rec2->getXref() && $rec1::RECORD_TYPE === $rec2::RECORD_TYPE): ?>
<form method="post">
<input type="hidden" name="action" value="merge">
<input type="hidden" name="ged" value="<?php echo $WT_TREE->getNameHtml(); ?>">
<input type="hidden" name="url" value="<?php echo Filter::get('url', 'admin_trees_duplicates\.php'); ?>">
<?php echo Filter::getCsrf(); ?>
<p>
<?php echo I18N::translate('Select the facts and events to keep from both records.'); ?>
</p>
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title">
<?php echo I18N::translate('The following facts and events were found in both records.'); ?>
</h2>
</div>
<div class="panel-body">
<?php if ($facts): ?>
<table class="table table-bordered table-condensed">
<thead>
<tr>
<th>
<?php echo I18N::translate('Select'); ?>
</th>
<th>
<?php echo I18N::translate('Details'); ?>
</th>
</tr>
</thead>
<tbody>
<?php foreach ($facts as $fact_id => $fact): ?>
<tr>
<td>
<input type="checkbox" name="keep1[]" value="<?php echo $fact->getFactId(); ?>" checked>
</td>
<td>
<div class="gedcom-data" dir="ltr"><?php echo Filter::escapeHtml($fact->getGedcom()); ?></div>
<?php if ($fact->getTarget()): ?>
<a href="<?php echo $fact->getTarget()->getHtmlUrl(); ?>">
<?php echo $fact->getTarget()->getFullName(); ?>
</a>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else: ?>
<p>
<?php echo I18N::translate('No matching facts found'); ?>
</p>
<?php endif; ?>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title">
<?php echo /* I18N: the name of an individual, source, etc. */ I18N::translate('The following facts and events were only found in the record of %s.', '<a href="' . $rec1->getHtmlUrl() . '">' . $rec1->getFullName()) . '</a>'; ?>
</h2>
</div>
<div class="panel-body">
<?php if ($facts1): ?>
<table class="table table-bordered table-condensed">
<thead>
<tr>
<th>
<?php echo I18N::translate('Select'); ?>
</th>
<th>
<?php echo I18N::translate('Details'); ?>
</th>
</tr>
</thead>
<tbody>
<?php foreach ($facts1 as $fact_id => $fact): ?>
<tr>
<td>
<input type="checkbox" name="keep1[]" value="<?php echo $fact->getFactId(); ?>" checked>
</td>
<td>
<div class="gedcom-data" dir="ltr"><?php echo Filter::escapeHtml($fact->getGedcom()); ?></div>
<?php if ($fact->getTarget()): ?>
<a href="<?php echo $fact->getTarget()->getHtmlUrl(); ?>">
<?php echo $fact->getTarget()->getFullName(); ?>
</a>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else: ?>
<p>
<?php echo I18N::translate('No matching facts found'); ?>
</p>
<?php endif; ?>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title">
<?php echo /* I18N: the name of an individual, source, etc. */ I18N::translate('The following facts and events were only found in the record of %s.', '<a href="' . $rec2->getHtmlUrl() . '">' . $rec2->getFullName()) . '</a>'; ?>
</h2>
</div>
<div class="panel-body">
<?php if ($facts2): ?>
<table class="table table-bordered table-condensed">
<thead>
<tr>
<th>
<?php echo I18N::translate('Select'); ?>
</th>
<th>
<?php echo I18N::translate('Details'); ?>
</th>
</tr>
</thead>
<tbody>
<?php foreach ($facts2 as $fact_id => $fact): ?>
<tr>
<td>
<input type="checkbox" name="keep2[]" value="<?php echo $fact->getFactId(); ?>" checked>
</td>
<td>
<div class="gedcom-data" dir="ltr"><?php echo Filter::escapeHtml($fact->getGedcom()); ?></div>
<?php if ($fact->getTarget()): ?>
<a href="<?php echo $fact->getTarget()->getHtmlUrl(); ?>">
<?php echo $fact->getTarget()->getFullName(); ?>
</a>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else: ?>
<p>
<?php echo I18N::translate('No matching facts found'); ?>
</p>
<?php endif; ?>
</div>
</div>
</div>
</div>
<button type="submit" class="btn btn-primary">
<i class="fa fa-check"></i>
<?php echo I18N::translate('save'); ?>
</button>
</form>
<?php else: ?>
<form class="form form-horizontal">
<input type="hidden" name="ged" value="<?php echo $WT_TREE->getNameHtml(); ?>">
<p><?php echo /* I18N: Records are indviduals, sources, etc. */ I18N::translate('Select two records to merge.'); ?></p>
<div class="form-group">
<div class="control-label col-sm-3">
<label for="gid1">
<?php echo /* I18N: Record is an indvidual, source, etc. */ I18N::translate('First record'); ?>
</label>
</div>
<div class="col-sm-9">
<input data-autocomplete-type="IFSRO" type="text" name="gid1" id="gid1" maxlength="20" value="<?php echo $gid1; ?>">
<?php echo FunctionsPrint::printFindIndividualLink('gid1'); ?>
<?php echo FunctionsPrint::printFindFamilyLink('gid1'); ?>
<?php echo FunctionsPrint::printFindSourceLink('gid1'); ?>
<?php echo FunctionsPrint::printFindRepositoryLink('gid1'); ?>
<?php echo FunctionsPrint::printFindMediaLink('gid1'); ?>
<?php echo FunctionsPrint::printFindNoteLink('gid1'); ?>
</div>
</div>
<div class="form-group">
<div class="control-label col-sm-3">
<label for="gid2">
<?php echo /* I18N: Record is an indvidual, source, etc. */ I18N::translate('Second record'); ?>
</label>
</div>
<div class="col-sm-9">
<input data-autocomplete-type="IFSRO" type="text" name="gid2" id="gid2" maxlength="20" value="<?php echo $gid2; ?>" >
<?php echo FunctionsPrint::printFindIndividualLink('gid2'); ?>
<?php echo FunctionsPrint::printFindFamilyLink('gid2'); ?>
<?php echo FunctionsPrint::printFindSourceLink('gid2'); ?>
<?php echo FunctionsPrint::printFindRepositoryLink('gid2'); ?>
<?php echo FunctionsPrint::printFindMediaLink('gid2'); ?>
<?php echo FunctionsPrint::printFindNoteLink('gid2'); ?>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9">
<button type="submit" class="btn btn-primary">
<?php echo I18N::translate('continue'); ?>
</button>
</div>
</div>
</form>
<?php endif; ?>

View file

@ -1,45 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
use Fisharebest\Webtrees\Controller\PageController;
use Michelf\MarkdownExtra;
define('WT_SCRIPT_NAME', 'admin_site_readme.php');
require './includes/session.php';
$controller = new PageController;
$controller
->restrictAccess(Auth::isAdmin())
->setPageTitle(I18N::translate('README documentation'))
->pageHeader();
// The readme file contains code-quality badges before the first header
$readme = file_get_contents('README.md');
$readme = preg_replace('/.*(?=# webtrees)/s', '', $readme);
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li class="active"><?php echo $controller->getPageTitle(); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<div class="markdown" dir="ltr" lang="en">
<?php echo MarkdownExtra::defaultTransform($readme); ?>
</div>

View file

@ -1,460 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
use Exception;
use Fisharebest\Webtrees\Controller\PageController;
use Fisharebest\Webtrees\Functions\Functions;
use Fisharebest\Webtrees\Functions\FunctionsDate;
use PclZip;
define('WT_SCRIPT_NAME', 'admin_site_upgrade.php');
require './includes/session.php';
// Check for updates
$latest_version_txt = Functions::fetchLatestVersion();
if (preg_match('/^[0-9.]+\|[0-9.]+\|/', $latest_version_txt)) {
list($latest_version, $earliest_version, $download_url) = explode('|', $latest_version_txt);
} else {
// Cannot determine the latest version
list($latest_version, $earliest_version, $download_url) = explode('|', '||');
}
$latest_version_html = '<span dir="ltr">' . $latest_version . '</span>';
// Show a friendly message while the site is being upgraded
$lock_file = __DIR__ . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'offline.txt';
$lock_file_text = I18N::translate('This website is being upgraded. Try again in a few minutes.') . PHP_EOL . FunctionsDate::formatTimestamp(WT_TIMESTAMP) . /* I18N: Timezone - http://en.wikipedia.org/wiki/UTC */ I18N::translate('UTC');
// Success/failure indicators
$icon_success = '<i class="icon-yes"></i>';
$icon_failure = '<i class="icon-failure"></i>';
// Need confirmation for various actions
$continue = Filter::post('continue', '1') && Filter::checkCsrf();
$modules_action = Filter::post('modules', 'ignore|disable');
$themes_action = Filter::post('themes', 'ignore|disable');
$controller = new PageController;
$controller
->restrictAccess(Auth::isAdmin())
->setPageTitle(I18N::translate('Upgrade wizard'))
->pageHeader();
echo '<h1>', $controller->getPageTitle(), '</h1>';
if ($latest_version == '') {
echo '<p>', I18N::translate('No upgrade information is available.'), '</p>';
return;
}
if (version_compare(WT_VERSION, $latest_version) >= 0) {
echo '<p>', I18N::translate('This is the latest version of webtrees. No upgrade is available.'), '</p>';
return;
}
echo '<form method="post" action="admin_site_upgrade.php">';
echo Filter::getCsrf();
if ($continue) {
echo '<input type="hidden" name="continue" value="1">';
echo '<p>', I18N::translate('It can take several minutes to download and install the upgrade. Be patient.'), '</p>';
} else {
echo '<p>', I18N::translate('A new version of webtrees is available.'), '</p>';
echo '<p>', I18N::translate('Depending on your server configuration, you may be able to upgrade automatically.'), '</p>';
echo '<p>', I18N::translate('It can take several minutes to download and install the upgrade. Be patient.'), '</p>';
echo '<button type="submit" name="continue" value="1">', /* I18N: %s is a version number, such as 1.2.3 */ I18N::translate('Upgrade to webtrees %s.', $latest_version_html), '</button>';
echo '</form>';
return;
}
echo '<ul>';
////////////////////////////////////////////////////////////////////////////////
// Cannot upgrade until pending changes are accepted/rejected
////////////////////////////////////////////////////////////////////////////////
echo '<li>', /* I18N: The system is about to… */ I18N::translate('Check for pending changes…');
$changes = Database::prepare("SELECT 1 FROM `##change` WHERE status='pending' LIMIT 1")->fetchOne();
if ($changes) {
echo '<br>', I18N::translate('You should accept or reject all pending changes before upgrading.'), $icon_failure;
echo '<br><button onclick="window.open(\'edit_changes.php\',\'_blank\', chan_window_specs); return false;"">', I18N::translate('Pending changes'), '</button>';
echo '</li></ul></form>';
return;
} else {
echo '<br>', I18N::translate('There are no pending changes.'), $icon_success;
}
echo '</li>';
////////////////////////////////////////////////////////////////////////////////
// Custom modules may not work with the new version.
////////////////////////////////////////////////////////////////////////////////
echo '<li>', /* I18N: The system is about to [...] */ I18N::translate('Check for custom modules…');
$custom_modules = false;
foreach (Module::getInstalledModules('disabled') as $module) {
if (!in_array($module->getName(), Module::getCoreModuleNames())) {
switch ($modules_action) {
case 'disable':
Database::prepare(
"UPDATE `##module` SET status = 'disabled' WHERE module_name = ?"
)->execute(array($module->getName()));
break;
case 'ignore':
echo '<br>', I18N::translate('Custom module'), ' — ', WT_MODULES_DIR, $module->getName(), ' — ', $module->getTitle(), $icon_success;
break;
default:
echo '<br>', I18N::translate('Custom module'), ' — ', WT_MODULES_DIR, $module->getName(), ' — ', $module->getTitle(), $icon_failure;
$custom_modules = true;
break;
}
}
}
if ($custom_modules) {
echo '<br>', I18N::translate('You should consult the modules author to confirm compatibility with this version of webtrees.');
echo '<br>', '<button type="submit" name="modules" value="disable">', I18N::translate('Disable these modules'), '</button> — ', I18N::translate('You can re-enable these modules after the upgrade.');
echo '<br>', '<button type="submit" name="modules" value="ignore">', /* I18N: Ignore the warnings, and… */ I18N::translate('Upgrade anyway'), '</button> — ', I18N::translate('Caution: old modules may not work, or they may prevent webtrees from working.');
echo '</li></ul></form>';
return;
} else {
if ($modules_action != 'ignore') {
echo '<br>', I18N::translate('No custom modules are enabled.'), $icon_success;
}
echo '<input type="hidden" name="modules" value="', Filter::escapeHtml($modules_action), '">';
}
echo '</li>';
////////////////////////////////////////////////////////////////////////////////
// Custom themes may not work with the new version.
////////////////////////////////////////////////////////////////////////////////
echo '<li>', /* I18N: The system is about to… */ I18N::translate('Check for custom themes…');
$custom_themes = false;
foreach (Theme::themeNames() as $theme_id => $theme_name) {
switch ($theme_id) {
case 'clouds':
case 'colors':
case 'fab':
case 'minimal':
case 'webtrees':
case 'xenea':
break;
default:
$theme_used = Database::prepare(
"SELECT EXISTS (SELECT 1 FROM `##site_setting` WHERE setting_name='THEME_DIR' AND setting_value=?)" .
" OR EXISTS (SELECT 1 FROM `##gedcom_setting` WHERE setting_name='THEME_DIR' AND setting_value=?)" .
" OR EXISTS (SELECT 1 FROM `##user_setting` WHERE setting_name='theme' AND setting_value=?)"
)->execute(array($theme_id, $theme_id, $theme_id))->fetchOne();
if ($theme_used) {
switch ($themes_action) {
case 'disable':
Database::prepare(
"DELETE FROM `##site_setting` WHERE setting_name = 'THEME_DIR' AND setting_value = ?"
)->execute(array($theme_id));
Database::prepare(
"DELETE FROM `##gedcom_setting` WHERE setting_name = 'THEME_DIR' AND setting_value = ?"
)->execute(array($theme_id));
Database::prepare(
"DELETE FROM `##user_setting` WHERE setting_name = 'theme' AND setting_value = ?"
)->execute(array($theme_id));
break;
case 'ignore':
echo '<br>', I18N::translate('Custom theme'), ' — ', $theme_id, ' — ', $theme_name, $icon_success;
break;
default:
echo '<br>', I18N::translate('Custom theme'), ' — ', $theme_id, ' — ', $theme_name, $icon_failure;
$custom_themes = true;
break;
}
}
break;
}
}
if ($custom_themes) {
echo '<br>', I18N::translate('You should consult the themes author to confirm compatibility with this version of webtrees.');
echo '<br>', '<button type="submit" name="themes" value="disable">', I18N::translate('Disable these themes'), '</button> — ', I18N::translate('You can re-enable these themes after the upgrade.');
echo '<br>', '<button type="submit" name="themes" value="ignore">', I18N::translate('Upgrade anyway'), '</button> — ', I18N::translate('Caution: old themes may not work, or they may prevent webtrees from working.');
echo '</li></ul></form>';
return;
} else {
if ($themes_action != 'ignore') {
echo '<br>', I18N::translate('No custom themes are enabled.'), $icon_success;
}
echo '<input type="hidden" name="themes" value="', Filter::escapeHtml($themes_action), '">';
}
echo '</li>';
////////////////////////////////////////////////////////////////////////////////
// Make a backup of genealogy data
////////////////////////////////////////////////////////////////////////////////
echo '<li>', /* I18N: The system is about to… */ I18N::translate('Export all the family trees to GEDCOM files…');
foreach (Tree::getAll() as $tree) {
reset_timeout();
$filename = WT_DATA_DIR . $tree->getName() . date('-Y-m-d') . '.ged';
try {
// To avoid partial trees on timeout/diskspace/etc, write to a temporary file first
$stream = fopen($filename . '.tmp', 'w');
$tree->exportGedcom($stream);
fclose($stream);
rename($filename . '.tmp', $filename);
echo '<br>', I18N::translate('The family tree has been exported to %s.', Html::filename($filename)), $icon_success;
} catch (\ErrorException $ex) {
echo '<br>', I18N::translate('The file %s could not be created.', Html::filename($filename)), $icon_failure;
}
}
echo '</li>';
////////////////////////////////////////////////////////////////////////////////
// Download a .ZIP file containing the new code
////////////////////////////////////////////////////////////////////////////////
echo '<li>', /* I18N: The system is about to…; %s is a URL. */ I18N::translate('Download %s…', Html::filename($download_url));
$zip_file = WT_DATA_DIR . basename($download_url);
$zip_dir = WT_DATA_DIR . basename($download_url, '.zip');
$zip_stream = fopen($zip_file, 'w');
reset_timeout();
$start_time = microtime(true);
File::fetchUrl($download_url, $zip_stream);
$end_time = microtime(true);
$zip_size = filesize($zip_file);
fclose($zip_stream);
echo '<br>', /* I18N: %1$s is a number of KB, %2$s is a (fractional) number of seconds */ I18N::translate('%1$s KB were downloaded in %2$s seconds.', I18N::number($zip_size / 1024), I18N::number($end_time - $start_time, 2));
if ($zip_size) {
echo $icon_success;
} else {
echo $icon_failure;
// Guess why we might have failed...
if (preg_match('/^https:/', $download_url) && !in_array('ssl', stream_get_transports())) {
echo '<br>', /* I18N: http://en.wikipedia.org/wiki/Https */ I18N::translate('This server does not support secure downloads using HTTPS.');
}
}
echo '</li>';
////////////////////////////////////////////////////////////////////////////////
// Unzip the file - this checks we have enough free disk space, that the .zip
// file is valid, etc.
////////////////////////////////////////////////////////////////////////////////
echo '<li>', /* I18N: The system is about to…; %s is a .ZIP file. */ I18N::translate('Unzip %s to a temporary folder…', Html::filename(basename($download_url)));
File::delete($zip_dir);
File::mkdir($zip_dir);
$archive = new PclZip($zip_file);
$res = $archive->properties();
if (!is_array($res) || $res['status'] != 'ok') {
echo '<br>', I18N::translate('An error occurred when unzipping the file.'), $icon_failure;
echo '<br>', $archive->errorInfo(true);
echo '</li></ul></form>';
return;
}
$num_files = $res['nb'];
reset_timeout();
$start_time = microtime(true);
$res = $archive->extract(
\PCLZIP_OPT_PATH, $zip_dir,
\PCLZIP_OPT_REMOVE_PATH, 'webtrees',
\PCLZIP_OPT_REPLACE_NEWER
);
$end_time = microtime(true);
if (is_array($res)) {
foreach ($res as $result) {
// Note that we're stripping the initial "webtrees/", so the top folder will fail.
if ($result['status'] != 'ok' && $result['filename'] != 'webtrees/') {
echo '<br>', I18N::translate('An error occurred when unzipping the file.'), $icon_failure;
echo '<pre>', $result['status'], '</pre>';
echo '<pre>', $result['filename'], '</pre>';
echo '</li></ul></form>';
return;
}
}
echo '<br>', /* I18N: …from the .ZIP file, %2$s is a (fractional) number of seconds */ I18N::plural('%1$s file was extracted in %2$s seconds.', '%1$s files were extracted in %2$s seconds.', count($res), count($res), I18N::number($end_time - $start_time, 2)), $icon_success;
} else {
echo '<br>', I18N::translate('An error occurred when unzipping the file.'), $icon_failure;
echo '<pre>', $archive->errorInfo(true), '</pre>';
echo '</li></ul></form>';
return;
}
echo '</li>';
////////////////////////////////////////////////////////////////////////////////
// This is it - take the site offline first
////////////////////////////////////////////////////////////////////////////////
echo '<li>', /* I18N: The system is about to… */ I18N::translate('Check file permissions…');
reset_timeout();
$iterator = new \RecursiveDirectoryIterator($zip_dir);
$iterator->setFlags(\RecursiveDirectoryIterator::SKIP_DOTS);
foreach (new \RecursiveIteratorIterator($iterator) as $file) {
$file = WT_ROOT . substr($file, strlen($zip_dir) + 1);
if (file_exists($file) && (!is_readable($file) || !is_writable($file))) {
echo '<br>', I18N::translate('The file %s could not be updated.', Html::filename($file)), $icon_failure;
echo '</li></ul>';
echo '<p class="error">', I18N::translate('To complete the upgrade, you should install the files manually.'), '</p>';
echo '<p>', I18N::translate('The new files are currently located in the folder %s.', Html::filename($zip_dir)), '</p>';
echo '<p>', I18N::translate('Copy these files to the folder %s, replacing any that have the same name.', Html::filename(WT_ROOT)), '</p>';
echo '<p>', I18N::translate('To prevent visitors from accessing the website while you are in the middle of copying files, you can temporarily create a file %s on the server. If it contains a message, it will be displayed to visitors.', Html::filename($lock_file)), '</p>';
return;
}
}
echo '<br>', I18N::translate('All files have read and write permission.'), $icon_success;
echo '</li>';
////////////////////////////////////////////////////////////////////////////////
// This is it - take the site offline first
////////////////////////////////////////////////////////////////////////////////
echo '<li>', I18N::translate('Place the website offline, by creating the file %s…', $lock_file);
try {
file_put_contents($lock_file, $lock_file_text);
echo '<br>', I18N::translate('The file %s has been created.', Html::filename($lock_file)), $icon_success;
} catch (\ErrorException $ex) {
echo '<br>', I18N::translate('The file %s could not be created.', Html::filename($lock_file)), $icon_failure;
}
echo '</li>';
////////////////////////////////////////////////////////////////////////////////
// Copy files
////////////////////////////////////////////////////////////////////////////////
echo '<li>', /* I18N: The system is about to… */ I18N::translate('Copy files…');
// The wiki tells people how to customize webtrees by modifying various files.
// Create a backup of these, just in case the user forgot!
try {
copy('app/GedcomCode/GedcomCode/Rela.php', WT_DATA_DIR . 'GedcomCodeRela' . date('-Y-m-d') . '.php');
copy('app/GedcomTag.php', WT_DATA_DIR . 'GedcomTag' . date('-Y-m-d') . '.php');
} catch (\ErrorException $ex) {
// No problem if we cannot do this.
}
reset_timeout();
$start_time = microtime(true);
$res = $archive->extract(
\PCLZIP_OPT_PATH, WT_ROOT,
\PCLZIP_OPT_REMOVE_PATH, 'webtrees',
\PCLZIP_OPT_REPLACE_NEWER
);
$end_time = microtime(true);
if (is_array($res)) {
foreach ($res as $result) {
// Note that most of the folders will already exist, so it is not an error if we cannot create them
if ($result['status'] != 'ok' && !substr($result['filename'], -1) == '/') {
echo '<br>', I18N::translate('The file %s could not be created.', Html::filename($result['filename'])), $icon_failure;
}
}
echo '<br>', /* I18N: …from the .ZIP file, %2$s is a (fractional) number of seconds */ I18N::plural('%1$s file was extracted in %2$s seconds.', '%1$s files were extracted in %2$s seconds.', count($res), count($res), I18N::number($end_time - $start_time, 2)), $icon_success;
} else {
echo '<br>', I18N::translate('An error occurred when unzipping the file.'), $icon_failure;
echo '</li></ul></form>';
return;
}
echo '</li>';
////////////////////////////////////////////////////////////////////////////////
// All done - put the site back online
////////////////////////////////////////////////////////////////////////////////
echo '<li>', I18N::translate('Place the website online, by deleting the file %s…', Html::filename($lock_file));
if (File::delete($lock_file)) {
echo '<br>', I18N::translate('The file %s has been deleted.', Html::filename($lock_file)), $icon_success;
} else {
echo '<br>', I18N::translate('The file %s could not be deleted.', Html::filename($lock_file)), $icon_failure;
}
echo '</li>';
////////////////////////////////////////////////////////////////////////////////
// Clean up
////////////////////////////////////////////////////////////////////////////////
echo '<li>', /* I18N: The system is about to… */ I18N::translate('Delete temporary files…');
reset_timeout();
if (File::delete($zip_dir)) {
echo '<br>', I18N::translate('The folder %s has been deleted.', Html::filename($zip_dir)), $icon_success;
} else {
echo '<br>', I18N::translate('The folder %s could not be deleted.', Html::filename($zip_dir)), $icon_failure;
}
if (File::delete($zip_file)) {
echo '<br>', I18N::translate('The file %s has been deleted.', Html::filename($zip_file)), $icon_success;
} else {
echo '<br>', I18N::translate('The file %s could not be deleted.', Html::filename($zip_file)), $icon_failure;
}
echo '</li>';
echo '</ul>';
// We have updated the language files.
foreach (glob(WT_DATA_DIR . 'cache/language-*') as $file) {
File::delete($file);
}
echo '<p>', I18N::translate('The upgrade is complete.'), '</p>';
/**
* Reset the time limit, as timeouts in this script could leave the upgrade incomplete.
*/
function reset_timeout() {
if (!ini_get('safe_mode') && strpos(ini_get('disable_functions'), 'set_time_limit') === false) {
try {
set_time_limit(ini_get('max_execution_time'));
} catch (Exception $ex) {
// "set_time_limt(): Cannot set max execution time limit due to system policy"
}
}
}

View file

@ -1,284 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
/**
* Defined in session.php
*
* @global Tree $WT_TREE
*/
global $WT_TREE;
use Fisharebest\Webtrees\Controller\PageController;
define('WT_SCRIPT_NAME', 'admin_trees_check.php');
require './includes/session.php';
$controller = new PageController;
$controller
->restrictAccess(Auth::isManager($WT_TREE))
->setPageTitle(I18N::translate('Check for errors') . ' — ' . $WT_TREE->getTitleHtml())
->pageHeader();
// We need to work with raw GEDCOM data, as we are looking for errors
// which may prevent the GedcomRecord objects from working.
$rows = Database::prepare(
"SELECT i_id AS xref, 'INDI' AS type, i_gedcom AS gedrec FROM `##individuals` WHERE i_file=?" .
" UNION " .
"SELECT f_id AS xref, 'FAM' AS type, f_gedcom AS gedrec FROM `##families` WHERE f_file=?" .
" UNION " .
"SELECT s_id AS xref, 'SOUR' AS type, s_gedcom AS gedrec FROM `##sources` WHERE s_file=?" .
" UNION " .
"SELECT m_id AS xref, 'OBJE' AS type, m_gedcom AS gedrec FROM `##media` WHERE m_file=?" .
" UNION " .
"SELECT o_id AS xref, o_type AS type, o_gedcom AS gedrec FROM `##other` WHERE o_file=? AND o_type NOT IN ('HEAD', 'TRLR')"
)->execute(array($WT_TREE->getTreeId(), $WT_TREE->getTreeId(), $WT_TREE->getTreeId(), $WT_TREE->getTreeId(), $WT_TREE->getTreeId()))->fetchAll();
$records = array();
foreach ($rows as $row) {
$records[$row->xref] = $row;
}
// Need to merge pending new/changed/deleted records
$rows = Database::prepare(
"SELECT xref, SUBSTRING_INDEX(SUBSTRING_INDEX(SUBSTRING_INDEX(CASE WHEN old_gedcom='' THEN new_gedcom ELSE old_gedcom END, '\n', 1), ' ', 3), ' ', -1) AS type, new_gedcom AS gedrec" .
" FROM (" .
" SELECT MAX(change_id) AS change_id" .
" FROM `##change`" .
" WHERE gedcom_id=? AND status='pending'" .
" GROUP BY xref" .
" ) AS t1" .
" JOIN `##change` t2 USING (change_id)"
)->execute(array($WT_TREE->getTreeId()))->fetchAll();
foreach ($rows as $row) {
if ($row->gedrec) {
// new/updated record
$records[$row->xref] = $row;
} else {
// deleted record
unset($records[$row->xref]);
}
}
// Keep a list of upper case XREFs, to detect mismatches.
$ukeys = array();
foreach (array_keys($records) as $key) {
$ukeys[strtoupper($key)] = $key;
}
// LOOK FOR BROKEN LINKS
$XREF_LINKS = array(
'NOTE' => 'NOTE',
'SOUR' => 'SOUR',
'REPO' => 'REPO',
'OBJE' => 'OBJE',
'SUBM' => 'SUBM',
'FAMC' => 'FAM',
'FAMS' => 'FAM',
//'ADOP'=>'FAM', // Need to handle this case specially. We may have both ADOP and FAMC links to the same FAM, but only store one.
'HUSB' => 'INDI',
'WIFE' => 'INDI',
'CHIL' => 'INDI',
'ASSO' => 'INDI',
'_ASSO' => 'INDI', // A webtrees extension
'ALIA' => 'INDI',
'AUTH' => 'INDI', // A webtrees extension
'ANCI' => 'SUBM',
'DESI' => 'SUBM',
'_WT_OBJE_SORT' => 'OBJE',
'_LOC' => '_LOC',
);
$RECORD_LINKS = array(
'INDI' => array('NOTE', 'OBJE', 'SOUR', 'SUBM', 'ASSO', '_ASSO', 'FAMC', 'FAMS', 'ALIA', '_WT_OBJE_SORT', '_LOC'),
'FAM' => array('NOTE', 'OBJE', 'SOUR', 'SUBM', 'ASSO', '_ASSO', 'HUSB', 'WIFE', 'CHIL', '_LOC'),
'SOUR' => array('NOTE', 'OBJE', 'REPO', 'AUTH'),
'REPO' => array('NOTE'),
'OBJE' => array('NOTE'), // The spec also allows SOUR, but we treat this as a warning
'NOTE' => array(), // The spec also allows SOUR, but we treat this as a warning
'SUBM' => array('NOTE', 'OBJE'),
'SUBN' => array('SUBM'),
'_LOC' => array('SOUR', 'OBJE', '_LOC'),
);
$errors = false;
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li><a href="admin_trees_manage.php"><?php echo I18N::translate('Manage family trees'); ?></a></li>
<li class="active"><?php echo $controller->getPageTitle(); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<ul class="list-group">
<li class="list-group-item"><strong><?php echo I18N::translate('Types of error'); ?></strong></li>
<li class="list-group-item list-group-item-danger"><?php echo I18N::translate('This may cause a problem for webtrees.'); ?></li>
<li class="list-group-item list-group-item-warning"><?php echo I18N::translate('This may cause a problem for other applications.'); ?></li>
<li class="list-group-item list-group-item-info"><?php echo I18N::translate('This may be a mistake in your data.'); ?></li>
</ul>
<ul class="list-group">
<li class="list-group-item"><strong><?php echo I18N::translate('GEDCOM errors'); ?></strong></li>
<?php
// Generate lists of all links
$all_links = array();
$upper_links = array();
foreach ($records as $record) {
$all_links[$record->xref] = array();
$upper_links[strtoupper($record->xref)] = $record->xref;
preg_match_all('/\n\d (' . WT_REGEX_TAG . ') @([^#@\n][^\n@]*)@/', $record->gedrec, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$all_links[$record->xref][$match[2]] = $match[1];
}
}
foreach ($all_links as $xref1 => $links) {
$type1 = $records[$xref1]->type;
foreach ($links as $xref2 => $type2) {
$type3 = isset($records[$xref2]) ? $records[$xref2]->type : '';
if (!array_key_exists($xref2, $all_links)) {
if (array_key_exists(strtoupper($xref2), $upper_links)) {
echo warning(
link_message($type1, $xref1, $type2, $xref2) . ' ' .
/* I18N: placeholders are GEDCOM XREFs, such as R123 */
I18N::translate('%1$s does not exist. Did you mean %2$s?', format_link($xref2), format_link($upper_links[strtoupper($xref2)]))
);
} else {
echo error(
link_message(
$type1, $xref1, $type2, $xref2) . ' ' .
/* I18N: placeholders are GEDCOM XREFs, such as R123 */
I18N::translate('%1$s does not exist.', format_link($xref2))
);
}
} elseif ($type2 === 'SOUR' && $type1 === 'NOTE') {
// Notes are intended to add explanations and comments to other records. They should not have their own sources.
} elseif ($type2 === 'SOUR' && $type1 === 'OBJE') {
// Media objects are intended to illustrate other records, facts, and source/citations. They should not have their own sources.
} elseif ($type2 === 'OBJE' && $type1 === 'REPO') {
echo warning(
link_message($type1, $xref1, $type2, $xref2) . ' ' . I18N::translate('This type of link is not allowed here.')
);
} elseif (!array_key_exists($type1, $RECORD_LINKS) || !in_array($type2, $RECORD_LINKS[$type1]) || !array_key_exists($type2, $XREF_LINKS)) {
echo error(
link_message($type1, $xref1, $type2, $xref2) . ' ' .
I18N::translate('This type of link is not allowed here.')
);
} elseif ($XREF_LINKS[$type2] !== $type3) {
// Target XREF does exist - but is invalid
echo error(
link_message($type1, $xref1, $type2, $xref2) . ' ' .
/* I18N: %1$s is an internal ID number such as R123. %2$s and %3$s are record types, such as INDI or SOUR */
I18N::translate('%1$s is a %2$s but a %3$s is expected.', format_link($xref2), format_type($type3), format_type($type2))
);
} elseif (
$type2 === 'FAMC' && (!array_key_exists($xref1, $all_links[$xref2]) || $all_links[$xref2][$xref1] !== 'CHIL') ||
$type2 === 'FAMS' && (!array_key_exists($xref1, $all_links[$xref2]) || $all_links[$xref2][$xref1] !== 'HUSB' && $all_links[$xref2][$xref1] !== 'WIFE') ||
$type2 === 'CHIL' && (!array_key_exists($xref1, $all_links[$xref2]) || $all_links[$xref2][$xref1] !== 'FAMC') ||
$type2 === 'HUSB' && (!array_key_exists($xref1, $all_links[$xref2]) || $all_links[$xref2][$xref1] !== 'FAMS') ||
$type2 === 'WIFE' && (!array_key_exists($xref1, $all_links[$xref2]) || $all_links[$xref2][$xref1] !== 'FAMS')
) {
echo error(
link_message($type1, $xref1, $type2, $xref2) . ' ' .
/* I18N: %1$s and %2$s are internal ID numbers such as R123 */
I18N::translate('%1$s does not have a link back to %2$s.', format_link($xref2), format_link($xref1))
);
}
}
}
if (!$errors) {
echo '<li class="list-group-item">', I18N::translate('No errors have been found.'), '</li>';
}
echo '</ul>';
/**
* Create a message linking one record to another.
*
* @param string $type1
* @param string $xref1
* @param string $type2
* @param string $xref2
*
* @return string
*/
function link_message($type1, $xref1, $type2, $xref2) {
return /* I18N: The placeholders are GEDCOM XREFs and tags. e.g. “INDI I123 contains a FAMC link to F234.” */ I18N::translate(
'%1$s %2$s has a %3$s link to %4$s.',
format_type($type1),
format_link($xref1),
format_type($type2),
format_link($xref2)
);
}
/**
* Format a link to a record.
*
* @param string $xref
*
* @return string
*/
function format_link($xref) {
return '<b><a href="gedrecord.php?pid=' . $xref . '">' . $xref . '</a></b>';
}
/**
* Format a record type.
*
* @param string $type
*
* @return string
*/
function format_type($type) {
return '<b title="' . strip_tags(GedcomTag::getLabel($type)) . '">' . $type . '</b>';
}
/**
* Format an error message.
*
* @param string $message
*
* @return string
*/
function error($message) {
global $errors;
$errors = true;
return '<li class="list-group-item list-group-item-danger">' . $message . '</li>';
}
/**
* Format a warning message.
*
* @param string $message
*
* @return string
*/
function warning($message) {
global $errors;
$errors = true;
return '<li class="list-group-item list-group-item-warning">' . $message . '</li>';
}

File diff suppressed because it is too large Load diff

View file

@ -1,210 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
/**
* Defined in session.php
*
* @global Tree $WT_TREE
*/
global $WT_TREE;
use Fisharebest\Webtrees\Controller\PageController;
use Fisharebest\Webtrees\Functions\FunctionsExport;
use PclZip;
define('WT_SCRIPT_NAME', 'admin_trees_download.php');
require './includes/session.php';
$controller = new PageController;
$controller
->setPageTitle(I18N::translate($WT_TREE->getTitleHtml()) . ' — ' . I18N::translate('Export a GEDCOM file'))
->restrictAccess(Auth::isManager($WT_TREE));
// Validate user parameters
$action = Filter::get('action', 'download');
$convert = Filter::get('convert', 'yes|no', 'no');
$zip = Filter::get('zip', 'yes|no', 'no');
$conv_path = Filter::get('conv_path');
$privatize_export = Filter::get('privatize_export', 'none|visitor|user|gedadmin');
if ($action === 'download') {
$exportOptions = array(
'privatize' => $privatize_export,
'toANSI' => $convert,
'path' => $conv_path,
);
// What to call the downloaded file
$download_filename = $WT_TREE->getName();
if (strtolower(substr($download_filename, -4, 4)) != '.ged') {
$download_filename .= '.ged';
}
if ($zip === 'yes') {
$temp_dir = WT_DATA_DIR . 'tmp-' . $WT_TREE->getName() . '-' . date('YmdHis') . '/';
$zip_file = $download_filename . '.zip';
if (!File::mkdir($temp_dir)) {
echo "Error : Could not create temporary path!";
return;
}
// Create the unzipped GEDCOM on disk, so we can ZIP it.
$stream = fopen($temp_dir . $download_filename, "w");
FunctionsExport::exportGedcom($WT_TREE, $stream, $exportOptions);
fclose($stream);
// Create a ZIP file containing the GEDCOM file.
$comment = "Created by " . WT_WEBTREES . " " . WT_VERSION . " on " . date("r") . ".";
$archive = new PclZip($temp_dir . $zip_file);
$v_list = $archive->add($temp_dir . $download_filename, \PCLZIP_OPT_COMMENT, $comment, \PCLZIP_OPT_REMOVE_PATH, $temp_dir);
if ($v_list == 0) {
echo "Error : " . $archive->errorInfo(true);
} else {
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="' . $zip_file . '"');
header('Content-length: ' . filesize($temp_dir . $zip_file));
readfile($temp_dir . $zip_file);
File::delete($temp_dir);
}
} else {
header('Content-Type: text/plain; charset=UTF-8');
header('Content-Disposition: attachment; filename="' . $download_filename . '"');
// Stream the GEDCOM file straight to the browser.
// We could open "php://compress.zlib" to create a .gz file or "php://compress.bzip2" to create a .bz2 file
$stream = fopen('php://output', 'w');
FunctionsExport::exportGedcom($WT_TREE, $stream, $exportOptions);
fclose($stream);
}
return;
}
$controller->pageHeader();
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li><a href="admin_trees_manage.php"><?php echo I18N::translate('Manage family trees'); ?></a></li>
<li class="active"><?php echo $controller->getPageTitle(); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<form class="form form-horizontal" method="post" action="admin_trees_export.php">
<?php echo Filter::getCsrf(); ?>
<input type="hidden" name="ged" value="<?php echo $WT_TREE->getNameHtml(); ?>">
<div class="form-group">
<label for="submit-export" class="col-sm-3 control-label">
<?php echo I18N::translate('A file on the server'); ?>
</label>
<div class="col-sm-9">
<button id="submit-export" type="submit" class="btn btn-primary">
<?php echo /* I18N: A button label. */ I18N::translate('continue'); ?>
</button>
</div>
</div>
</form>
<hr>
<form class="form form-horizontal">
<input type="hidden" name="action" value="download">
<input type="hidden" name="ged" value="<?php echo $WT_TREE->getNameHtml(); ?>">
<!-- DOWNLOAD OPTIONS -->
<fieldset class="form-group">
<legend class="control-label col-sm-3">
<?php echo I18N::translate('Export preferences'); ?>
</legend>
<!-- ZIP FILES -->
<div class="col-sm-9">
<label>
<input type="checkbox" name="zip" value="yes">
<?php echo I18N::translate('Compress the GEDCOM file'); ?>
</label>
<p class="small muted">
<?php echo I18N::translate('To reduce the size of the download, you can compress the data into a .ZIP file. You will need to uncompress the .ZIP file before you can use it.'); ?>
</p>
<!-- CONVERT TO ISO8859-1 -->
<label>
<input type="checkbox" name="convert" value="yes">
<?php echo I18N::translate('Convert from UTF-8 to ISO-8859-1'); ?>
</label>
<p class="small muted">
<?php echo I18N::translate('webtrees uses UTF-8 encoding for accented letters, special characters and non-Latin scripts. If you want to use this GEDCOM file with genealogy software that does not support UTF-8, then you can create it using ISO-8859-1 encoding.'); ?>
</p>
<!-- GEDCOM_MEDIA_PATH -->
<?php if ($WT_TREE->getPreference('GEDCOM_MEDIA_PATH')): ?>
<label>
<input type="checkbox" name="conv_path" value="<?php echo Filter::escapeHtml($WT_TREE->getPreference('GEDCOM_MEDIA_PATH')); ?>">
<?php echo /* I18N: A media path (e.g. C:\aaa\bbb\ccc\) in a GEDCOM file */ I18N::translate('Add the GEDCOM media path to filenames'); ?>
</label>
<p>
<?php echo /* I18N: %s is the name of a folder. */ I18N::translate('Media filenames will be prefixed by %s.', '<code dir="ltr">' . Filter::escapeHtml($WT_TREE->getPreference('GEDCOM_MEDIA_PATH')) . '</code>'); ?>
</p>
<?php endif; ?>
</div>
</fieldset>
<!-- PRIVACY OPTIONS -->
<fieldset class="form-group">
<legend class="control-label col-sm-3">
<?php echo I18N::translate('Apply privacy settings'); ?>
</legend>
<div class="col-sm-9">
<label>
<input type="radio" name="privatize_export" value="none" checked>
<?php echo I18N::translate('None'); ?>
</label>
<br>
<label>
<input type="radio" name="privatize_export" value="gedadmin">
<?php echo I18N::translate('Manager'); ?>
</label>
<br>
<label>
<input type="radio" name="privatize_export" value="user">
<?php echo I18N::translate('Member'); ?>
</label>
<br>
<label>
<input type="radio" name="privatize_export" value="visitor">
<?php echo I18N::translate('Visitor'); ?>
</label>
</div>
</fieldset>
<div class="form-group">
<label for="submit-export" class="col-sm-3 control-label">
<?php echo I18N::translate('A file on your computer'); ?>
</label>
<div class="col-sm-9">
<button id="submit-export" type="submit" class="btn btn-primary">
<?php echo /* I18N: A button label. */ I18N::translate('continue'); ?>
</button>
</div>
</div>
</form>
</form>

View file

@ -1,184 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
/**
* Defined in session.php
*
* @global Tree $WT_TREE
*/
global $WT_TREE;
use Fisharebest\Webtrees\Controller\PageController;
define('WT_SCRIPT_NAME', 'admin_trees_duplicates.php');
require './includes/session.php';
$controller = new PageController;
$controller
->restrictAccess(Auth::isManager($WT_TREE))
->setPageTitle(I18N::translate('Find duplicates') . ' — ' . $WT_TREE->getTitleHtml())
->pageHeader();
$repositories = Database::prepare(
"SELECT GROUP_CONCAT(n_id) AS xrefs " .
" FROM `##other`" .
" JOIN `##name` ON o_id = n_id AND o_file = n_file" .
" WHERE o_file = :tree_id AND o_type = 'REPO'" .
" GROUP BY n_full" .
" HAVING COUNT(n_id) > 1"
)->execute(array(
'tree_id' => $WT_TREE->getTreeId(),
))->fetchAll();
$repositories = array_map(
function (\stdClass $x) use ($WT_TREE) {
$tmp = explode(',', $x->xrefs);
return array_map(function ($y) use ($WT_TREE) {
return Repository::getInstance($y, $WT_TREE);
}, $tmp);
}, $repositories
);
$sources = Database::prepare(
"SELECT GROUP_CONCAT(n_id) AS xrefs " .
" FROM `##sources`" .
" JOIN `##name` ON s_id = n_id AND s_file = n_file" .
" WHERE s_file = :tree_id" .
" GROUP BY n_full" .
" HAVING COUNT(n_id) > 1"
)->execute(array(
'tree_id' => $WT_TREE->getTreeId(),
))->fetchAll();
$sources = array_map(
function (\stdClass $x) use ($WT_TREE) {
$tmp = explode(',', $x->xrefs);
return array_map(function ($y) use ($WT_TREE) {
return Source::getInstance($y, $WT_TREE);
}, $tmp);
}, $sources
);
$individuals = Database::prepare(
"SELECT DISTINCT GROUP_CONCAT(d_gid ORDER BY d_gid) AS xrefs" .
" FROM `##dates` AS d" .
" JOIN `##name` ON d_file = n_file AND d_gid = n_id" .
" WHERE d_file = :tree_id AND d_fact IN ('BIRT', 'CHR', 'BAPM', 'DEAT', 'BURI')" .
" GROUP BY d_day, d_month, d_year, d_type, d_fact, n_type, n_full" .
" HAVING COUNT(DISTINCT d_gid) > 1"
)->execute(array(
'tree_id' => $WT_TREE->getTreeId(),
))->fetchAll();
$individuals = array_map(
function (\stdClass $x) use ($WT_TREE) {
$tmp = explode(',', $x->xrefs);
return array_map(function ($y) use ($WT_TREE) {
return Individual::getInstance($y, $WT_TREE);
}, $tmp);
}, $individuals
);
$families = Database::prepare(
"SELECT GROUP_CONCAT(f_id) AS xrefs " .
" FROM `##families`" .
" WHERE f_file = :tree_id" .
" GROUP BY LEAST(f_husb, f_wife), GREATEST(f_husb, f_wife)" .
" HAVING COUNT(f_id) > 1"
)->execute(array(
'tree_id' => $WT_TREE->getTreeId(),
))->fetchAll();
$families = array_map(
function (\stdClass $x) use ($WT_TREE) {
$tmp = explode(',', $x->xrefs);
return array_map(function ($y) use ($WT_TREE) {
return Family::getInstance($y, $WT_TREE);
}, $tmp);
}, $families
);
$media = Database::prepare(
"SELECT GROUP_CONCAT(m_id) AS xrefs " .
" FROM `##media`" .
" WHERE m_file = :tree_id" .
" GROUP BY m_titl" .
" HAVING COUNT(m_id) > 1"
)->execute(array(
'tree_id' => $WT_TREE->getTreeId(),
))->fetchAll();
$media = array_map(
function (\stdClass $x) use ($WT_TREE) {
$tmp = explode(',', $x->xrefs);
return array_map(function ($y) use ($WT_TREE) {
return Media::getInstance($y, $WT_TREE);
}, $tmp);
}, $media
);
$all_duplicates = array(
I18N::translate('Repositories') => $repositories,
I18N::translate('Sources') => $sources,
I18N::translate('Individuals') => $individuals,
I18N::translate('Families') => $families,
I18N::translate('Media objects') => $media,
);
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li><a href="admin_trees_manage.php"><?php echo I18N::translate('Manage family trees'); ?></a></li>
<li class="active"><?php echo $controller->getPageTitle(); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<?php foreach ($all_duplicates as $title => $duplicate_list): ?>
<h2><?php echo $title; ?></h2>
<?php if ($duplicate_list): ?>
<ul>
<?php foreach ($duplicate_list as $duplicates): ?>
<li>
<?php echo $duplicates[0]->getFullName(); ?>
<?php foreach ($duplicates as $record): ?>
<a href="<?php echo $record->getHtmlUrl(); ?>">
<?php echo $record->getXref(); ?>
</a>
<?php endforeach; ?>
<?php if (count($duplicates) === 2): ?>
<a href="admin_site_merge.php?ged=<?php echo $WT_TREE->getNameHtml(); ?>&amp;gid1=<?php echo $duplicates[0]->getXref(); ?>&amp;gid2=<?php echo $duplicates[1]->getXref(); ?>&amp;url=admin_trees_duplicates.php">
<?php echo I18N::translate('Merge'); ?>
</a>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
<?php else: ?>
<p><?php echo I18N::translate('No duplicates have been found.'); ?></p>
<?php endif; ?>
<?php endforeach; ?>

View file

@ -1,50 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
/**
* Defined in session.php
*
* @global Tree $WT_TREE
*/
global $WT_TREE;
define('WT_SCRIPT_NAME', 'admin_trees_export.php');
require './includes/session.php';
if (Auth::isManager($WT_TREE) && Filter::checkCsrf()) {
$filename = WT_DATA_DIR . $WT_TREE->getName();
// Force a ".ged" suffix
if (strtolower(substr($filename, -4)) != '.ged') {
$filename .= '.ged';
}
try {
// To avoid partial trees on timeout/diskspace/etc, write to a temporary file first
$stream = fopen($filename . '.tmp', 'w');
$WT_TREE->exportGedcom($stream);
fclose($stream);
rename($filename . '.tmp', $filename);
FlashMessages::addMessage(/* I18N: %s is a filename */ I18N::translate('The family tree has been exported to %s.', Html::filename($filename)), 'success');
} catch (\ErrorException $ex) {
FlashMessages::addMessage(
I18N::translate('The file %s could not be created.', Html::filename($filename)) . '<hr><samp dir="ltr">' . $ex->getMessage() . '</samp>',
'danger'
);
}
}
header('Location: ' . WT_BASE_URL . 'admin_trees_manage.php');

View file

@ -1,774 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
/**
* Defined in session.php
*
* @global Tree $WT_TREE
*/
global $WT_TREE;
use Fisharebest\Webtrees\Controller\PageController;
use Fisharebest\Webtrees\Functions\Functions;
define('WT_SCRIPT_NAME', 'admin_trees_manage.php');
require './includes/session.php';
$controller = new PageController;
$controller
->addExternalJavascript(WT_ADMIN_JS_URL)
->restrictAccess(Auth::isAdmin() || Auth::isManager($WT_TREE))
->setPageTitle(I18N::translate('Manage family trees'));
// Show a reduced page when there are more than a certain number of trees
$multiple_tree_threshold = Site::getPreference('MULTIPLE_TREE_THRESHOLD') ?: 500;
// Note that glob() returns false instead of an empty array when open_basedir_restriction
// is in force and no files are found. See PHP bug #47358.
if (defined('GLOB_BRACE')) {
$gedcom_files = glob(WT_DATA_DIR . '*.{ged,Ged,GED}', GLOB_NOSORT | GLOB_BRACE) ?: array();
} else {
$gedcom_files = array_merge(
glob(WT_DATA_DIR . '*.ged', GLOB_NOSORT) ?: array(),
glob(WT_DATA_DIR . '*.Ged', GLOB_NOSORT) ?: array(),
glob(WT_DATA_DIR . '*.GED', GLOB_NOSORT) ?: array()
);
}
// Process POST actions
switch (Filter::post('action')) {
case 'delete':
$gedcom_id = Filter::postInteger('gedcom_id');
if (Filter::checkCsrf() && $gedcom_id) {
$tree = Tree::findById($gedcom_id);
FlashMessages::addMessage(/* I18N: %s is the name of a family tree */ I18N::translate('The family tree “%s” has been deleted.', $tree->getTitleHtml()), 'success');
$tree->delete();
}
header('Location: ' . WT_BASE_URL . WT_SCRIPT_NAME);
return;
case 'setdefault':
if (Filter::checkCsrf()) {
Site::setPreference('DEFAULT_GEDCOM', Filter::post('ged'));
FlashMessages::addMessage(/* I18N: %s is the name of a family tree */ I18N::translate('The family tree “%s” will be shown to visitors when they first arrive at this website.', $WT_TREE->getTitleHtml()), 'success');
}
header('Location: ' . WT_BASE_URL . WT_SCRIPT_NAME);
return;
case 'new_tree':
$basename = basename(Filter::post('tree_name'));
$tree_title = Filter::post('tree_title');
if (Filter::checkCsrf() && $basename && $tree_title) {
if (Tree::findByName($basename)) {
FlashMessages::addMessage(/* I18N: %s is the name of a family tree */ I18N::translate('The family tree “%s” already exists.', Filter::escapeHtml($basename)), 'danger');
} else {
Tree::create($basename, $tree_title);
FlashMessages::addMessage(/* I18N: %s is the name of a family tree */ I18N::translate('The family tree “%s” has been created.', Filter::escapeHtml($basename)), 'success');
}
}
header('Location: ' . WT_BASE_URL . WT_SCRIPT_NAME . '?ged=' . Filter::escapeUrl($basename));
return;
case 'replace_upload':
$gedcom_id = Filter::postInteger('gedcom_id');
$keep_media = Filter::post('keep_media', '1', '0');
$GEDCOM_MEDIA_PATH = Filter::post('GEDCOM_MEDIA_PATH');
$WORD_WRAPPED_NOTES = Filter::post('WORD_WRAPPED_NOTES', '1', '0');
$tree = Tree::findById($gedcom_id);
if (Filter::checkCsrf() && $tree) {
$tree->setPreference('keep_media', $keep_media);
$tree->setPreference('GEDCOM_MEDIA_PATH', $GEDCOM_MEDIA_PATH);
$tree->setPreference('WORD_WRAPPED_NOTES', $WORD_WRAPPED_NOTES);
if (isset($_FILES['tree_name'])) {
if ($_FILES['tree_name']['error'] == 0 && is_readable($_FILES['tree_name']['tmp_name'])) {
$tree->importGedcomFile($_FILES['tree_name']['tmp_name'], $_FILES['tree_name']['name']);
} else {
FlashMessages::addMessage(Functions::fileUploadErrorText($_FILES['tree_name']['error']), 'danger');
}
} else {
FlashMessages::addMessage(I18N::translate('No GEDCOM file was received.'), 'danger');
}
}
header('Location: ' . WT_BASE_URL . WT_SCRIPT_NAME);
return;
case 'replace_import':
$basename = basename(Filter::post('tree_name'));
$gedcom_id = Filter::postInteger('gedcom_id');
$keep_media = Filter::post('keep_media', '1', '0');
$GEDCOM_MEDIA_PATH = Filter::post('GEDCOM_MEDIA_PATH');
$WORD_WRAPPED_NOTES = Filter::post('WORD_WRAPPED_NOTES', '1', '0');
$tree = Tree::findById($gedcom_id);
if (Filter::checkCsrf() && $tree) {
$tree->setPreference('keep_media', $keep_media);
$tree->setPreference('GEDCOM_MEDIA_PATH', $GEDCOM_MEDIA_PATH);
$tree->setPreference('WORD_WRAPPED_NOTES', $WORD_WRAPPED_NOTES);
if ($basename) {
$tree->importGedcomFile(WT_DATA_DIR . $basename, $basename);
} else {
FlashMessages::addMessage(I18N::translate('No GEDCOM file was received.'), 'danger');
}
}
header('Location: ' . WT_BASE_URL . WT_SCRIPT_NAME);
return;
case 'synchronize':
if (Filter::checkCsrf()) {
$basenames = array();
foreach ($gedcom_files as $gedcom_file) {
$filemtime = filemtime($gedcom_file); // Only import files that have changed
$basename = basename($gedcom_file);
$basenames[] = $basename;
$tree = Tree::findByName($basename);
if (!$tree) {
$tree = Tree::create($basename, $basename);
}
if ($tree->getPreference('filemtime') != $filemtime) {
$tree->importGedcomFile($gedcom_file, $basename);
$tree->setPreference('filemtime', $filemtime);
FlashMessages::addMessage(I18N::translate('The GEDCOM file “%s” has been imported.', Filter::escapeHtml($basename)), 'success');
}
}
foreach (Tree::getAll() as $tree) {
if (!in_array($tree->getName(), $basenames)) {
FlashMessages::addMessage(I18N::translate('The family tree “%s” has been deleted.', $tree->getTitleHtml()), 'success');
$tree->delete();
}
}
}
header('Location: ' . WT_BASE_URL . WT_SCRIPT_NAME);
return;
}
$default_tree_title = /* I18N: Default name for a new tree */ I18N::translate('My family tree');
$default_tree_name = 'tree';
$default_tree_number = 1;
$existing_trees = Tree::getNameList();
while (array_key_exists($default_tree_name . $default_tree_number, $existing_trees)) {
$default_tree_number++;
}
$default_tree_name .= $default_tree_number;
// Process GET actions
switch (Filter::get('action')) {
case 'importform':
$controller
->setPageTitle($WT_TREE->getTitleHtml() . ' — ' . I18N::translate('Import a GEDCOM file'))
->pageHeader();
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li><a href="admin_trees_manage.php"><?php echo I18N::translate('Manage family trees'); ?></a></li>
<li class="active"><?php echo $controller->getPageTitle(); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<?php
$tree = Tree::findById(Filter::getInteger('gedcom_id'));
// Check it exists
if (!$tree) {
break;
}
$gedcom_filename = $tree->getPreference('gedcom_filename')
?>
<p>
<?php echo /* I18N: %s is the name of a family tree */ I18N::translate('This will delete all the genealogy data from “%s” and replace it with data from a GEDCOM file.', $tree->getTitleHtml()); ?>
</p>
<form class="form form-horizontal" name="gedcomimportform" method="post" enctype="multipart/form-data" onsubmit="return checkGedcomImportForm('<?php echo Filter::escapeHtml(I18N::translate('You have selected a GEDCOM file with a different name. Is this correct?')); ?>');">
<input type="hidden" name="gedcom_id" value="<?php echo $tree->getTreeId(); ?>">
<input type="hidden" id="gedcom_filename" value="<?php echo Filter::escapeHtml($gedcom_filename); ?>">
<?php echo Filter::getCsrf(); ?>
<fieldset class="form-group">
<legend class="control-label col-sm-3">
<?php echo /* I18N: A configuration setting */ I18N::translate('Select a GEDCOM file to import'); ?>
</legend>
<div class="col-sm-9">
<div class="row">
<label class="col-sm-3">
<input type="radio" name="action" id="import-computer" value="replace_upload" checked>
<?php echo I18N::translate('A file on your computer'); ?>
</label>
<div class="col-sm-9">
<div class="btn btn-default">
<input type="file" name="tree_name" id="import-computer-file">
</div>
</div>
</div>
<div class="row">
<label class="col-sm-3">
<input type="radio" name="action" id="import-server" value="replace_import">
<?php echo I18N::translate('A file on the server'); ?>
</label>
<div class="col-sm-9">
<div class="input-group">
<span class="input-group-addon">
<?php echo WT_DATA_DIR; ?>
</span>
<?php
$d = opendir(WT_DATA_DIR);
$files = array();
while (($f = readdir($d)) !== false) {
if (!is_dir(WT_DATA_DIR . $f) && is_readable(WT_DATA_DIR . $f)) {
$fp = fopen(WT_DATA_DIR . $f, 'rb');
$header = fread($fp, 64);
fclose($fp);
if (preg_match('/^(' . WT_UTF8_BOM . ')?0 *HEAD/', $header)) {
$files[] = $f;
}
}
}
echo '<select name="tree_name" class="form-control" id="import-server-file">';
echo '<option value=""></option>';
sort($files);
foreach ($files as $gedcom_file) {
echo '<option value="', Filter::escapeHtml($gedcom_file), '" ';
if ($gedcom_file === $gedcom_filename) {
echo ' selected';
}
echo'>', Filter::escapeHtml($gedcom_file), '</option>';
}
if (!$files) {
echo '<option disabled selected>', I18N::translate('No GEDCOM files found.'), '</option>';
}
echo '</select>';
?>
</div>
</div>
</div>
</div>
</fieldset>
<hr>
<fieldset class="form-group">
<legend class="control-label col-sm-3">
<?php echo I18N::translate('Import preferences'); ?>
</legend>
<div class="col-sm-9">
<label>
<input type="checkbox" name="keep_media" value="1" <?php echo $tree->getPreference('keep_media') ? 'checked' : ''; ?>>
<?php echo /* I18N: A configuration setting */ I18N::translate('Keep media objects'); ?>
</label>
<p class="small text-muted">
<?php echo I18N::translate('If you have created media objects in webtrees, and have subsequently edited this GEDCOM file using genealogy software that deletes media objects, then select this option to merge the current media objects with the new GEDCOM file.'); ?>
</p>
<label>
<input type="checkbox" name="WORD_WRAPPED_NOTES" value="1" <?php echo $tree->getPreference('WORD_WRAPPED_NOTES') ? 'checked' : ''; ?>>
<?php echo I18N::translate('Add spaces where long lines were wrapped'); ?>
</label>
<p class="small text-muted">
<?php echo I18N::translate('If you created this GEDCOM file using genealogy software that omits spaces when splitting long lines, then select this option to reinsert the missing spaces.'); ?>
</p>
<label for="GEDCOM_MEDIA_PATH">
<?php echo /* I18N: A media path (e.g. c:\aaa\bbb\ccc\ddd.jpeg) in a GEDCOM file */ I18N::translate('Remove the GEDCOM media path from filenames'); ?>
</label>
<input
class="form-control"
dir="ltr"
id="GEDCOM_MEDIA_PATH"
maxlength="255"
name="GEDCOM_MEDIA_PATH"
type="text"
value="<?php echo Filter::escapeHtml($WT_TREE->getPreference('GEDCOM_MEDIA_PATH')); ?>"
>
<p class="small text-muted">
<?php echo /* I18N: Help text for the “GEDCOM media path” configuration setting. A “path” is something like “C:\Documents\Genealogy\Photos\John_Smith.jpeg” */ I18N::translate('Some genealogy software creates GEDCOM files that contain media filenames with full paths. These paths will not exist on the web-server. To allow webtrees to find the file, the first part of the path must be removed.'); ?>
<?php echo /* I18N: Help text for the “GEDCOM media path” configuration setting. %s are all folder names */ I18N::translate('For example, if the GEDCOM file contains %1$s and webtrees expects to find %2$s in the media folder, then you would need to remove %3$s.', '<code>C:\\Documents\\family\\photo.jpeg</code>', '<code>family\\photo.jpeg</code>', '<code>C:\\Documents\\</code>'); ?>
</p>
</div>
</fieldset>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9">
<button type="submit" class="btn btn-primary">
<?php echo /* I18N: A button label. */ I18N::translate('continue'); ?>
</button>
</div>
</div>
</form>
<?php
return;
}
if (!Tree::getAll()) {
FlashMessages::addMessage(I18N::translate('You need to create a family tree.'), 'info');
}
$controller->pageHeader();
$all_trees = Tree::getAll();
// On sites with hundreds or thousands of trees, this page becomes very large.
// Just show the current tree, the default tree, and unimported trees
if (count($all_trees) >= $multiple_tree_threshold) {
$all_trees = array_filter($all_trees, function (Tree $x) use ($WT_TREE) {
return $x->getPreference('imported') === '0' || $WT_TREE->getTreeId() === $x->getTreeId() || $x->getName() === Site::getPreference('DEFAULT_GEDCOM');
});
}
// List the gedcoms available to this user
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li class="active"><?php echo I18N::translate('Manage family trees'); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<div class="panel-group" id="accordion" role="tablist">
<?php foreach ($all_trees as $tree): ?>
<?php if (Auth::isManager($tree)): ?>
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="panel-tree-<?php echo $tree->getTreeId(); ?>">
<h2 class="panel-title">
<i class="fa fa-fw fa-tree"></i>
<a data-toggle="collapse" data-parent="#accordion" href="#tree-<?php echo $tree->getTreeId(); ?>" aria-expanded="true" aria-controls="tree-<?php echo $tree->getTreeId(); ?>">
<?php echo $tree->getNameHtml(); ?> — <?php echo $tree->getTitleHtml(); ?>
</a>
</h2>
</div>
<div id="tree-<?php echo $tree->getTreeId(); ?>" class="panel-collapse collapse<?php echo $tree == $WT_TREE || $tree->getPreference('imported') === '0' ? ' in' : ''; ?>" role="tabpanel" aria-labelledby="panel-tree-<?php echo $tree->getTreeId(); ?>">
<div class="panel-body">
<?php
// The third row shows an optional progress bar and a list of maintenance options
$importing = Database::prepare(
"SELECT 1 FROM `##gedcom_chunk` WHERE gedcom_id = ? AND imported = '0' LIMIT 1"
)->execute(array($tree->getTreeId()))->fetchOne();
if ($importing) {
?>
<div id="import<?php echo $tree->getTreeId(); ?>" class="col-xs-12">
<div class="progress">
<?php echo I18N::translate('Calculating…'); ?>
</div>
</div>
<?php
$controller->addInlineJavascript(
'jQuery("#import' . $tree->getTreeId() . '").load("import.php?gedcom_id=' . $tree->getTreeId() . '");'
);
}
?>
<div class="row<?php echo $importing ? ' hidden' : ''; ?>" id="actions<?php echo $tree->getTreeId(); ?>">
<div class="col-sm-6 col-md-3">
<h3>
<a href="index.php?ctype=gedcom&ged=<?php echo $tree->getNameUrl(); ?>">
<?php echo I18N::translate('Family tree'); ?>
</a>
</h3>
<ul class="fa-ul">
<!-- PREFERENCES -->
<li>
<i class="fa fa-li fa-cogs"></i>
<a href="admin_trees_config.php?action=general&amp;ged=<?php echo $tree->getNameUrl(); ?>">
<?php echo I18N::translate('Preferences'); ?>
<span class="sr-only">
<?php echo $tree->getTitleHtml(); ?>
</span>
</a>
</li>
<!-- PRIVACY -->
<li>
<i class="fa fa-li fa-lock"></i>
<a href="admin_trees_config.php?action=privacy&amp;ged=<?php echo $tree->getNameUrl(); ?>">
<?php echo I18N::translate('Privacy'); ?>
<span class="sr-only">
<?php echo $tree->getTitleHtml(); ?>
</span>
</a>
</li>
<!-- HOME PAGE BLOCKS-->
<li>
<i class="fa fa-li fa-th-large"></i>
<a href="index_edit.php?gedcom_id=<?php echo $tree->getTreeId(); ?>">
<?php echo I18N::translate('Change the “Home page” blocks'); ?>
<span class="sr-only">
<?php echo $tree->getTitleHtml(); ?>
</span>
</a>
</li>
<!-- DELETE -->
<li>
<i class="fa fa-li fa-trash-o"></i>
<a href="#" onclick="if (confirm('<?php echo I18N::translate('Are you sure you want to delete “%s”?', Filter::escapeJs($tree->getTitle())); ?>')) { document.delete_form<?php echo $tree->getTreeId(); ?>.submit(); } return false;">
<?php echo I18N::translate('Delete'); ?>
<span class="sr-only">
<?php echo $tree->getTitleHtml(); ?>
</span>
</a>
<form name="delete_form<?php echo $tree->getTreeId(); ?>" method="post">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="gedcom_id" value="<?php echo $tree->getTreeId(); ?>">
<?php echo Filter::getCsrf(); ?>
<!-- A11Y - forms need submit buttons, but they look ugly here -->
<button class="sr-only" onclick="return confirm('<?php echo I18N::translate('Are you sure you want to delete “%s”?', Filter::escapeJs($tree->getTitle())); ?>')" type="submit">
<?php echo I18N::translate('Delete'); ?>
</button>
</form>
</li>
<!-- SET AS DEFAULT -->
<?php if (count(Tree::getAll()) > 1): ?>
<li>
<i class="fa fa-li fa-star"></i>
<?php if ($tree->getName() == Site::getPreference('DEFAULT_GEDCOM')): ?>
<?php echo I18N::translate('Default family tree'); ?>
<?php else: ?>
<a href="#" onclick="document.defaultform<?php echo $tree->getTreeId(); ?>.submit();">
<?php echo I18N::translate('Set as default'); ?>
<span class="sr-only">
<?php echo $tree->getTitleHtml(); ?>
</span>
</a>
<form name="defaultform<?php echo $tree->getTreeId(); ?>" method="post">
<input type="hidden" name="action" value="setdefault">
<input type="hidden" name="ged" value="<?php echo $tree->getNameHtml(); ?>">
<?php echo Filter::getCsrf(); ?>
<!-- A11Y - forms need submit buttons, but they look ugly here -->
<button class="sr-only" type="submit">
<?php echo I18N::translate('Set as default'); ?>
</button>
</form>
<?php endif; ?>
</li>
<?php endif; ?>
</ul>
</div>
<div class="col-sm-6 col-md-3">
<h3>
<?php echo /* I18N: Individuals, sources, dates, places, etc. */ I18N::translate('Genealogy data'); ?>
</h3>
<ul class="fa-ul">
<!-- FIND DUPLICATES -->
<li>
<i class="fa fa-li fa-copy"></i>
<a href="admin_trees_duplicates.php?ged=<?php echo $tree->getNameUrl(); ?>">
<?php echo I18N::translate('Find duplicates'); ?>
<span class="sr-only">
<?php echo $tree->getTitleHtml(); ?>
</span>
</a>
</li>
<!-- MERGE -->
<li>
<i class="fa fa-li fa-code-fork"></i>
<a href="admin_site_merge.php?ged=<?php echo $tree->getNameUrl(); ?>">
<?php echo I18N::translate('Merge records'); ?>
<span class="sr-only">
<?php echo $tree->getTitleHtml(); ?>
</span>
</a>
</li>
<!-- UPDATE PLACE NAMES -->
<li>
<i class="fa fa-li fa-map-marker"></i>
<a href="admin_trees_places.php?ged=<?php echo $tree->getNameUrl(); ?>">
<?php echo I18N::translate('Update place names'); ?>
<span class="sr-only">
<?php echo $tree->getTitleHtml(); ?>
</span>
</a>
</li>
<!-- CHECK FOR ERRORS -->
<li>
<i class="fa fa-li fa-check"></i>
<a href="admin_trees_check.php?ged=<?php echo $tree->getNameUrl(); ?>">
<?php echo I18N::translate('Check for errors'); ?>
<span class="sr-only">
<?php echo $tree->getTitleHtml(); ?>
</span>
</a>
</li>
<!-- UNCONNECTED INDIVIDUALS -->
<li>
<i class="fa fa-li fa-chain-broken"></i>
<a href="admin_trees_unconnected.php?ged=<?php echo $tree->getNameUrl(); ?>">
<?php echo I18N::translate('Find unrelated individuals'); ?>
<span class="sr-only">
<?php echo $tree->getTitleHtml(); ?>
</span>
</a>
</li>
<!-- RENUMBER -->
<li>
<i class="fa fa-li fa-sort-numeric-asc"></i>
<a href="admin_trees_renumber.php?ged=<?php echo $tree->getNameUrl(); ?>">
<?php echo I18N::translate('Renumber'); ?>
<span class="sr-only">
<?php echo $tree->getTitleHtml(); ?>
</span>
</a>
</li>
<!-- CHANGES -->
<li>
<i class="fa fa-li fa-th-list"></i>
<a href="admin_site_change.php?gedc=<?php echo $tree->getNameUrl(); ?>">
<?php echo I18N::translate('Changes log'); ?>
<span class="sr-only">
<?php echo $tree->getTitleHtml(); ?>
</span>
</a>
</li>
</ul>
</div>
<div class="clearfix visible-sm-block"></div>
<div class="col-sm-6 col-md-3">
<h3>
<?php echo I18N::translate('Add unlinked records'); ?>
</h3>
<ul class="fa-ul">
<!-- UNLINKED INDIVIDUAL -->
<li>
<i class="fa fa-li fa-user"></i>
<a href="#" onclick="add_unlinked_indi(); return false;">
<?php echo I18N::translate('Individual'); ?>
<span class="sr-only">
<?php echo $tree->getTitleHtml(); ?>
</span>
</a>
</li>
<!-- UNLINKED SOURCE -->
<li>
<i class="fa fa-li fa-book"></i>
<a href="#" onclick="addnewsource(''); return false;">
<?php echo I18N::translate('Source'); ?>
<span class="sr-only">
<?php echo $tree->getTitleHtml(); ?>
</span>
</a>
</li>
<!-- UNLINKED REPOSITORY -->
<li>
<i class="fa fa-li fa-university"></i>
<a href="#" onclick="addnewrepository(''); return false;">
<?php echo I18N::translate('Repository'); ?>
<span class="sr-only">
<?php echo $tree->getTitleHtml(); ?>
</span>
</a>
</li>
<!-- UNLINKED MEDIA OBJECT -->
<li>
<i class="fa fa-li fa-photo"></i>
<a href="#" onclick="window.open('addmedia.php?action=showmediaform', '_blank', edit_window_specs); return false;">
<?php echo I18N::translate('Media object'); ?>
<span class="sr-only">
<?php echo $tree->getTitleHtml(); ?>
</span>
</a>
</li>
<!-- UNLINKED NOTE -->
<li>
<i class="fa fa-li fa-paragraph"></i>
<a href="#" onclick="addnewnote(''); return false;">
<span class="sr-only">
<?php echo $tree->getTitleHtml(); ?>
</span>
<?php echo I18N::translate('Shared note'); ?>
</a>
</li>
</ul>
</div>
<div class="col-sm-6 col-md-3">
<h3>
<?php echo I18N::translate('GEDCOM file'); ?>
</h3>
<ul class="fa-ul">
<!-- DOWNLOAD/Export -->
<li>
<i class="fa fa-li fa-download"></i>
<a href="admin_trees_download.php?ged=<?php echo $tree->getNameUrl(); ?>">
<?php echo I18N::translate('Export'); ?>
<span class="sr-only">
<?php echo $tree->getTitleHtml(); ?>
</span>
</a>
</li>
<!-- UPLOAD/IMPORT -->
<li>
<i class="fa fa-li fa-upload"></i>
<a href="?action=importform&amp;gedcom_id=<?php echo $tree->getTreeId(); ?>">
<?php echo I18N::translate('Import'); ?>
<span class="sr-only">
<?php echo $tree->getTitleHtml(); ?>
</span>
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<?php endif; ?>
<?php endforeach; ?>
<?php if (Auth::isAdmin()): ?>
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title">
<i class="fa fa-fw fa-plus"></i>
<a data-toggle="collapse" data-parent="#accordion" href="#create-a-family-tree">
<?php echo I18N::translate('Create a family tree'); ?>
</a>
</h2>
</div>
<div id="create-a-family-tree" class="panel-collapse collapse<?php echo Tree::getAll() ? '' : ' in'; ?>">
<div class="panel-body">
<form role="form" class="form-horizontal" method="post">
<?php echo Filter::getCsrf(); ?>
<input type="hidden" name="action" value="new_tree">
<div class="form-group">
<label for="tree_title" class="col-sm-2 control-label">
<?php echo I18N::translate('Family tree title'); ?>
</label>
<div class="col-sm-10">
<input
class="form-control"
id="tree_title"
maxlength="255"
name="tree_title"
required
type="text"
placeholder="<?php echo $default_tree_title; ?>"
>
</div>
</div>
<div class="form-group">
<label for="tree_name" class="col-sm-2 control-label">
<?php echo I18N::translate('URL'); ?>
</label>
<div class="col-sm-10">
<div class="input-group">
<span class="input-group-addon">
<?php echo WT_BASE_URL; ?>?ged=
</span>
<input
class="form-control"
id="tree_name"
maxlength="31"
name="tree_name"
pattern="[^&lt;&gt;&amp;&quot;#^$*?{}()\[\]/\\]*"
required
type="text"
value="<?php echo $default_tree_name; ?>"
>
</div>
<p class="small text-muted">
<?php echo I18N::translate('Avoid spaces and punctuation. A family name might be a good choice.'); ?>
</p>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">
<i class="fa fa-check"></i>
<?php echo /* I18N: A button label. */ I18N::translate('create'); ?>
</button>
<p class="small text-muted">
<?php echo I18N::translate('After creating the family tree, you will be able to import data from a GEDCOM file.'); ?>
</p>
</div>
</div>
</form>
</div>
</div>
</div>
<?php endif; ?>
<!-- display link to PhpGedView-WT transfer wizard on first visit to this page, before any GEDCOM is loaded -->
<?php if (count(Tree::getAll()) === 0 && count(User::all()) === 1): ?>
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title">
<i class="fa fa-fw fa-magic"></i>
<a data-toggle="collapse" data-parent="#accordion" href="#pgv-import-wizard">
<?php echo I18N::translate('PhpGedView to webtrees transfer wizard'); ?>
</a>
</h2>
</div>
<div id="pgv-import-wizard" class="panel-collapse collapse">
<div class="panel-body">
<p>
<?php echo I18N::translate('The PhpGedView to webtrees wizard is an automated process to assist administrators make the move from a PhpGedView installation to a new webtrees one. It will transfer all PhpGedView GEDCOM and other database information directly to your new webtrees database. The following requirements are necessary:'); ?>
</p>
<ul>
<li>
<?php echo I18N::translate('webtrees database must be on the same server as PhpGedViews'); ?>
</li>
<li>
<?php echo /* I18N: %s is a number */ I18N::translate('PhpGedView must be version 4.2.3, or any SVN up to #%s', I18N::digits(7101)); ?>
</li>
<li>
<?php echo I18N::translate('All changes in PhpGedView must be accepted'); ?>
</li>
<li>
<?php echo I18N::translate('All existing PhpGedView users must have distinct email addresses'); ?>
</li>
</ul>
<p>
<?php echo I18N::translate('<b>Important note:</b> The transfer wizard is not able to assist with moving media items. You will need to set up and move or copy your media configuration and objects separately after the transfer wizard is finished.'); ?>
</p>
<p>
<a href="admin_pgv_to_wt.php">
<?php echo I18N::translate('PhpGedView to webtrees transfer wizard'); ?>
</a>
</p>
</div>
</div>
</div>
<?php endif; ?>
<!-- BULK LOAD/SYNCHRONISE GEDCOM FILES -->
<?php if (count($gedcom_files) >= $multiple_tree_threshold): ?>
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title">
<i class="fa fa-fw fa-refresh"></i>
<a data-toggle="collapse" data-parent="#accordion" href="#synchronize-gedcom-files">
<?php echo I18N::translate('Synchronize family trees with GEDCOM files'); ?>
</a>
</h2>
</div>
<div id="synchronize-gedcom-files" class="panel-collapse collapse">
<div class="panel-body">
<p>
<?php echo I18N::translate('Create, update, and delete a family tree for every GEDCOM file in the data folder.'); ?>
</p>
<form method="post" class="form form-horizontal">
<?php echo Filter::getCsrf(); ?>
<input type="hidden" name="action" value="synchronize">
<button type="submit" class="btn btn-danger">
<i class="fa fa-refresh"></i>
<?php echo /* I18N: A button label. */ I18N::translate('continue'); ?>
</button>
<p class="small text-muted">
<?php echo I18N::translate('Caution! This may take a long time. Be patient.'); ?>
</p>
</form>
</div>
</div>
</div>
<?php endif; ?>
</div>

View file

@ -1,205 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
/**
* Defined in session.php
*
* @global Tree $WT_TREE
*/
global $WT_TREE;
use Fisharebest\Webtrees\Controller\PageController;
use Fisharebest\Webtrees\Functions\FunctionsEdit;
define('WT_SCRIPT_NAME', 'admin_trees_merge.php');
require './includes/session.php';
$controller = new PageController;
$controller
->restrictAccess(Auth::isManager($WT_TREE))
->setPageTitle(I18N::translate('Merge family trees'))
->pageHeader();
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li><a href="admin_trees_manage.php"><?php echo I18N::translate('Manage family trees'); ?></a></li>
<li class="active"><?php echo $controller->getPageTitle(); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<?php
$tree1_id = Filter::post('tree1_id');
$tree2_id = Filter::post('tree2_id');
if ($tree1_id && $tree2_id != $tree1_id) {
// Every XREF used by both trees
$xrefs = Database::prepare(
"SELECT xref, type FROM (" .
" SELECT i_id AS xref, 'INDI' AS type FROM `##individuals` WHERE i_file = ?" .
" UNION " .
" SELECT f_id AS xref, 'FAM' AS type FROM `##families` WHERE f_file = ?" .
" UNION " .
" SELECT s_id AS xref, 'SOUR' AS type FROM `##sources` WHERE s_file = ?" .
" UNION " .
" SELECT m_id AS xref, 'OBJE' AS type FROM `##media` WHERE m_file = ?" .
" UNION " .
" SELECT o_id AS xref, o_type AS type FROM `##other` WHERE o_file = ? AND o_type NOT IN ('HEAD', 'TRLR')" .
") AS this_tree JOIN (" .
" SELECT xref FROM `##change` WHERE gedcom_id = ?" .
" UNION " .
" SELECT i_id AS xref FROM `##individuals` WHERE i_file = ?" .
" UNION " .
" SELECT f_id AS xref FROM `##families` WHERE f_file = ?" .
" UNION " .
" SELECT s_id AS xref FROM `##sources` WHERE s_file = ?" .
" UNION " .
" SELECT m_id AS xref FROM `##media` WHERE m_file = ?" .
" UNION " .
" SELECT o_id AS xref FROM `##other` WHERE o_file = ? AND o_type NOT IN ('HEAD', 'TRLR')" .
") AS other_trees USING (xref)"
)->execute(array(
$tree1_id, $tree1_id, $tree1_id, $tree1_id, $tree1_id,
$tree2_id, $tree2_id, $tree2_id, $tree2_id, $tree2_id, $tree2_id,
))->fetchAssoc();
if ($xrefs) {
$tree1 = Tree::findById($tree1_id);
$tree2 = Tree::findById($tree2_id);
echo
'<p>', I18N::translate('In a family tree, each record has an internal reference number (called an “XREF”) such as “F123” or “R14”.'), '</p>',
'<p>',
I18N::plural(
/* I18N: An XREF is the identification number used in GEDCOM files. */
'The two family trees have %1$s record which uses the same “XREF”.',
'The two family trees have %1$s records which use the same “XREF”.',
count($xrefs), count($xrefs)
),
'</p>',
'<p>',
I18N::translate('You must renumber the records in one of the trees before you can merge them.'),
'</p>',
'<p>',
'<a class="current" href="admin_trees_renumber.php?ged=', $tree1->getNameUrl(), '">',
I18N::translate('Renumber family tree'), ' — ', $tree1->getTitleHtml(),
'</a>',
'</p>',
'<p>',
'<a class="current" href="admin_trees_renumber.php?ged=', $tree2->getNameUrl(), '">',
I18N::translate('Renumber family tree'), ' — ', $tree2->getTitleHtml(),
'</a>',
'</p>';
} else {
Database::beginTransaction();
Database::exec(
"LOCK TABLE" .
" `##individuals` WRITE," .
" `##individuals` AS individuals2 READ," .
" `##families` WRITE," .
" `##families` AS families2 READ," .
" `##sources` WRITE," .
" `##sources` AS sources2 READ," .
" `##media` WRITE," .
" `##media` AS media2 READ," .
" `##other` WRITE," .
" `##other` AS other2 READ," .
" `##name` WRITE," .
" `##name` AS name2 READ," .
" `##placelinks` WRITE," .
" `##placelinks` AS placelinks2 READ," .
" `##change` WRITE," .
" `##change` AS change2 READ," .
" `##dates` WRITE," .
" `##dates` AS dates2 READ," .
" `##default_resn` WRITE," .
" `##default_resn` AS default_resn2 READ," .
" `##hit_counter` WRITE," .
" `##hit_counter` AS hit_counter2 READ," .
" `##link` WRITE," .
" `##link` AS link2 READ"
);
Database::prepare(
"INSERT INTO `##individuals` (i_id, i_file, i_rin, i_sex, i_gedcom)" .
" SELECT i_id, ?, i_rin, i_sex, i_gedcom FROM `##individuals` AS individuals2 WHERE i_file = ?"
)->execute(array($tree2_id, $tree1_id));
Database::prepare(
"INSERT INTO `##families` (f_id, f_file, f_husb, f_wife, f_gedcom, f_numchil)" .
" SELECT f_id, ?, f_husb, f_wife, f_gedcom, f_numchil FROM `##families` AS families2 WHERE f_file = ?"
)->execute(array($tree2_id, $tree1_id));
Database::prepare(
"INSERT INTO `##sources` (s_id, s_file, s_name, s_gedcom)" .
" SELECT s_id, ?, s_name, s_gedcom FROM `##sources` AS sources2 WHERE s_file = ?"
)->execute(array($tree2_id, $tree1_id));
Database::prepare(
"INSERT INTO `##media` (m_id, m_ext, m_type, m_titl, m_filename, m_file, m_gedcom)" .
" SELECT m_id, m_ext, m_type, m_titl, m_filename, ?, m_gedcom FROM `##media` AS media2 WHERE m_file = ?"
)->execute(array($tree2_id, $tree1_id));
Database::prepare(
"INSERT INTO `##other` (o_id, o_file, o_type, o_gedcom)" .
" SELECT o_id, ?, o_type, o_gedcom FROM `##other` AS other2 WHERE o_file = ? AND o_type NOT IN ('HEAD', 'TRLR')"
)->execute(array($tree2_id, $tree1_id));
Database::prepare(
"INSERT INTO `##name` (n_file, n_id, n_num, n_type, n_sort, n_full, n_surname, n_surn, n_givn, n_soundex_givn_std, n_soundex_surn_std, n_soundex_givn_dm, n_soundex_surn_dm)" .
" SELECT ?, n_id, n_num, n_type, n_sort, n_full, n_surname, n_surn, n_givn, n_soundex_givn_std, n_soundex_surn_std, n_soundex_givn_dm, n_soundex_surn_dm FROM `##name` AS name2 WHERE n_file = ?"
)->execute(array($tree2_id, $tree1_id));
Database::prepare(
"INSERT INTO `##placelinks` (pl_p_id, pl_gid, pl_file)" .
" SELECT pl_p_id, pl_gid, ? FROM `##placelinks` AS placelinks2 WHERE pl_file = ?"
)->execute(array($tree2_id, $tree1_id));
Database::prepare(
"INSERT INTO `##dates` (d_day, d_month, d_mon, d_year, d_julianday1, d_julianday2, d_fact, d_gid, d_file, d_type)" .
" SELECT d_day, d_month, d_mon, d_year, d_julianday1, d_julianday2, d_fact, d_gid, ?, d_type FROM `##dates` AS dates2 WHERE d_file = ?"
)->execute(array($tree2_id, $tree1_id));
Database::prepare(
"INSERT INTO `##default_resn` (gedcom_id, xref, tag_type, resn)" .
" SELECT ?, xref, tag_type, resn FROM `##default_resn` AS default_resn2 WHERE gedcom_id = ?"
)->execute(array($tree2_id, $tree1_id));
Database::prepare(
"INSERT INTO `##link` (l_file, l_from, l_type, l_to)" .
" SELECT ?, l_from, l_type, l_to FROM `##link` AS link2 WHERE l_file = ?"
)->execute(array($tree2_id, $tree1_id));
// This table may contain old (deleted) references, which could clash. IGNORE these.
Database::prepare(
"INSERT IGNORE INTO `##change` (change_time, status, gedcom_id, xref, old_gedcom, new_gedcom, user_id)" .
" SELECT change_time, status, ?, xref, old_gedcom, new_gedcom, user_id FROM `##change` AS change2 WHERE gedcom_id = ?"
)->execute(array($tree2_id, $tree1_id));
// This table may contain old (deleted) references, which could clash. IGNORE these.
Database::prepare(
"INSERT IGNORE INTO `##hit_counter` (gedcom_id, page_name, page_parameter, page_count)" .
" SELECT ?, page_name, page_parameter, page_count FROM `##hit_counter` AS hit_counter2 WHERE gedcom_id = ? AND page_name <> 'index.php'"
)->execute(array($tree2_id, $tree1_id));
Database::exec("UNLOCK TABLES");
Database::commit();
echo '<p>', I18N::translate('The family trees have been merged successfully.'), '</p>';
}
} else {
echo '<form method="post">';
echo '<input type="hidden" name="go" value="1">';
echo '<p>', I18N::translate(/* I18N: Copy all the records from [family tree 1] into [family tree 2] */
'Copy all the records from %1$s into %2$s.',
FunctionsEdit::selectEditControl('tree1_id', Tree::getIdList(), '', null),
FunctionsEdit::selectEditControl('tree2_id', Tree::getIdList(), '', null)
),
'</p>';
echo '<button type="submit" class="btn btn-primary">';
echo '<i class="fa fa-check"></i> ', /* I18N: A button label. */ I18N::translate('continue');
echo '</button>';
echo '</form>';
}

View file

@ -1,132 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
/**
* Defined in session.php
*
* @global Tree $WT_TREE
*/
global $WT_TREE;
use Fisharebest\Webtrees\Controller\PageController;
define('WT_SCRIPT_NAME', 'admin_trees_places.php');
require './includes/session.php';
$search = Filter::post('search', null, Filter::get('search'));
$replace = Filter::post('replace');
$confirm = Filter::post('confirm');
$changes = array();
if ($search && $replace) {
$rows = Database::prepare(
"SELECT i_id AS xref, i_gedcom AS gedcom" .
" FROM `##individuals`" .
" LEFT JOIN `##change` ON (i_id = xref AND i_file=gedcom_id AND status='pending')" .
" WHERE i_file = ?" .
" AND COALESCE(new_gedcom, i_gedcom) REGEXP CONCAT('\n2 PLAC ([^\n]*, )*', ?, '(\n|$)')"
)->execute(array($WT_TREE->getTreeId(), preg_quote($search)))->fetchAll();
foreach ($rows as $row) {
$record = Individual::getInstance($row->xref, $WT_TREE, $row->gedcom);
foreach ($record->getFacts() as $fact) {
$old_place = $fact->getAttribute('PLAC');
if (preg_match('/(^|, )' . preg_quote($search, '/') . '$/i', $old_place)) {
$new_place = preg_replace('/(^|, )' . preg_quote($search, '/') . '$/i', '$1' . $replace, $old_place);
$changes[$old_place] = $new_place;
if ($confirm == 'update') {
$gedcom = preg_replace('/(\n2 PLAC (?:.*, )*)' . preg_quote($search, '/') . '(\n|$)/i', '$1' . $replace . '$2', $fact->getGedcom());
$record->updateFact($fact->getFactId(), $gedcom, false);
}
}
}
}
$rows = Database::prepare(
"SELECT f_id AS xref, f_gedcom AS gedcom" .
" FROM `##families`" .
" LEFT JOIN `##change` ON (f_id = xref AND f_file=gedcom_id AND status='pending')" .
" WHERE f_file = ?" .
" AND COALESCE(new_gedcom, f_gedcom) REGEXP CONCAT('\n2 PLAC ([^\n]*, )*', ?, '(\n|$)')"
)->execute(array($WT_TREE->getTreeId(), preg_quote($search)))->fetchAll();
foreach ($rows as $row) {
$record = Family::getInstance($row->xref, $WT_TREE, $row->gedcom);
foreach ($record->getFacts() as $fact) {
$old_place = $fact->getAttribute('PLAC');
if (preg_match('/(^|, )' . preg_quote($search, '/') . '$/i', $old_place)) {
$new_place = preg_replace('/(^|, )' . preg_quote($search, '/') . '$/i', '$1' . $replace, $old_place);
$changes[$old_place] = $new_place;
if ($confirm == 'update') {
$gedcom = preg_replace('/(\n2 PLAC (?:.*, )*)' . preg_quote($search, '/') . '(\n|$)/i', '$1' . $replace . '$2', $fact->getGedcom());
$record->updateFact($fact->getFactId(), $gedcom, false);
}
}
}
}
}
$controller = new PageController;
$controller
->restrictAccess(Auth::isManager($WT_TREE))
->setPageTitle(I18N::translate('Update all the place names in a family tree') . ' — ' . $WT_TREE->getTitleHtml())
->addInlineJavascript('autocomplete();')
->pageHeader();
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li><a href="admin_trees_manage.php"><?php echo I18N::translate('Manage family trees'); ?></a></li>
<li class="active"><?php echo $controller->getPageTitle(); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<p>
<?php echo I18N::translate('This will update the highest-level part or parts of the place name. For example, “Mexico” will match “Quintana Roo, Mexico”, but not “Santa Fe, New Mexico”.'); ?>
</p>
<form method="post">
<dl>
<dt><label for="search"><?php echo I18N::translate('Search for'); ?></label></dt>
<dd><input name="search" id="search" type="text" size="60" value="<?php echo Filter::escapeHtml($search); ?>" data-autocomplete-type="PLAC" required autofocus></dd>
<dt><label for="replace"><?php echo I18N::translate('Replace with'); ?></label></dt>
<dd><input name="replace" id="replace" type="text" size="60" value="<?php echo Filter::escapeHtml($replace); ?>" data-autocomplete-type="PLAC" required></dd>
</dl>
<button type="submit" value="preview"><?php echo /* I18N: A button label. */ I18N::translate('preview'); ?></button>
<button type="submit" value="update" name="confirm"><?php echo /* I18N: A button label. */ I18N::translate('update'); ?></button>
</form>
<?php if ($search && $replace) { ?>
<?php if ($changes) { ?>
<p>
<?php echo $confirm ? I18N::translate('The following places have been changed:') : I18N::translate('The following places would be changed:'); ?>
</p>
<ul>
<?php foreach ($changes as $old_place => $new_place) { ?>
<li>
<?php echo Filter::escapeHtml($old_place); ?>
&rarr;
<?php echo Filter::escapeHtml($new_place); ?>
</li>
<?php } ?>
</ul>
<?php } else { ?>
<p>
<?php echo I18N::translate('No places have been found.'); ?>
</p>
<?php } ?>
<?php } ?>

View file

@ -1,287 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
/**
* Defined in session.php
*
* @global Tree $WT_TREE
*/
global $WT_TREE;
use Fisharebest\Webtrees\Controller\PageController;
define('WT_SCRIPT_NAME', 'admin_trees_renumber.php');
require './includes/session.php';
$controller = new PageController;
$controller
->restrictAccess(Auth::isManager($WT_TREE))
->setPageTitle(I18N::translate(/* I18N: Renumber the records in a family tree */ 'Renumber family tree') . ' — ' . $WT_TREE->getTitleHtml())
->pageHeader();
// Every XREF used by this tree and also used by some other tree
$xrefs = Database::prepare(
"SELECT xref, type FROM (" .
" SELECT i_id AS xref, 'INDI' AS type FROM `##individuals` WHERE i_file = ?" .
" UNION " .
" SELECT f_id AS xref, 'FAM' AS type FROM `##families` WHERE f_file = ?" .
" UNION " .
" SELECT s_id AS xref, 'SOUR' AS type FROM `##sources` WHERE s_file = ?" .
" UNION " .
" SELECT m_id AS xref, 'OBJE' AS type FROM `##media` WHERE m_file = ?" .
" UNION " .
" SELECT o_id AS xref, o_type AS type FROM `##other` WHERE o_file = ? AND o_type NOT IN ('HEAD', 'TRLR')" .
") AS this_tree JOIN (" .
" SELECT xref FROM `##change` WHERE gedcom_id <> ?" .
" UNION " .
" SELECT i_id AS xref FROM `##individuals` WHERE i_file <> ?" .
" UNION " .
" SELECT f_id AS xref FROM `##families` WHERE f_file <> ?" .
" UNION " .
" SELECT s_id AS xref FROM `##sources` WHERE s_file <> ?" .
" UNION " .
" SELECT m_id AS xref FROM `##media` WHERE m_file <> ?" .
" UNION " .
" SELECT o_id AS xref FROM `##other` WHERE o_file <> ? AND o_type NOT IN ('HEAD', 'TRLR')" .
") AS other_trees USING (xref)"
)->execute(array(
$WT_TREE->getTreeId(), $WT_TREE->getTreeId(), $WT_TREE->getTreeId(), $WT_TREE->getTreeId(), $WT_TREE->getTreeId(),
$WT_TREE->getTreeId(), $WT_TREE->getTreeId(), $WT_TREE->getTreeId(), $WT_TREE->getTreeId(), $WT_TREE->getTreeId(), $WT_TREE->getTreeId(),
))->fetchAssoc();
echo '<h1>', $controller->getPageTitle(), '</h1>';
if (Filter::get('action') === 'renumber') {
foreach ($xrefs as $old_xref => $type) {
Database::beginTransaction();
Database::exec(
"LOCK TABLE `##individuals` WRITE," .
" `##families` WRITE," .
" `##sources` WRITE," .
" `##media` WRITE," .
" `##other` WRITE," .
" `##name` WRITE," .
" `##placelinks` WRITE," .
" `##change` WRITE," .
" `##next_id` WRITE," .
" `##dates` WRITE," .
" `##default_resn` WRITE," .
" `##hit_counter` WRITE," .
" `##link` WRITE," .
" `##user_gedcom_setting` WRITE"
);
$new_xref = $WT_TREE->getNewXref($type);
switch ($type) {
case 'INDI':
Database::prepare(
"UPDATE `##individuals` SET i_id = ?, i_gedcom = REPLACE(i_gedcom, ?, ?) WHERE i_id = ? AND i_file = ?"
)->execute(array($new_xref, "0 @$old_xref@ INDI\n", "0 @$new_xref@ INDI\n", $old_xref, $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##families` JOIN `##link` ON (l_file = f_file AND l_to = ? AND l_type = 'HUSB') SET f_gedcom = REPLACE(f_gedcom, ?, ?) WHERE f_file = ?"
)->execute(array($old_xref, " HUSB @$old_xref@", " HUSB @$new_xref@", $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##families` JOIN `##link` ON (l_file = f_file AND l_to = ? AND l_type = 'WIFE') SET f_gedcom = REPLACE(f_gedcom, ?, ?) WHERE f_file = ?"
)->execute(array($old_xref, " WIFE @$old_xref@", " WIFE @$new_xref@", $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##families` JOIN `##link` ON (l_file = f_file AND l_to = ? AND l_type = 'CHIL') SET f_gedcom = REPLACE(f_gedcom, ?, ?) WHERE f_file = ?"
)->execute(array($old_xref, " CHIL @$old_xref@", " CHIL @$new_xref@", $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##families` JOIN `##link` ON (l_file = f_file AND l_to = ? AND l_type = 'ASSO') SET f_gedcom = REPLACE(f_gedcom, ?, ?) WHERE f_file = ?"
)->execute(array($old_xref, " ASSO @$old_xref@", " ASSO @$new_xref@", $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##families` JOIN `##link` ON (l_file = f_file AND l_to = ? AND l_type = '_ASSO') SET f_gedcom = REPLACE(f_gedcom, ?, ?) WHERE f_file = ?"
)->execute(array($old_xref, " _ASSO @$old_xref@", " _ASSO @$new_xref@", $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##individuals` JOIN `##link` ON (l_file = i_file AND l_to = ? AND l_type = 'ASSO') SET i_gedcom = REPLACE(i_gedcom, ?, ?) WHERE i_file = ?"
)->execute(array($old_xref, " ASSO @$old_xref@", " ASSO @$new_xref@", $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##individuals` JOIN `##link` ON (l_file = i_file AND l_to = ? AND l_type = '_ASSO') SET i_gedcom = REPLACE(i_gedcom, ?, ?) WHERE i_file = ?"
)->execute(array($old_xref, " _ASSO @$old_xref@", " _ASSO @$new_xref@", $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##placelinks` SET pl_gid = ? WHERE pl_gid = ? AND pl_file = ?"
)->execute(array($new_xref, $old_xref, $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##dates` SET d_gid = ? WHERE d_gid = ? AND d_file = ?"
)->execute(array($new_xref, $old_xref, $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##user_gedcom_setting` SET setting_value = ? WHERE setting_value = ? AND gedcom_id = ? AND setting_name IN ('gedcomid', 'rootid')"
)->execute(array($new_xref, $old_xref, $WT_TREE->getTreeId()));
break;
case 'FAM':
Database::prepare(
"UPDATE `##families` SET f_id = ?, f_gedcom = REPLACE(f_gedcom, ?, ?) WHERE f_id = ? AND f_file = ?"
)->execute(array($new_xref, "0 @$old_xref@ FAM\n", "0 @$new_xref@ FAM\n", $old_xref, $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##individuals` JOIN `##link` ON (l_file = i_file AND l_to = ? AND l_type = 'FAMC') SET i_gedcom = REPLACE(i_gedcom, ?, ?) WHERE i_file = ?"
)->execute(array($old_xref, " FAMC @$old_xref@", " FAMC @$new_xref@", $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##individuals` JOIN `##link` ON (l_file = i_file AND l_to = ? AND l_type = 'FAMS') SET i_gedcom = REPLACE(i_gedcom, ?, ?) WHERE i_file = ?"
)->execute(array($old_xref, " FAMS @$old_xref@", " FAMS @$new_xref@", $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##placelinks` SET pl_gid = ? WHERE pl_gid = ? AND pl_file = ?"
)->execute(array($new_xref, $old_xref, $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##dates` SET d_gid = ? WHERE d_gid = ? AND d_file = ?"
)->execute(array($new_xref, $old_xref, $WT_TREE->getTreeId()));
break;
case 'SOUR':
Database::prepare(
"UPDATE `##sources` SET s_id = ?, s_gedcom = REPLACE(s_gedcom, ?, ?) WHERE s_id = ? AND s_file = ?"
)->execute(array($new_xref, "0 @$old_xref@ SOUR\n", "0 @$new_xref@ SOUR\n", $old_xref, $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##individuals` JOIN `##link` ON (l_file = i_file AND l_to = ? AND l_type = 'SOUR') SET i_gedcom = REPLACE(i_gedcom, ?, ?) WHERE i_file = ?"
)->execute(array($old_xref, " SOUR @$old_xref@", " SOUR @$new_xref@", $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##families` JOIN `##link` ON (l_file = f_file AND l_to = ? AND l_type = 'SOUR') SET f_gedcom = REPLACE(f_gedcom, ?, ?) WHERE f_file = ?"
)->execute(array($old_xref, " SOUR @$old_xref@", " SOUR @$new_xref@", $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##media` JOIN `##link` ON (l_file = m_file AND l_to = ? AND l_type = 'SOUR') SET m_gedcom = REPLACE(m_gedcom, ?, ?) WHERE m_file = ?"
)->execute(array($old_xref, " SOUR @$old_xref@", " SOUR @$new_xref@", $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##other` JOIN `##link` ON (l_file = o_file AND l_to = ? AND l_type = 'SOUR') SET o_gedcom = REPLACE(o_gedcom, ?, ?) WHERE o_file = ?"
)->execute(array($old_xref, " SOUR @$old_xref@", " SOUR @$new_xref@", $WT_TREE->getTreeId()));
break;
case 'REPO':
Database::prepare(
"UPDATE `##other` SET o_id = ?, o_gedcom = REPLACE(o_gedcom, ?, ?) WHERE o_id = ? AND o_file = ?"
)->execute(array($new_xref, "0 @$old_xref@ REPO\n", "0 @$new_xref@ REPO\n", $old_xref, $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##sources` JOIN `##link` ON (l_file = s_file AND l_to = ? AND l_type = 'REPO') SET s_gedcom = REPLACE(s_gedcom, ?, ?) WHERE s_file = ?"
)->execute(array($old_xref, " REPO @$old_xref@", " REPO @$new_xref@", $WT_TREE->getTreeId()));
break;
case 'NOTE':
Database::prepare(
"UPDATE `##other` SET o_id = ?, o_gedcom = REPLACE(REPLACE(o_gedcom, ?, ?), ?, ?) WHERE o_id = ? AND o_file = ?"
)->execute(array($new_xref, "0 @$old_xref@ NOTE\n", "0 @$new_xref@ NOTE\n", "0 @$old_xref@ NOTE ", "0 @$new_xref@ NOTE ", $old_xref, $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##individuals` JOIN `##link` ON (l_file = i_file AND l_to = ? AND l_type = 'NOTE') SET i_gedcom = REPLACE(i_gedcom, ?, ?) WHERE i_file = ?"
)->execute(array($old_xref, " NOTE @$old_xref@", " NOTE @$new_xref@", $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##families` JOIN `##link` ON (l_file = f_file AND l_to = ? AND l_type = 'NOTE') SET f_gedcom = REPLACE(f_gedcom, ?, ?) WHERE f_file = ?"
)->execute(array($old_xref, " NOTE @$old_xref@", " NOTE @$new_xref@", $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##media` JOIN `##link` ON (l_file = m_file AND l_to = ? AND l_type = 'NOTE') SET m_gedcom = REPLACE(m_gedcom, ?, ?) WHERE m_file = ?"
)->execute(array($old_xref, " NOTE @$old_xref@", " NOTE @$new_xref@", $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##sources` JOIN `##link` ON (l_file = s_file AND l_to = ? AND l_type = 'NOTE') SET s_gedcom = REPLACE(s_gedcom, ?, ?) WHERE s_file = ?"
)->execute(array($old_xref, " NOTE @$old_xref@", " NOTE @$new_xref@", $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##other` JOIN `##link` ON (l_file = o_file AND l_to = ? AND l_type = 'NOTE') SET o_gedcom = REPLACE(o_gedcom, ?, ?) WHERE o_file = ?"
)->execute(array($old_xref, " NOTE @$old_xref@", " NOTE @$new_xref@", $WT_TREE->getTreeId()));
break;
case 'OBJE':
Database::prepare(
"UPDATE `##media` SET m_id = ?, m_gedcom = REPLACE(m_gedcom, ?, ?) WHERE m_id = ? AND m_file = ?"
)->execute(array($new_xref, "0 @$old_xref@ OBJE\n", "0 @$new_xref@ OBJE\n", $old_xref, $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##individuals` JOIN `##link` ON (l_file = i_file AND l_to = ? AND l_type = 'OBJE') SET i_gedcom = REPLACE(i_gedcom, ?, ?) WHERE i_file = ?"
)->execute(array($old_xref, " OBJE @$old_xref@", " OBJE @$new_xref@", $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##families` JOIN `##link` ON (l_file = f_file AND l_to = ? AND l_type = 'OBJE') SET f_gedcom = REPLACE(f_gedcom, ?, ?) WHERE f_file = ?"
)->execute(array($old_xref, " OBJE @$old_xref@", " OBJE @$new_xref@", $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##media` JOIN `##link` ON (l_file = m_file AND l_to = ? AND l_type = 'OBJE') SET m_gedcom = REPLACE(m_gedcom, ?, ?) WHERE m_file = ?"
)->execute(array($old_xref, " OBJE @$old_xref@", " OBJE @$new_xref@", $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##sources` JOIN `##link` ON (l_file = s_file AND l_to = ? AND l_type = 'OBJE') SET s_gedcom = REPLACE(s_gedcom, ?, ?) WHERE s_file = ?"
)->execute(array($old_xref, " OBJE @$old_xref@", " OBJE @$new_xref@", $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##other` JOIN `##link` ON (l_file = o_file AND l_to = ? AND l_type = 'OBJE') SET o_gedcom = REPLACE(o_gedcom, ?, ?) WHERE o_file = ?"
)->execute(array($old_xref, " OBJE @$old_xref@", " OBJE @$new_xref@", $WT_TREE->getTreeId()));
break;
default:
Database::prepare(
"UPDATE `##other` SET o_id = ?, o_gedcom = REPLACE(o_gedcom, ?, ?) WHERE o_id = ? AND o_file = ?"
)->execute(array($new_xref, "0 @$old_xref@ $type\n", "0 @$new_xref@ $type\n", $old_xref, $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##individuals` JOIN `##link` ON (l_file = i_file AND l_to = ?) SET i_gedcom = REPLACE(i_gedcom, ?, ?) WHERE i_file = ?"
)->execute(array($old_xref, " @$old_xref@", " @$new_xref@", $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##families` JOIN `##link` ON (l_file = f_file AND l_to = ?) SET f_gedcom = REPLACE(f_gedcom, ?, ?) WHERE f_file = ?"
)->execute(array($old_xref, " @$old_xref@", " @$new_xref@", $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##media` JOIN `##link` ON (l_file = m_file AND l_to = ?) SET m_gedcom = REPLACE(m_gedcom, ?, ?) WHERE m_file = ?"
)->execute(array($old_xref, " @$old_xref@", " @$new_xref@", $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##sources` JOIN `##link` ON (l_file = s_file AND l_to = ?) SET s_gedcom = REPLACE(s_gedcom, ?, ?) WHERE s_file = ?"
)->execute(array($old_xref, " @$old_xref@", " @$new_xref@", $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##other` JOIN `##link` ON (l_file = o_file AND l_to = ?) SET o_gedcom = REPLACE(o_gedcom, ?, ?) WHERE o_file = ?"
)->execute(array($old_xref, " @$old_xref@", " @$new_xref@", $WT_TREE->getTreeId()));
break;
}
Database::prepare(
"UPDATE `##name` SET n_id = ? WHERE n_id = ? AND n_file = ?"
)->execute(array($new_xref, $old_xref, $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##default_resn` SET xref = ? WHERE xref = ? AND gedcom_id = ?"
)->execute(array($new_xref, $old_xref, $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##hit_counter` SET page_parameter = ? WHERE page_parameter = ? AND gedcom_id = ?"
)->execute(array($new_xref, $old_xref, $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##link` SET l_from = ? WHERE l_from = ? AND l_file = ?"
)->execute(array($new_xref, $old_xref, $WT_TREE->getTreeId()));
Database::prepare(
"UPDATE `##link` SET l_to = ? WHERE l_to = ? AND l_file = ?"
)->execute(array($new_xref, $old_xref, $WT_TREE->getTreeId()));
echo '<p>', I18N::translate('The record %1$s has been renamed to %2$s.', $old_xref, $new_xref), '</p>';
unset($xrefs[$old_xref]);
Database::exec("UNLOCK TABLES");
Database::commit();
try {
Database::prepare(
"UPDATE `##favorite` SET xref = ? WHERE xref = ? AND gedcom_id = ?"
)->execute(array($new_xref, $old_xref, $WT_TREE->getTreeId()));
} catch (\Exception $ex) {
// Perhaps the favorites module was not installed?
}
// How much time do we have left?
if (microtime(true) - WT_START_TIME > ini_get('max_execution_time') - 2) {
echo '<p>', I18N::translate('The servers time limit has been reached.'), '</p>';
break;
}
}
if ($xrefs) {
}
} else {
echo '<p>', I18N::translate('In a family tree, each record has an internal reference number (called an “XREF”) such as “F123” or “R14”.'), '</p>';
echo '<p>', I18N::translate('You can renumber the records in a family tree, so that these internal reference numbers are not duplicated in any other family tree.'), '</p>';
}
echo '<p>', I18N::plural(
'This family tree has %s record which uses the same “XREF” as another family tree.',
'This family tree has %s records which use the same “XREF” as another family tree.',
count($xrefs), count($xrefs)
), '</p>';
if ($xrefs) {
// We use GET (not POST) for this update operation - because we want the user to
// be able to press F5 to continue after a timeout.
echo '<form>';
echo '<p>', I18N::translate('You can renumber this family tree.'), '</p>';
echo '<button type="submit" class="btn btn-primary">';
echo '<i class="fa fa-check"></i> ', /* I18N: A button label. */ I18N::translate('continue');
echo '</button>';
echo '<input type="hidden" name="action" value="renumber">';
echo '<input type="hidden" name="ged" value="', $WT_TREE->getNameHtml(), '">';
echo '</form>';
echo '<p>', I18N::translate('Caution! This may take a long time. Be patient.'), '</p>';
}

View file

@ -1,91 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
/**
* Defined in session.php
*
* @global Tree $WT_TREE
*/
global $WT_TREE;
use Fisharebest\Algorithm\ConnectedComponent;
use Fisharebest\Webtrees\Controller\PageController;
define('WT_SCRIPT_NAME', 'admin_trees_unconnected.php');
require './includes/session.php';
$controller = new PageController;
$controller
->restrictAccess(Auth::isManager($WT_TREE))
->setPageTitle(I18N::translate('Find unrelated individuals') . ' — ' . $WT_TREE->getTitleHtml())
->pageHeader();
$rows = Database::prepare(
"SELECT l_from, l_to FROM `##link` WHERE l_file = :tree_id AND l_type IN ('FAMS', 'FAMC')"
)->execute(array(
'tree_id' => $WT_TREE->getTreeId(),
))->fetchAll();
$graph = array();
foreach ($rows as $row) {
$graph[$row->l_from][$row->l_to] = 1;
$graph[$row->l_to][$row->l_from] = 1;
}
$algorithm = new ConnectedComponent($graph);
$components = $algorithm->findConnectedComponents();
$root = $controller->getSignificantIndividual();
$root_id = $root->getXref();
/** @var Individual[][] */
$individual_groups = array();
$group_number = 1;
foreach ($components as $key => $component) {
if (!in_array($root_id, $component)) {
$individuals = array();
foreach ($component as $xref) {
$individual = Individual::getInstance($xref, $WT_TREE);
if ($individual instanceof Individual) {
$individuals[] = $individual;
}
}
$individual_groups[$group_number++] = $individuals;
}
}
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li><a href="admin_trees_manage.php"><?php echo I18N::translate('Manage family trees'); ?></a></li>
<li class="active"><?php echo $controller->getPageTitle(); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<p><?php echo I18N::translate('These groups of individuals are not related to %s.', $root->getFullName()) ?></p>
<?php foreach ($individual_groups as $group): ?>
<h2><?php echo I18N::plural('%s individual', '%s individuals', count($group), I18N::number(count($group))) ?></h2>
<ul>
<?php foreach ($group as $individual): ?>
<li>
<a href="<?php echo $individual->getHtmlUrl() ?>"><?php echo $individual->getFullName() ?></a>
</li>
<?php endforeach ?>
</ul>
<?php endforeach ?>

View file

@ -1,862 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
use Fisharebest\Webtrees\Controller\PageController;
use Fisharebest\Webtrees\Functions\FunctionsDate;
use Fisharebest\Webtrees\Functions\FunctionsEdit;
use Fisharebest\Webtrees\Functions\FunctionsPrint;
use PDO;
/**
* Defined in session.php
*
* @global Tree $WT_TREE
*/
global $WT_TREE;
define('WT_SCRIPT_NAME', 'admin_users.php');
require './includes/session.php';
$controller = new PageController;
$controller->restrictAccess(Auth::isAdmin());
// Valid values for form variables
$ALL_EDIT_OPTIONS = array(
'none' => /* I18N: Listbox entry; name of a role */ I18N::translate('Visitor'),
'access' => /* I18N: Listbox entry; name of a role */ I18N::translate('Member'),
'edit' => /* I18N: Listbox entry; name of a role */ I18N::translate('Editor'),
'accept' => /* I18N: Listbox entry; name of a role */ I18N::translate('Moderator'),
'admin' => /* I18N: Listbox entry; name of a role */ I18N::translate('Manager'),
);
// Form actions
switch (Filter::post('action')) {
case 'save':
if (Filter::checkCsrf()) {
$user_id = Filter::postInteger('user_id');
$user = User::find($user_id);
$username = Filter::post('username');
$real_name = Filter::post('real_name');
$email = Filter::postEmail('email');
$pass1 = Filter::post('pass1', WT_REGEX_PASSWORD);
$pass2 = Filter::post('pass2', WT_REGEX_PASSWORD);
$theme = Filter::post('theme', implode('|', array_keys(Theme::themeNames())), '');
$language = Filter::post('language');
$timezone = Filter::post('timezone');
$contact_method = Filter::post('contact_method');
$comment = Filter::post('comment');
$auto_accept = Filter::postBool('auto_accept');
$canadmin = Filter::postBool('canadmin');
$visible_online = Filter::postBool('visible_online');
$verified = Filter::postBool('verified');
$approved = Filter::postBool('approved');
if ($user_id === 0) {
// Create a new user
if (User::findByUserName($username)) {
FlashMessages::addMessage(I18N::translate('Duplicate username. A user with that username already exists. Please choose another username.'));
} elseif (User::findByEmail($email)) {
FlashMessages::addMessage(I18N::translate('Duplicate email address. A user with that email already exists.'));
} elseif ($pass1 !== $pass2) {
FlashMessages::addMessage(I18N::translate('The passwords do not match.'));
} else {
$user = User::create($username, $real_name, $email, $pass1);
$user->setPreference('reg_timestamp', date('U'))->setPreference('sessiontime', '0');
Log::addAuthenticationLog('User ->' . $username . '<- created');
}
} else {
$user = User::find($user_id);
if ($user && $username && $real_name) {
$user->setEmail($email);
$user->setUserName($username);
$user->setRealName($real_name);
if ($pass1 !== null && $pass1 === $pass2) {
$user->setPassword($pass1);
}
}
}
if ($user) {
// Approving for the first time? Send a confirmation email
if ($approved && !$user->getPreference('verified_by_admin') && $user->getPreference('sessiontime') == 0) {
I18N::init($user->getPreference('language'));
Mail::systemMessage(
$WT_TREE,
$user,
I18N::translate('Approval of account at %s', WT_BASE_URL),
I18N::translate('The administrator at the webtrees site %s has approved your application for an account. You may now sign in by accessing the following link: %s', WT_BASE_URL, WT_BASE_URL)
);
}
$user
->setPreference('theme', $theme)
->setPreference('language', $language)
->setPreference('TIMEZONE', $timezone)
->setPreference('contactmethod', $contact_method)
->setPreference('comment', $comment)
->setPreference('auto_accept', $auto_accept ? '1' : '0')
->setPreference('visibleonline', $visible_online ? '1' : '0')
->setPreference('verified', $verified ? '1' : '0')
->setPreference('verified_by_admin', $approved ? '1' : '0');
// We cannot change our own admin status. Another admin will need to do it.
if ($user->getUserId() !== Auth::id()) {
$user->setPreference('canadmin', $canadmin ? '1' : '0');
}
foreach (Tree::getAll() as $tree) {
$tree->setUserPreference($user, 'gedcomid', Filter::post('gedcomid' . $tree->getTreeId(), WT_REGEX_XREF));
$tree->setUserPreference($user, 'canedit', Filter::post('canedit' . $tree->getTreeId(), implode('|', array_keys($ALL_EDIT_OPTIONS))));
if (Filter::post('gedcomid' . $tree->getTreeId(), WT_REGEX_XREF)) {
$tree->setUserPreference($user, 'RELATIONSHIP_PATH_LENGTH', Filter::postInteger('RELATIONSHIP_PATH_LENGTH' . $tree->getTreeId(), 0, 10, 0));
} else {
// Do not allow a path length to be set if the individual ID is not
$tree->setUserPreference($user, 'RELATIONSHIP_PATH_LENGTH', null);
}
}
}
}
header('Location: ' . WT_BASE_URL . WT_SCRIPT_NAME);
return;
}
switch (Filter::get('action')) {
case 'load_json':
// Generate an AJAX/JSON response for datatables to load a block of rows
$search = Filter::postArray('search');
$search = $search['value'];
$start = Filter::postInteger('start');
$length = Filter::postInteger('length');
$order = Filter::postArray('order');
$sql_select =
"SELECT SQL_CACHE SQL_CALC_FOUND_ROWS '', u.user_id, user_name, real_name, email, us1.setting_value, us2.setting_value, NULL, us3.setting_value, NULL, us4.setting_value, us5.setting_value" .
" FROM `##user` u" .
" LEFT JOIN `##user_setting` us1 ON (u.user_id=us1.user_id AND us1.setting_name='language')" .
" LEFT JOIN `##user_setting` us2 ON (u.user_id=us2.user_id AND us2.setting_name='reg_timestamp')" .
" LEFT JOIN `##user_setting` us3 ON (u.user_id=us3.user_id AND us3.setting_name='sessiontime')" .
" LEFT JOIN `##user_setting` us4 ON (u.user_id=us4.user_id AND us4.setting_name='verified')" .
" LEFT JOIN `##user_setting` us5 ON (u.user_id=us5.user_id AND us5.setting_name='verified_by_admin')" .
" WHERE u.user_id > 0";
$args = array();
if ($search) {
$sql_select .= " AND (user_name LIKE CONCAT('%', :search_1, '%') OR real_name LIKE CONCAT('%', :search_2, '%') OR email LIKE CONCAT('%', :search_3, '%'))";
$args['search_1'] = $search;
$args['search_2'] = $search;
$args['search_3'] = $search;
}
if ($order) {
$sql_select .= " ORDER BY ";
foreach ($order as $key => $value) {
if ($key > 0) {
$sql_select .= ',';
}
// Datatables numbers columns 0, 1, 2
// MySQL numbers columns 1, 2, 3
switch ($value['dir']) {
case 'asc':
$sql_select .= (1 + $value['column']) . " ASC ";
break;
case 'desc':
$sql_select .= (1 + $value['column']) . " DESC ";
break;
}
}
} else {
$sql_select = " ORDER BY 1 ASC";
}
if ($length) {
Auth::user()->setPreference('admin_users_page_size', $length);
$sql_select .= " LIMIT :limit OFFSET :offset";
$args['limit'] = $length;
$args['offset'] = $start;
}
// This becomes a JSON list, not array, so need to fetch with numeric keys.
$data = Database::prepare($sql_select)->execute($args)->fetchAll(PDO::FETCH_NUM);
$installed_languages = array();
foreach (I18N::installedLocales() as $installed_locale) {
$installed_languages[$installed_locale->languageTag()] = $installed_locale->endonym();
}
// Reformat various columns for display
foreach ($data as &$datum) {
$user_id = $datum[1];
$user_name = $datum[2];
if ($user_id != Auth::id()) {
$admin_options = '<li><a href="#" onclick="return masquerade(' . $user_id . ')"><i class="fa fa-fw fa-user"></i> ' . /* I18N: Pretend to be another user, by logging in as them */
I18N::translate('Masquerade as this user') . '</a></li>' . '<li><a href="#" onclick="delete_user(\'' . I18N::translate('Are you sure you want to delete “%s”?', Filter::escapeJs($user_name)) . '\', \'' . Filter::escapeJs($user_id) . '\');"><i class="fa fa-fw fa-trash-o"></i> ' . I18N::translate('Delete') . '</a></li>';
} else {
// Do not delete ourself!
$admin_options = '';
}
$datum[0] = '<div class="btn-group"><button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><i class="fa fa-pencil"></i> <span class="caret"></span></button><ul class="dropdown-menu" role="menu"><li><a href="?action=edit&amp;user_id=' . $user_id . '"><i class="fa fa-fw fa-pencil"></i> ' . I18N::translate('Edit') . '</a></li><li class="divider"><li><a href="index_edit.php?user_id=' . $user_id . '"><i class="fa fa-fw fa-th-large"></i> ' . I18N::translate('Change the blocks on this users “My page”') . '</a></li>' . $admin_options . '</ul></div>';
// $datum[1] is the user ID
// $datum[3] is the real name
$datum[3] = '<span dir="auto">' . Filter::escapeHtml($datum[3]) . '</span>';
// $datum[4] is the email address
if ($user_id != Auth::id()) {
$datum[4] = '<a href="#" onclick="return message(\'' . Filter::escapeHtml($datum[2]) . '\', \'\', \'\');">' . Filter::escapeHtml($datum[4]) . '</i></a>';
}
// $datum[2] is the username
$datum[2] = '<span dir="auto">' . Filter::escapeHtml($datum[2]) . '</span>';
// $datum[5] is the langauge
if (array_key_exists($datum[5], $installed_languages)) {
$datum[5] = $installed_languages[$datum[5]];
}
// $datum[6] is the sortable registration timestamp
$datum[7] = $datum[6] ? FunctionsDate::formatTimestamp($datum[6] + WT_TIMESTAMP_OFFSET) : '';
if (date("U") - $datum[6] > 604800 && !$datum[10]) {
$datum[7] = '<span class="red">' . $datum[7] . '</span>';
}
// $datum[8] is the sortable last-login timestamp
if ($datum[8]) {
$datum[9] = FunctionsDate::formatTimestamp($datum[8] + WT_TIMESTAMP_OFFSET) . '<br>' . I18N::timeAgo(WT_TIMESTAMP - $datum[8]);
} else {
$datum[9] = I18N::translate('Never');
}
$datum[10] = $datum[10] ? I18N::translate('yes') : I18N::translate('no');
$datum[11] = $datum[11] ? I18N::translate('yes') : I18N::translate('no');
}
// Total filtered/unfiltered rows
$recordsFiltered = (int) Database::prepare("SELECT FOUND_ROWS()")->fetchOne();
$recordsTotal = User::count();
header('Content-type: application/json');
// See http://www.datatables.net/usage/server-side
echo json_encode(array(
'draw' => Filter::getInteger('draw'),
'recordsTotal' => $recordsTotal,
'recordsFiltered' => $recordsFiltered,
'data' => $data,
));
return;
case 'edit':
$user_id = Filter::getInteger('user_id');
if ($user_id === 0) {
$controller->setPageTitle(I18N::translate('Add a user'));
$tmp = new \stdClass;
$tmp->user_id = '';
$tmp->user_name = '';
$tmp->real_name = '';
$tmp->email = '';
$user = new User($tmp);
} else {
$controller->setPageTitle(I18N::translate('Edit the user'));
$user = User::find($user_id);
}
$controller
->pageHeader()
->addExternalJavascript(WT_AUTOCOMPLETE_JS_URL)
->addInlineJavascript('autocomplete();')
->addInlineJavascript('
jQuery(".relpath").change(function() {
var fieldIDx = jQuery(this).attr("id");
var idNum = fieldIDx.replace("RELATIONSHIP_PATH_LENGTH","");
var newIDx = "gedcomid"+idNum;
if (jQuery("#"+newIDx).val() === "" && jQuery("#".fieldIDx).val() !== "0") {
alert("' . I18N::translate('You must specify an individual record before you can restrict the user to their immediate family.') . '");
jQuery(this).val("0");
}
});
function regex_quote(str) {
return str.replace(/[\\\\.?+*()[\](){}|]/g, "\\\\$&");
}
');
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li><a href="admin_users.php"><?php echo I18N::translate('User administration'); ?></a></li>
<li class="active"><?php echo $controller->getPageTitle(); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<form class="form-horizontal" name="newform" method="post" role="form" action="?action=edit" autocomplete="off">
<?php echo Filter::getCsrf(); ?>
<input type="hidden" name="action" value="save">
<input type="hidden" name="user_id" value="<?php echo $user->getUserId(); ?>">
<!-- REAL NAME -->
<div class="form-group">
<label class="control-label col-sm-3" for="real_name">
<?php echo I18N::translate('Real name'); ?>
</label>
<div class="col-sm-9">
<input class="form-control" type="text" id="real_name" name="real_name" required maxlength="64" value="<?php echo Filter::escapeHtml($user->getRealName()); ?>" dir="auto">
<p class="small text-muted">
<?php echo I18N::translate('This is your real name, as you would like it displayed on screen.'); ?>
</p>
</div>
</div>
<!-- USER NAME -->
<div class="form-group">
<label class="control-label col-sm-3" for="username">
<?php echo I18N::translate('Username'); ?>
</label>
<div class="col-sm-9">
<input class="form-control" type="text" id="username" name="username" required maxlength="32" value="<?php echo Filter::escapeHtml($user->getUserName()); ?>" dir="auto">
<p class="small text-muted">
<?php echo I18N::translate('Usernames are case-insensitive and ignore accented letters, so that “chloe”, “chloë”, and “Chloe” are considered to be the same.'); ?>
</p>
</div>
</div>
<!-- PASSWORD -->
<div class="form-group">
<label class="control-label col-sm-3" for="pass1">
<?php echo I18N::translate('Password'); ?>
</label>
<div class="col-sm-9">
<input class="form-control" type="password" id="pass1" name="pass1" pattern = "<?php echo WT_REGEX_PASSWORD; ?>" placeholder="<?php echo I18N::plural('Use at least %s character.', 'Use at least %s characters.', WT_MINIMUM_PASSWORD_LENGTH, I18N::number(WT_MINIMUM_PASSWORD_LENGTH)); ?>" <?php echo $user->getUserId() ? '' : 'required'; ?> onchange="form.pass2.pattern = regex_quote(this.value);">
<p class="small text-muted">
<?php echo I18N::translate('Passwords must be at least 6 characters long and are case-sensitive, so that “secret” is different from “SECRET”.'); ?>
</p>
</div>
</div>
<!-- CONFIRM PASSWORD -->
<div class="form-group">
<label class="control-label col-sm-3" for="pass2">
<?php echo I18N::translate('Confirm password'); ?>
</label>
<div class="col-sm-9">
<input class="form-control" type="password" id="pass2" name="pass2" pattern = "<?php echo WT_REGEX_PASSWORD; ?>" placeholder="<?php echo I18N::translate('Type the password again.'); ?>" <?php echo $user->getUserId() ? '' : 'required'; ?>>
</div>
</div>
<!-- EMAIL ADDRESS -->
<div class="form-group">
<label class="control-label col-sm-3" for="email">
<?php echo I18N::translate('Email address'); ?>
</label>
<div class="col-sm-9">
<input class="form-control" type="email" id="email" name="email" required maxlength="64" value="<?php echo Filter::escapeHtml($user->getEmail()); ?>">
<p class="small text-muted">
<?php echo I18N::translate('This email address will be used to send password reminders, website notifications, and messages from other family members who are registered on the website.'); ?>
</p>
</div>
</div>
<!-- EMAIL VERIFIED -->
<!-- ACCOUNT APPROVED -->
<div class="form-group">
<label class="control-label col-sm-3" for="verified">
<?php echo I18N::translate('Account approval and email verification'); ?>
</label>
<div class="col-sm-9">
<div class="checkbox">
<label>
<input type="checkbox" name="verified" value="1" <?php echo $user->getPreference('verified') ? 'checked' : ''; ?>>
<?php echo I18N::translate('Email verified'); ?>
</label>
<label>
<input type="checkbox" name="approved" value="1" <?php echo $user->getPreference('verified_by_admin') ? 'checked' : ''; ?>>
<?php echo I18N::translate('Approved by administrator'); ?>
</label>
<p class="small text-muted">
<?php echo I18N::translate('When a user registers for an account, an email is sent to their email address with a verification link. When they follow this link, we know the email address is correct, and the “email verified” option is selected automatically.'); ?>
</p>
<p class="small text-muted">
<?php echo I18N::translate('If an administrator creates a user account, the verification email is not sent, and the email must be verified manually.'); ?>
</p>
<p class="small text-muted">
<?php echo I18N::translate('You should not approve an account unless you know that the email address is correct.'); ?>
</p>
<p class="small text-muted">
<?php echo I18N::translate('A user will not be able to sign in until both “email verified” and “approved by administrator” are selected.'); ?>
</p>
</div>
</div>
</div>
<!-- LANGUAGE -->
<div class="form-group">
<label class="control-label col-sm-3" for="language">
<?php echo /* I18N: A configuration setting */ I18N::translate('Language'); ?>
</label>
<div class="col-sm-9">
<select id="language" name="language" class="form-control">
<?php foreach (I18N::installedLocales() as $installed_locale): ?>
<option value="<?php echo $installed_locale->languageTag(); ?>" <?php echo $user->getPreference('language', WT_LOCALE) === $installed_locale->languageTag() ? 'selected' : ''; ?>>
<?php echo $installed_locale->endonym(); ?>
</option>
<?php endforeach; ?>
</select>
</div>
</div>
<!-- TIMEZONE -->
<div class="form-group">
<label class="control-label col-sm-3" for="timezone">
<?php echo /* I18N: A configuration setting */ I18N::translate('Time zone'); ?>
</label>
<div class="col-sm-9">
<?php echo FunctionsEdit::selectEditControl('timezone', array_combine(\DateTimeZone::listIdentifiers(), \DateTimeZone::listIdentifiers()), null, $user->getPreference('TIMEZONE') ?: 'UTC', 'class="form-control"'); ?>
<p class="small text-muted">
<?php echo I18N::translate('The time zone is required for date calculations, such as knowing todays date.'); ?>
</p>
</div>
</div>
<!-- AUTO ACCEPT -->
<div class="form-group">
<label class="control-label col-sm-3" for="auto_accept">
<?php echo I18N::translate('Changes'); ?>
</label>
<div class="col-sm-9">
<div class="checkbox">
<label>
<input type="checkbox" name="auto_accept" value="1" <?php echo $user->getPreference('auto_accept') ? 'checked' : ''; ?>>
<?php echo I18N::translate('Automatically accept changes made by this user'); ?>
</label>
<p class="small text-muted">
<?php echo I18N::translate('Normally, any changes made to a family tree need to be reviewed by a moderator. This option allows a user to make changes without needing a moderator.'); ?>
</p>
</div>
</div>
</div>
<!-- VISIBLE ONLINE -->
<div class="form-group">
<label class="control-label col-sm-3" for="visible_online">
<?php echo /* I18N: A configuration setting */ I18N::translate('Visible online'); ?>
</label>
<div class="col-sm-9">
<div class="checkbox">
<label>
<input type="checkbox" id="visible_online" name="visible_online" value="1" <?php echo $user->getPreference('visibleonline') ? 'checked' : ''; ?>>
<?php echo /* I18N: A configuration setting */ I18N::translate('Visible to other users when online'); ?>
</label>
<p class="small text-muted">
<?php echo I18N::translate('You can choose whether to appear in the list of users who are currently signed-in.'); ?>
</p>
</div>
</div>
</div>
<!-- CONTACT METHOD -->
<div class="form-group">
<label class="control-label col-sm-3" for="contactmethod">
<?php echo /* I18N: A configuration setting */ I18N::translate('Preferred contact method'); ?>
</label>
<div class="col-sm-9">
<?php echo FunctionsEdit::editFieldContact('contact_method', $user->getPreference('contactmethod')); ?>
<p class="small text-muted">
<?php echo /* I18N: Help text for the “Preferred contact method” configuration setting */
I18N::translate('Site members can send each other messages. You can choose to how these messages are sent to you, or choose not receive them at all.'); ?>
</p>
</div>
</div>
<!-- THEME -->
<div class="form-group">
<label class="control-label col-sm-3" for="theme">
<?php echo I18N::translate('Theme'); ?>
</label>
<div class="col-sm-9">
<?php echo FunctionsEdit::selectEditControl('theme', Theme::themeNames(), I18N::translate('<default theme>'), $user->getPreference('theme'), 'class="form-control"'); ?>
</div>
</div>
<!-- COMMENTS -->
<div class="form-group">
<label class="control-label col-sm-3" for="comment">
<?php echo I18N::translate('Administrator comments on user'); ?>
</label>
<div class="col-sm-9">
<textarea class="form-control" id="comment" name="comment" rows="5" maxlength="255"><?php echo Filter::escapeHtml($user->getPreference('comment')); ?></textarea>
</div>
</div>
<!-- ADMINISTRATOR -->
<div class="form-group">
<label class="control-label col-sm-3" for="admin">
</label>
<div class="col-sm-9">
<div class="checkbox">
<label>
<input
type="checkbox" id="admin" name="canadmin" value="1"
<?php echo $user->getPreference('canadmin') ? 'checked' : ''; ?>
<?php echo $user->getUserId() === Auth::id() ? 'disabled' : ''; ?>
>
<?php echo I18N::translate('Administrator'); ?>
</label>
</div>
</div>
</div>
<h3><?php echo I18N::translate('Access to family trees'); ?></h3>
<p>
<?php echo I18N::translate('A role is a set of access rights, which give permission to view data, change preferences, etc. Access rights are assigned to roles, and roles are granted to users. Each family tree can assign different access to each role, and users can have a different role in each family tree.'); ?>
</p>
<div class="row">
<div class="col-xs-4">
<h4>
<?php echo I18N::translate('Visitor'); ?>
</h4>
<p class="small text-muted">
<?php echo I18N::translate('Everybody has this role, including visitors to the website and search engines.'); ?>
</p>
<h4>
<?php echo I18N::translate('Member'); ?>
</h4>
<p class="small text-muted">
<?php echo I18N::translate('This role has all the permissions of the visitor role, plus any additional access granted by the family tree configuration.'); ?>
</p>
</div>
<div class="col-xs-4">
<h4>
<?php echo I18N::translate('Editor'); ?>
</h4>
<p class="small text-muted">
<?php echo I18N::translate('This role has all the permissions of the member role, plus permission to add/change/delete data. Any changes will need to be reviewed by a moderator, unless the user has the “automatically accept changes” option enabled.'); ?>
</p>
<h4>
<?php echo I18N::translate('Moderator'); ?>
</h4>
<p class="small text-muted">
<?php echo I18N::translate('This role has all the permissions of the editor role, plus permission to accept/reject changes made by other users.'); ?>
</p>
</div>
<div class="col-xs-4">
<h4>
<?php echo I18N::translate('Manager'); ?>
</h4>
<p class="small text-muted">
<?php echo I18N::translate('This role has all the permissions of the moderator role, plus any additional access granted by the family tree configuration, plus permission to change the settings/configuration of a family tree.'); ?>
</p>
<h4>
<?php echo I18N::translate('Administrator'); ?>
</h4>
<p class="small text-muted">
<?php echo I18N::translate('This role has all the permissions of the manager role in all family trees, plus permission to change the settings/configuration of the website, users, and modules.'); ?>
</p>
</div>
</div>
<table class="table table-bordered table-condensed table-responsive">
<thead>
<tr>
<th>
<?php echo I18N::translate('Family tree'); ?>
</th>
<th>
<?php echo I18N::translate('Role'); ?>
</th>
<th>
<?php echo I18N::translate('Individual record'); ?>
</th>
<th>
<?php echo I18N::translate('Restrict to immediate family'); ?>
</th>
</tr>
<tr>
<td>
</td>
<td>
</td>
<td>
<p class="small text-muted">
<?php echo I18N::translate('Link this user to an individual in the family tree.'); ?>
</p>
</td>
<td>
<p class="small text-muted">
<?php echo I18N::translate('Where a user is associated to an individual record in a family tree and has a role of member, editor, or moderator, you can prevent them from accessing the details of distant, living relations. You specify the number of relationship steps that the user is allowed to see.'); ?>
<?php echo I18N::translate('For example, if you specify a path length of 2, the individual will be able to see their grandson (child, child), their aunt (parent, sibling), their step-daughter (spouse, child), but not their first cousin (parent, sibling, child).'); ?>
<?php echo I18N::translate('Note: longer path lengths require a lot of calculation, which can make your website run slowly for these users.'); ?>
</p>
</td>
</tr>
</thead>
<tbody>
<?php foreach (Tree::getAll() as $tree): ?>
<tr>
<td>
<?php echo $tree->getTitleHtml(); ?>
</td>
<td>
<select name="canedit<?php echo $tree->getTreeId(); ?>">
<?php foreach ($ALL_EDIT_OPTIONS as $EDIT_OPTION => $desc): ?>
<option value="<?php echo $EDIT_OPTION; ?>"
<?php echo $EDIT_OPTION === $tree->getUserPreference($user, 'canedit') ? 'selected' : ''; ?>
>
<?php echo $desc; ?>
</option>
<?php endforeach; ?>
</select>
</td>
<td>
<input
data-autocomplete-type="INDI"
data-autocomplete-ged="<?php echo Filter::escapeHtml($tree->getName()); ?>"
type="text"
size="12"
name="gedcomid<?php echo $tree->getTreeId(); ?>"
id="gedcomid<?php echo $tree->getTreeId(); ?>"
value="<?php echo Filter::escapeHtml($tree->getUserPreference($user, 'gedcomid')); ?>"
>
<?php echo FunctionsPrint::printFindIndividualLink('gedcomid' . $tree->getTreeId(), '', $tree); ?>
</td>
<td>
<select name="RELATIONSHIP_PATH_LENGTH<?php echo $tree->getTreeId(); ?>" id="RELATIONSHIP_PATH_LENGTH<?php echo $tree->getTreeId(); ?>" class="relpath">
<?php for ($n = 0; $n <= 10; ++$n): ?>
<option value="<?php echo $n; ?>" <?php echo $tree->getUserPreference($user, 'RELATIONSHIP_PATH_LENGTH') == $n ? 'selected' : ''; ?>>
<?php echo $n ? $n : I18N::translate('No'); ?>
</option>
<?php endfor; ?>
</select>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9">
<button type="submit" class="btn btn-primary">
<?php echo I18N::translate('save'); ?>
</button>
</div>
</div>
</form>
<?php
return;
case 'cleanup':
$controller
->setPageTitle(I18N::translate('Delete inactive users'))
->pageHeader();
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li><a href="admin_users.php"><?php echo I18N::translate('User administration'); ?></a></li>
<li class="active"><?php echo $controller->getPageTitle(); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<form method="post" action="?action=cleanup2">
<table class="table table-bordered">
<?php
// Check for idle users
$month = Filter::getInteger('month', 1, 12, 6);
echo '<tr><th colspan="2">', I18N::translate('Number of months since the last sign-in for a users account to be considered inactive: '), '</th>';
echo '<td><select onchange="document.location=options[selectedIndex].value;">';
for ($i = 1; $i <= 12; $i++) {
echo '<option value="admin_users.php?action=cleanup&amp;month=' . $i . '" ';
if ($i === $month) {
echo 'selected';
}
echo '>', $i, '</option>';
}
echo '</select></td></tr>';
// Check users not logged in too long
$ucnt = 0;
foreach (User::all() as $user) {
if ($user->getPreference('sessiontime') === '0') {
$datelogin = (int) $user->getPreference('reg_timestamp');
} else {
$datelogin = (int) $user->getPreference('sessiontime');
}
if (mktime(0, 0, 0, (int) date('m') - $month, (int) date('d'), (int) date('Y')) > $datelogin && $user->getPreference('verified') && $user->getPreference('verified_by_admin')) {
$ucnt++;
?>
<tr>
<td>
<a href="?action=edit&amp;user_id=<?php echo $user->getUserId(); ?>">
<?php echo Filter::escapeHtml($user->getUserName()); ?>
<?php echo $user->getRealNameHtml(); ?>
</a>
</td>
<td>
<?php echo I18N::translate('Users account has been inactive too long: ') . FunctionsDate::timestampToGedcomDate($datelogin)->display(); ?>
</td>
<td>
<input type="checkbox" name="del_<?php echo $user->getUserId(); ?>" value="1">
</td>
</tr>
<?php
}
}
// Check unverified users
foreach (User::all() as $user) {
if (((date('U') - (int) $user->getPreference('reg_timestamp')) > 604800) && !$user->getPreference('verified')) {
$ucnt++;
?>
<tr>
<td>
<a href="?action=edit&amp;user_id=<?php echo $user->getUserId(); ?>">
<?php echo Filter::escapeHtml($user->getUserName()); ?>
<?php echo $user->getRealNameHtml(); ?>
</a>
</td>
<td>
<?php echo I18N::translate('User didnt verify within 7 days.'); ?>
</td>
<td>
<input type="checkbox" checked name="del_<?php echo $user->getUserId(); ?>" value="1">
</td>
</tr>
<?php
}
}
// Check users not verified by admin
foreach (User::all() as $user) {
if ($user->getUserId() !== Auth::id() && !$user->getPreference('verified_by_admin') && $user->getPreference('verified')) {
$ucnt++;
?>
<tr>
<td>
<a href="?action=edit&amp;user_id=<?php echo $user->getUserId(); ?>">
<?php echo Filter::escapeHtml($user->getUserName()); ?>
<?php echo $user->getRealNameHtml(); ?>
</a>
</td>
<td>
<?php echo I18N::translate('User not verified by administrator.'); ?>
</td>
<td>
<input type="checkbox" name="del_<?php echo $user->getUserId(); ?>" value="1">
</td>
</tr>
<?php
}
}
?>
</table>
<p>
<?php if ($ucnt): ?>
<input type="submit" value="<?php echo I18N::translate('delete'); ?>">
<?php else: ?>
<?php echo I18N::translate('Nothing found to cleanup'); ?>
<?php endif; ?>
</p>
</form>
<?php
break;
case 'cleanup2':
foreach (User::all() as $user) {
if (Filter::post('del_' . $user->getUserId()) == '1') {
Log::addAuthenticationLog('Deleted user: ' . $user->getUserName());
$user->delete();
I18N::translate('The user %s has been deleted.', Filter::escapeHtml($user->getUserName()));
}
}
header('Location: ' . WT_BASE_URL . WT_SCRIPT_NAME);
break;
default:
$controller
->setPageTitle(I18N::translate('User administration'))
->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
->addExternalJavascript(WT_DATATABLES_BOOTSTRAP_JS_URL)
->addInlineJavascript('
jQuery(".table-user-list").dataTable({
' . I18N::datatablesI18N() . ',
stateSave: true,
stateDuration: 300,
processing: true,
serverSide: true,
ajax: {
"url": "' . WT_SCRIPT_NAME . '?action=load_json",
"type": "POST"
},
search: {
search: "' . Filter::escapeJs(Filter::get('filter')) . '"
},
autoWidth: false,
pageLength: ' . Auth::user()->getPreference('admin_users_page_size', 10) . ',
sorting: [[2, "asc"]],
columns: [
/* details */ { sortable: false },
/* user-id */ { visible: false },
/* user_name */ null,
/* real_name */ null,
/* email */ null,
/* language */ null,
/* registered (sort) */ { visible: false },
/* registered */ { dataSort: 7 },
/* last_login (sort) */ { visible: false },
/* last_login */ { dataSort: 9 },
/* verified */ null,
/* approved */ null
]
})
.fnFilter("' . Filter::get('filter') . '"); // View the details of a newly created user
')
->pageHeader();
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li class="active"><?php echo $controller->getPageTitle(); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<table class="table table-condensed table-bordered table-user-list">
<thead>
<tr>
<th><?php echo I18N::translate('Edit'); ?></th>
<th><!-- user id --></th>
<th><?php echo I18N::translate('Username'); ?></th>
<th><?php echo I18N::translate('Real name'); ?></th>
<th><?php echo I18N::translate('Email address'); ?></th>
<th><?php echo I18N::translate('Language'); ?></th>
<th><!-- date registered --></th>
<th><?php echo I18N::translate('Date registered'); ?></th>
<th><!-- last login --></th>
<th><?php echo I18N::translate('Last signed in'); ?></th>
<th><?php echo I18N::translate('Verified'); ?></th>
<th><?php echo I18N::translate('Approved'); ?></th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<?php
break;
}

View file

@ -1,53 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
use Fisharebest\Webtrees\Controller\PageController;
define('WT_SCRIPT_NAME', 'admin_users_bulk.php');
require './includes/session.php';
$controller = new PageController;
$controller
->restrictAccess(Auth::isAdmin())
->setPageTitle(I18N::translate('Send broadcast messages'))
->pageHeader();
?>
<ol class="breadcrumb small">
<li><a href="admin.php"><?php echo I18N::translate('Control panel'); ?></a></li>
<li><a href="admin_users.php"><?php echo I18N::translate('User administration'); ?></a></li>
<li class="active"><?php echo $controller->getPageTitle(); ?></li>
</ol>
<h1><?php echo $controller->getPageTitle(); ?></h1>
<p>
<a href="#" onclick="return message('all', 'messaging2', '');">
<?php echo I18N::translate('Send a message to all users'); ?>
</a>
</p>
<p>
<a href="#" onclick="return message('never_logged', 'messaging2', '');">
<?php echo I18N::translate('Send a message to users who have never signed in'); ?>
</a>
</p>
<p>
<a href="#" onclick="return message('last_6mo', 'messaging2', '');">
<?php echo I18N::translate('Send a message to users who have not signed in for 6 months'); ?>
</a>
</p>

View file

@ -1,174 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
/**
* Defined in session.php
*
* @global Tree $WT_TREE
*/
global $WT_TREE;
use Fisharebest\Webtrees\Controller\AncestryController;
use Fisharebest\Webtrees\Functions\FunctionsCharts;
use Fisharebest\Webtrees\Functions\FunctionsEdit;
use Fisharebest\Webtrees\Functions\FunctionsPrint;
use Fisharebest\Webtrees\Functions\FunctionsPrintLists;
define('WT_SCRIPT_NAME', 'ancestry.php');
require './includes/session.php';
$MAX_PEDIGREE_GENERATIONS = $WT_TREE->getPreference('MAX_PEDIGREE_GENERATIONS');
$controller = new AncestryController;
$controller
->restrictAccess(Module::isActiveChart($WT_TREE, 'ancestors_chart'))
->pageHeader()
->addExternalJavascript(WT_AUTOCOMPLETE_JS_URL)
->addInlineJavascript('autocomplete();');
?>
<div id="ancestry-page">
<h2><?php echo $controller->getPageTitle(); ?></h2>
<form name="people" id="people" method="get" action="?">
<input type="hidden" name="ged" value="<?php echo $WT_TREE->getNameHtml(); ?>">
<table class="list_table">
<tbody>
<tr>
<td class="descriptionbox">
<label for="rootid"><?php echo I18N::translate('Individual'); ?></label>
</td>
<td class="optionbox">
<input class="pedigree_form" data-autocomplete-type="INDI" type="text" name="rootid" id="rootid" size="3" value="<?php echo $controller->root->getXref(); ?>">
<?php echo FunctionsPrint::printFindIndividualLink('rootid'); ?>
</td>
<td rowspan="3" class="descriptionbox">
<label><?php echo I18N::translate('Layout'); ?></label>
</td>
<td rowspan="3" class="optionbox">
<div>
<label>
<input type="radio" name="chart_style" value="0" onclick="statusDisable('cousins');" <?php echo $controller->chart_style == 0 ? 'checked' : ''; ?>>
<?php echo I18N::translate('List'); ?>
</label>
</div>
<div>
<label>
<input type="radio" name="chart_style" value="1" onclick="statusEnable('cousins');" <?php echo $controller->chart_style == 1 ? 'checked' : ''; ?>>
<?php echo I18N::translate('Booklet'); ?>
</label>
<label>
<?php echo FunctionsEdit::twoStateCheckbox('show_cousins', $controller->show_cousins, "id='cousins' " . ($controller->chart_style === 1 ? '' : 'disabled')); ?>
<?php echo I18N::translate('Show cousins'); ?>
</label>
</div>
<div>
<label>
<input type="radio" name="chart_style" value="2" onclick="statusDisable('cousins');" <?php echo $controller->chart_style == 2 ? 'checked' : ''; ?>>
<?php echo I18N::translate('Individuals'); ?>
</label>
</div>
<div>
<label>
<input type="radio" name="chart_style" value="3" onclick="statusDisable('cousins');" <?php echo $controller->chart_style == 3 ? 'checked' : ''; ?>>
<?php echo I18N::translate('Families'); ?>
</label>
</div>
</td>
<td rowspan="3" class="facts_label03">
<input type="submit" value="<?php echo /* I18N: A button label. */ I18N::translate('view'); ?>">
</td>
</tr>
<tr>
<td class="descriptionbox">
<?php echo '<label>', I18N::translate('Generations'), '</label>'; ?>
</td>
<td class="optionbox">
<select name="PEDIGREE_GENERATIONS">
<?php
for ($i = 2; $i <= $MAX_PEDIGREE_GENERATIONS; $i++) {
echo '<option value="', $i, '" ';
if ($i == $controller->generations) {
echo 'selected';
}
echo '>', I18N::number($i), '</option>';
}
?>
</select>
</td>
</tr>
<tr>
<td class="descriptionbox">
<?php echo '<label>', I18N::translate('Show details'), '</label>'; ?>
</td>
<td class="optionbox">
<?php echo FunctionsEdit::twoStateCheckbox('show_full', $controller->showFull()); ?>
</td>
</tr>
</tbody>
</table>
</form>
<?php
if ($controller->error_message) {
echo '<p class="ui-state-error">', $controller->error_message, '</p>';
return;
}
switch ($controller->chart_style) {
case 0:
// List
echo '<ul id="ancestry_chart" class="chart_common">';
$controller->printChildAscendancy($controller->root, 1, $controller->generations - 1);
echo '</ul>';
echo '<br>';
break;
case 1:
echo '<div id="ancestry_booklet">';
// Booklet
// first page : show indi facts
FunctionsPrint::printPedigreePerson($controller->root, $controller->showFull());
// process the tree
$ancestors = $controller->sosaAncestors($controller->generations - 1);
$ancestors = array_filter($ancestors); // The SOSA array includes empty placeholders
foreach ($ancestors as $sosa => $individual) {
foreach ($individual->getChildFamilies() as $family) {
FunctionsCharts::printSosaFamily($family->getXref(), $individual->getXref(), $sosa, '', '', '', $controller->show_cousins, $controller->showFull());
}
}
echo '</div>';
break;
case 2:
// Individual list
$ancestors = $controller->sosaAncestors($controller->generations);
$ancestors = array_filter($ancestors); // The SOSA array includes empty placeholders
echo '<div id="ancestry-list">', FunctionsPrintLists::individualTable($ancestors, 'sosa'), '</div>';
break;
case 3:
// Family list
$ancestors = $controller->sosaAncestors($controller->generations - 1);
$ancestors = array_filter($ancestors); // The SOSA array includes empty placeholders
$families = array();
foreach ($ancestors as $individual) {
foreach ($individual->getChildFamilies() as $family) {
$families[$family->getXref()] = $family;
}
}
echo '<div id="ancestry-list">', FunctionsPrintLists::familyTable($families), '</div>';
break;
}
echo '</div>';

View file

@ -1,194 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees;
/**
* Authentication.
*/
class Auth {
// Privacy constants
const PRIV_PRIVATE = 2; // Allows visitors to view the item
const PRIV_USER = 1; // Allows members to access the item
const PRIV_NONE = 0; // Allows managers to access the item
const PRIV_HIDE = -1; // Hide the item to all users
/**
* Are we currently logged in?
*
* @return bool
*/
public static function check() {
return self::id() !== null;
}
/**
* Is the specified/current user an administrator?
*
* @param User|null $user
*
* @return bool
*/
public static function isAdmin(User $user = null) {
if ($user === null) {
$user = self::user();
}
return $user && $user->getPreference('canadmin') === '1';
}
/**
* Is the specified/current user a manager of a tree?
*
* @param Tree $tree
* @param User|null $user
*
* @return bool
*/
public static function isManager(Tree $tree, User $user = null) {
if ($user === null) {
$user = self::user();
}
return self::isAdmin($user) || $user && $tree->getUserPreference($user, 'canedit') === 'admin';
}
/**
* Is the specified/current user a moderator of a tree?
*
* @param Tree $tree
* @param User|null $user
*
* @return bool
*/
public static function isModerator(Tree $tree, User $user = null) {
if ($user === null) {
$user = self::user();
}
return self::isManager($tree, $user) || $user && $tree->getUserPreference($user, 'canedit') === 'accept';
}
/**
* Is the specified/current user an editor of a tree?
*
* @param Tree $tree
* @param User|null $user
*
* @return bool
*/
public static function isEditor(Tree $tree, User $user = null) {
if ($user === null) {
$user = self::user();
}
return self::isModerator($tree, $user) || $user && $tree->getUserPreference($user, 'canedit') === 'edit';
}
/**
* Is the specified/current user a member of a tree?
*
* @param Tree $tree
* @param User|null $user
*
* @return bool
*/
public static function isMember(Tree $tree, User $user = null) {
if ($user === null) {
$user = self::user();
}
return self::isEditor($tree, $user) || $user && $tree->getUserPreference($user, 'canedit') === 'access';
}
/**
* What is the specified/current user's access level within a tree?
*
* @param Tree $tree
* @param User|null $user
*
* @return int
*/
public static function accessLevel(Tree $tree, User $user = null) {
if ($user === null) {
$user = self::user();
}
if (self::isManager($tree, $user)) {
return self::PRIV_NONE;
} elseif (self::isMember($tree, $user)) {
return self::PRIV_USER;
} else {
return self::PRIV_PRIVATE;
}
}
/**
* Is the current visitor a search engine? The global is set in session.php
*
* @return bool
*/
public static function isSearchEngine() {
global $SEARCH_SPIDER;
return $SEARCH_SPIDER;
}
/**
* The ID of the authenticated user, from the current session.
*
* @return string|null
*/
public static function id() {
return Session::get('wt_user');
}
/**
* The authenticated user, from the current session.
*
* @return User
*/
public static function user() {
$user = User::find(self::id());
if ($user === null) {
$visitor = new \stdClass;
$visitor->user_id = '';
$visitor->user_name = '';
$visitor->real_name = '';
$visitor->email = '';
return new User($visitor);
} else {
return $user;
}
}
/**
* Login directly as an explicit user - for masquerading.
*
* @param User $user
*/
public static function login(User $user) {
Session::regenerate(false);
Session::put('wt_user', $user->getUserId());
}
/**
* End the session for the current user.
*/
public static function logout() {
Session::regenerate(true);
}
}

View file

@ -1,172 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Date;
use Fisharebest\Webtrees\Family;
use Fisharebest\Webtrees\Individual;
/**
* Definitions for a census column
*/
class AbstractCensusColumn {
/** @var CensusInterface */
private $census;
/** @var string */
private $abbr;
/** @var string */
private $title;
/**
* Create a column for a census
*
* @param CensusInterface $census - The census to which this column forms part.
* @param string $abbr - The abbrievated on-screen name "BiC"
* @param string $title - The full column heading "Born in the county"
*/
public function __construct(CensusInterface $census, $abbr, $title) {
$this->census = $census;
$this->abbr = $abbr;
$this->title = $title;
}
/**
* A short version of the column's name.
*
* @return string
*/
public function abbreviation() {
return $this->abbr;
}
/**
* Extract the country (last part) of a place name.
*
* @param string $place - e.g. "London, England"
*
* @return string - e.g. "England"
*/
protected function lastPartOfPlace($place) {
$place = explode(', ', $place);
return end($place);
}
/**
* When did this census occur
*
* @return Date
*/
public function date() {
return new Date($this->census->censusDate());
}
/**
* Find the father of an individual
*
* @param Individual $individual
*
* @return Individual|null
*/
public function father(Individual $individual) {
$family = $individual->getPrimaryChildFamily();
if ($family) {
return $family->getHusband();
} else {
return null;
}
}
/**
* Find the mother of an individual
*
* @param Individual $individual
*
* @return Individual|null
*/
public function mother(Individual $individual) {
$family = $individual->getPrimaryChildFamily();
if ($family) {
return $family->getWife();
} else {
return null;
}
}
/**
* Remove the country of a place name, where it is the same as the census place
*
* @param string $place - e.g. "London, England"
*
* @return string - e.g. "London" (for census of England) and "London, England" elsewhere
*/
protected function notCountry($place) {
$parts = explode(', ', $place);
if (end($parts) === $this->place()) {
return implode(', ', array_slice($parts, 0, -1));
} else {
return $place;
}
}
/**
* Where did this census occur
*
* @return string
*/
public function place() {
return $this->census->censusPlace();
}
/**
* Find the current spouse family of an individual
*
* @param Individual $individual
*
* @return Family|null
*/
public function spouseFamily(Individual $individual) {
// Exclude families that were created after this census date
$families = array();
foreach ($individual->getSpouseFamilies() as $family) {
if (Date::compare($family->getMarriageDate(), $this->date()) <= 0) {
$families[] = $family;
}
}
if (empty($families)) {
return null;
} else {
usort($families, function (Family $x, Family $y) { return Date::compare($x->getMarriageDate(), $y->getMarriageDate()); });
return end($families);
}
}
/**
* The full version of the column's name.
*
* @return string
*/
public function title() {
return $this->title;
}
}

View file

@ -1,186 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Date;
use Fisharebest\Webtrees\Individual;
/**
* Marital status.
*/
abstract class AbstractCensusColumnCondition extends AbstractCensusColumn implements CensusColumnInterface {
/* Text to display for married males */
protected $husband = '';
/* Text to display for married females */
protected $wife = '';
/* Text to display for unmarried males */
protected $bachelor = '';
/* Text to display for unmarried females */
protected $spinster = '';
/* Text to display for male children */
protected $boy = '';
/* Text to display for female children */
protected $girl = '';
/* Text to display for divorced males */
protected $divorce = '';
/* Text to display for divorced females */
protected $divorcee = '';
/* Text to display for widowed males */
protected $widower = '';
/* Text to display for widowed females */
protected $widow = '';
/* At what age is this individual recorded as an adult */
protected $age_adult = 15;
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
$family = $this->spouseFamily($individual);
$sex = $individual->getSex();
if ($family === null || count($family->getFacts('_NMR')) > 0) {
if ($this->isChild($individual)) {
return $this->conditionChild($sex);
} else {
return $this->conditionSingle($sex);
}
} elseif (count($family->getFacts('DIV')) > 0) {
return $this->conditionDivorced($sex);
} else {
$spouse = $family->getSpouse($individual);
if ($spouse instanceof Individual && $this->isDead($spouse)) {
return $this->conditionWidowed($sex);
} else {
return $this->conditionMarried($sex);
}
}
}
/**
* How is this condition written in a census column.
*
* @param string $sex
*
* @return string
*/
private function conditionChild($sex) {
if ($sex === 'F') {
return $this->girl;
} else {
return $this->boy;
}
}
/**
* How is this condition written in a census column.
*
* @param string $sex
*
* @return string
*/
private function conditionDivorced($sex) {
if ($sex === 'F') {
return $this->divorcee;
} else {
return $this->divorce;
}
}
/**
* How is this condition written in a census column.
*
* @param string $sex
*
* @return string
*/
private function conditionMarried($sex) {
if ($sex === 'F') {
return $this->wife;
} else {
return $this->husband;
}
}
/**
* How is this condition written in a census column.
*
* @param string $sex
*
* @return string
*/
private function conditionSingle($sex) {
if ($sex === 'F') {
return $this->spinster;
} else {
return $this->bachelor;
}
}
/**
* How is this condition written in a census column.
*
* @param string $sex
*
* @return string
*/
private function conditionWidowed($sex) {
if ($sex === 'F') {
return $this->widow;
} else {
return $this->widower;
}
}
/**
* Is the individual a child.
*
* @param Individual $individual
*
* @return bool
*/
private function isChild(Individual $individual) {
$age = (int) Date::getAge($individual->getEstimatedBirthDate(), $this->date(), 0);
return $age < $this->age_adult;
}
/**
* Is the individual dead.
*
* @param Individual $individual
*
* @return bool
*/
private function isDead(Individual $individual) {
return $individual->getDeathDate()->isOK() && Date::compare($individual->getDeathDate(), $this->date()) < 0;
}
}

View file

@ -1,37 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
/**
* Definitions for a census
*/
class Census {
/**
* @return CensusPlaceInterface[]
*/
public static function allCensusPlaces() {
return array(
new CensusOfCzechRepublic,
new CensusOfDenmark,
new CensusOfDeutschland,
new CensusOfEngland,
new CensusOfFrance,
new CensusOfScotland,
new CensusOfUnitedStates,
new CensusOfWales,
);
}
}

View file

@ -1,36 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Date;
use Fisharebest\Webtrees\Individual;
/**
* The individual's age.
*/
class CensusColumnAge extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
return (string) Date::getAge($individual->getEstimatedBirthDate(), $this->date(), 0);
}
}

View file

@ -1,40 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Date;
use Fisharebest\Webtrees\Individual;
/**
* The age of a female individual.
*/
class CensusColumnAgeFemale extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
if ($individual->getSex() === 'M') {
return '';
} else {
return (string) Date::getAge($individual->getEstimatedBirthDate(), $this->date(), 0);
}
}
}

View file

@ -1,45 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Date;
use Fisharebest\Webtrees\Individual;
/**
* The age of a female individual (rounded down to the nearest 5 years).
*/
class CensusColumnAgeFemale5Years extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
if ($individual->getSex() === 'M') {
return '';
} else {
$years = Date::getAge($individual->getEstimatedBirthDate(), $this->date(), 0);
if ($years > 15) {
$years -= $years % 5;
}
return (string) $years;
}
}
}

View file

@ -1,40 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Date;
use Fisharebest\Webtrees\Individual;
/**
* The age of a male individual.
*/
class CensusColumnAgeMale extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
if ($individual->getSex() === 'F') {
return '';
} else {
return (string) Date::getAge($individual->getEstimatedBirthDate(), $this->date(), 0);
}
}
}

View file

@ -1,45 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Date;
use Fisharebest\Webtrees\Individual;
/**
* The age of a male individual (rounded down to the nearest 5 years).
*/
class CensusColumnAgeMale5Years extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
if ($individual->getSex() === 'F') {
return '';
} else {
$years = Date::getAge($individual->getEstimatedBirthDate(), $this->date(), 0);
if ($years > 15) {
$years -= $years % 5;
}
return (string) $years;
}
}
}

View file

@ -1,46 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Date;
use Fisharebest\Webtrees\Individual;
/**
* At what age did the individual marry.
*/
class CensusColumnAgeMarried extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
if ($individual->getBirthDate()->isOK()) {
foreach ($individual->getSpouseFamilies() as $family) {
foreach ($family->getFacts('MARR', true) as $fact) {
if ($fact->getDate()->isOK()) {
return Date::getAge($individual->getBirthDate(), $fact->getDate(), 0);
}
}
}
}
return '';
}
}

View file

@ -1,35 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* The individual's date of birth.
*/
class CensusColumnBirthDate extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
return $individual->getEstimatedBirthDate()->minimumDate()->format('%j %n %Y');
}
}

View file

@ -1,35 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* The individual's date of birth.
*/
class CensusColumnBirthDay extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
return $individual->getEstimatedBirthDate()->minimumDate()->format('%j');
}
}

View file

@ -1,41 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* The individual's date of birth.
*/
class CensusColumnBirthDayDotMonthYear extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
$birth_date = $individual->getBirthDate();
if ($birth_date->minimumJulianDay() === $birth_date->maximumJulianDay()) {
return $birth_date->minimumDate()->format('%j. %F %Y');
} else {
return '';
}
}
}

View file

@ -1,41 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* The individual's date of birth.
*/
class CensusColumnBirthDayMonthSlashYear extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
$birth_date = $individual->getBirthDate();
if ($birth_date->minimumJulianDay() === $birth_date->maximumJulianDay()) {
return $birth_date->minimumDate()->format('%j %M/%Y');
} else {
return '';
}
}
}

View file

@ -1,41 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* The individual's date of birth.
*/
class CensusColumnBirthDaySlashMonth extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
$birth_date = $individual->getBirthDate();
if ($birth_date->minimumJulianDay() === $birth_date->maximumJulianDay()) {
return $birth_date->minimumDate()->format('%j/%n');
} else {
return '';
}
}
}

View file

@ -1,41 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* The individual's date of birth.
*/
class CensusColumnBirthDaySlashMonthYear extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
$birth_date = $individual->getBirthDate();
if ($birth_date->minimumJulianDay() === $birth_date->maximumJulianDay()) {
return $birth_date->minimumDate()->format('%j/%n %Y');
} else {
return '';
}
}
}

View file

@ -1,35 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* The individual's date of birth.
*/
class CensusColumnBirthMonth extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
return $individual->getEstimatedBirthDate()->minimumDate()->format('%M');
}
}

View file

@ -1,47 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* The individual's birth place.
*/
class CensusColumnBirthPlace extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
$birth_place = $individual->getBirthPlace();
$census_place = $this->place();
// Ignore the census country
if ($birth_place === $census_place) {
return '';
}
if (substr($birth_place, -strlen($census_place) - 2) === ', ' . $census_place) {
return substr($birth_place, 0, -strlen($census_place) - 2);
} else {
return $birth_place;
}
}
}

View file

@ -1,35 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* The individual's birth place.
*/
class CensusColumnBirthPlaceSimple extends CensusColumnBirthPlace implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
return $this->lastPartOfPlace(parent::generate($individual, $head));
}
}

View file

@ -1,35 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* The individual's date of birth.
*/
class CensusColumnBirthYear extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
return $individual->getEstimatedBirthDate()->minimumDate()->format('%Y');
}
}

View file

@ -1,53 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* Was the individual born in "foreign parts".
*/
class CensusColumnBornForeignParts extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
$birth_place = explode(', ', $individual->getBirthPlace());
$birth_place = end($birth_place);
$census_place = $this->place();
if ($birth_place === 'Wales') {
$birth_place = 'England';
}
if ($census_place === 'Wales') {
$census_place = 'England';
}
if ($birth_place === $census_place || $birth_place === '') {
return '';
} elseif ($birth_place === 'England' || $birth_place === 'Scotland' || $birth_place === 'Ireland') {
return substr($birth_place, 0, 1);
} else {
return 'F';
}
}
}

View file

@ -1,53 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Date;
use Fisharebest\Webtrees\Individual;
/**
* The number of children born alive.
*/
class CensusColumnChildrenBornAlive extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
if ($individual->getSex() !== 'F') {
return '';
}
$count = 0;
foreach ($individual->getSpouseFamilies() as $family) {
foreach ($family->getChildren() as $child) {
if (
$child->getBirthDate()->isOK() &&
Date::compare($child->getBirthDate(), $this->date()) < 0 &&
$child->getBirthDate() != $child->getDeathDate()
) {
$count++;
}
}
}
return (string) $count;
}
}

View file

@ -1,55 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Date;
use Fisharebest\Webtrees\Individual;
/**
* The number of children who have died.
*/
class CensusColumnChildrenDied extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
if ($individual->getSex() !== 'F') {
return '';
}
$count = 0;
foreach ($individual->getSpouseFamilies() as $family) {
foreach ($family->getChildren() as $child) {
if (
$child->getBirthDate()->isOK() &&
Date::compare($child->getBirthDate(), $this->date()) < 0 &&
$child->getBirthDate() != $child->getDeathDate() &&
$child->getDeathDate()->isOK() &&
Date::compare($child->getDeathDate(), $this->date()) < 0
) {
$count++;
}
}
}
return (string) $count;
}
}

View file

@ -1,54 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Date;
use Fisharebest\Webtrees\Individual;
/**
* The number of children who are still living.
*/
class CensusColumnChildrenLiving extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
if ($individual->getSex() !== 'F') {
return '';
}
$count = 0;
foreach ($individual->getSpouseFamilies() as $family) {
foreach ($family->getChildren() as $child) {
if (
$child->getBirthDate()->isOK() &&
Date::compare($child->getBirthDate(), $this->date()) < 0 &&
$child->getBirthDate() != $child->getDeathDate() &&
(!$child->getDeathDate()->isOK() || Date::compare($child->getDeathDate(), $this->date()) > 0)
) {
$count++;
}
}
}
return (string) $count;
}
}

View file

@ -1,37 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
/**
* Marital status.
*/
class CensusColumnConditionDanish extends CensusColumnConditionEnglish {
/* Text to display for married individuals */
protected $husband = 'Gift';
protected $wife = 'Gift';
/* Text to display for unmarried individuals */
protected $bachelor = 'Ugift';
protected $spinster = 'Ugift';
/* Text to display for divorced individuals */
protected $divorce = 'Skilt';
protected $divorcee = 'Skilt';
/* Text to display for widowed individuals */
protected $widower = 'Gift';
protected $widow = 'Gift';
}

View file

@ -1,37 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
/**
* Marital status.
*/
class CensusColumnConditionEnglish extends AbstractCensusColumnCondition {
/* Text to display for married individuals */
protected $husband = 'Mar';
protected $wife = 'Mar';
/* Text to display for unmarried individuals */
protected $bachelor = 'Unm';
protected $spinster = 'Unm';
/* Text to display for divorced individuals */
protected $divorce = 'Div';
protected $divorcee = 'Div';
/* Text to display for widowed individuals (not yet implemented) */
protected $widower = 'Wid';
protected $widow = 'Wid';
}

View file

@ -1,41 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
/**
* Marital status.
*/
class CensusColumnConditionFrenchFemme extends AbstractCensusColumnCondition {
/* Text to display for married individuals */
protected $husband = '';
protected $wife = '1';
/* Text to display for unmarried individuals */
protected $bachelor = '';
protected $spinster = '';
/* Text to display for children */
protected $boy = '';
protected $girl = '';
/* Text to display for divorced individuals */
protected $divorce = '';
protected $divorcee = '1';
/* Text to display for widowed individuals (not yet implemented) */
protected $widower = '';
protected $widow = '';
}

View file

@ -1,41 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
/**
* Marital status.
*/
class CensusColumnConditionFrenchFille extends AbstractCensusColumnCondition {
/* Text to display for married individuals */
protected $husband = '';
protected $wife = '';
/* Text to display for unmarried individuals */
protected $bachelor = '';
protected $spinster = '1';
/* Text to display for children */
protected $boy = '';
protected $girl = '1';
/* Text to display for divorced individuals */
protected $divorce = '';
protected $divorcee = '';
/* Text to display for widowed individuals (not yet implemented) */
protected $widower = '';
protected $widow = '';
}

View file

@ -1,41 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
/**
* Marital status.
*/
class CensusColumnConditionFrenchGarcon extends AbstractCensusColumnCondition {
/* Text to display for married individuals */
protected $husband = '';
protected $wife = '';
/* Text to display for unmarried individuals */
protected $bachelor = '1';
protected $spinster = '';
/* Text to display for children */
protected $boy = '1';
protected $girl = '';
/* Text to display for divorced individuals */
protected $divorce = '';
protected $divorcee = '';
/* Text to display for widowed individuals (not yet implemented) */
protected $widower = '';
protected $widow = '';
}

View file

@ -1,41 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
/**
* Marital status.
*/
class CensusColumnConditionFrenchHomme extends AbstractCensusColumnCondition {
/* Text to display for married individuals */
protected $husband = '1';
protected $wife = '';
/* Text to display for unmarried individuals */
protected $bachelor = '';
protected $spinster = '';
/* Text to display for children */
protected $boy = '';
protected $girl = '';
/* Text to display for divorced individuals */
protected $divorce = '1';
protected $divorcee = '';
/* Text to display for widowed individuals (not yet implemented) */
protected $widower = '';
protected $widow = '';
}

View file

@ -1,41 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
/**
* Marital status.
*/
class CensusColumnConditionFrenchVeuf extends AbstractCensusColumnCondition {
/* Text to display for married individuals */
protected $husband = '';
protected $wife = '';
/* Text to display for unmarried individuals */
protected $bachelor = '';
protected $spinster = '';
/* Text to display for children */
protected $boy = '';
protected $girl = '';
/* Text to display for divorced individuals */
protected $divorce = '';
protected $divorcee = '';
/* Text to display for widowed individuals (not yet implemented) */
protected $widower = '1';
protected $widow = '';
}

View file

@ -1,41 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
/**
* Marital status.
*/
class CensusColumnConditionFrenchVeuve extends AbstractCensusColumnCondition {
/* Text to display for married individuals */
protected $husband = '';
protected $wife = '';
/* Text to display for unmarried individuals */
protected $bachelor = '';
protected $spinster = '';
/* Text to display for children */
protected $boy = '';
protected $girl = '';
/* Text to display for divorced individuals */
protected $divorce = '';
protected $divorcee = '';
/* Text to display for widowed individuals (not yet implemented) */
protected $widower = '';
protected $widow = '1';
}

View file

@ -1,41 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
/**
* Marital status.
*/
class CensusColumnConditionUs extends CensusColumnConditionEnglish {
/* Text to display for married individuals */
protected $husband = 'M';
protected $wife = 'M';
/* Text to display for unmarried individuals */
protected $bachelor = 'S';
protected $spinster = 'S';
/* Text to display for children */
protected $boy = 'S';
protected $girl = 'S';
/* Text to display for divorced individuals */
protected $divorce = 'D';
protected $divorcee = 'D';
/* Text to display for widowed individuals (not yet implemented) */
protected $widower = 'W';
protected $widow = 'W';
}

View file

@ -1,41 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* Birth place of the individual's fther.
*/
class CensusColumnFatherBirthPlace extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
$father = $this->father($individual);
if ($father) {
return $this->notCountry($father->getBirthPlace());
} else {
return '';
}
}
}

View file

@ -1,35 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* The individual's father's birth place.
*/
class CensusColumnFatherBirthPlaceSimple extends CensusColumnFatherBirthPlace implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
return $this->lastPartOfPlace(parent::generate($individual, $head));
}
}

View file

@ -1,41 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* Is the individual's father a foreigner.
*/
class CensusColumnFatherForeign extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
$father = $this->father($individual);
if ($father && $this->lastPartOfPlace($father->getBirthPlace()) !== $this->place()) {
return 'Y';
} else {
return '';
}
}
}

View file

@ -1,69 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Date;
use Fisharebest\Webtrees\Individual;
/**
* The individual's full name.
*/
class CensusColumnFullName extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
$name = $this->nameAtCensusDate($individual, $this->date());
return strip_tags($name['full']);
}
/**
* What was an individual's likely name on a given date, allowing
* for marriages and married names.
*
* @param Individual $individual
* @param Date $census_date
*
* @return string[]
*/
protected function nameAtCensusDate(Individual $individual, Date $census_date) {
$names = $individual->getAllNames();
$name = $names[0];
foreach ($individual->getSpouseFamilies() as $family) {
foreach ($family->getFacts('MARR') as $marriage) {
if ($marriage->getDate()->isOK() && Date::compare($marriage->getDate(), $census_date) < 0) {
$spouse = $family->getSpouse($individual);
foreach ($names as $individual_name) {
foreach ($spouse->getAllNames() as $spouse_name) {
if ($individual_name['type'] === '_MARNM' && $individual_name['surn'] === $spouse_name['surn']) {
return $individual_name;
}
}
}
}
}
}
return $name;
}
}

View file

@ -1,44 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* The individual's full name.
*/
class CensusColumnGivenNameInitial extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
foreach ($individual->getAllNames() as $name) {
$given = $name['givn'];
if (strpos($given, ' ') === false) {
return $given;
} else {
return substr($given, 0, strpos($given, ' ') + 2);
}
}
return '';
}
}

View file

@ -1,39 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* The individual's full name.
*/
class CensusColumnGivenNames extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
foreach ($individual->getAllNames() as $name) {
return $name['givn'];
}
return '';
}
}

View file

@ -1,62 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Date;
use Fisharebest\Webtrees\Individual;
/**
* Definitions for a census column
*/
interface CensusColumnInterface {
/**
* A short version of the column's name.
*
* @return string
*/
public function abbreviation();
/**
* When did this census occur
*
* @return Date
*/
public function date();
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null);
/**
* Where did this census occur
*
* @return string
*/
public function place();
/**
* The full version of the column's name.
*
* @return string
*/
public function title();
}

View file

@ -1,45 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* Did the individual marry within the last year.
*/
class CensusColumnMarriedWithinYear extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
foreach ($individual->getSpouseFamilies() as $family) {
foreach ($family->getFacts('MARR') as $fact) {
$marriage_jd = $fact->getDate()->julianDay();
$census_jd = $this->date()->julianDay();
if ($marriage_jd <= $census_jd && $marriage_jd >= $census_jd - 365) {
return 'Y';
}
}
}
return '';
}
}

View file

@ -1,42 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* The month of birth, if within the last year.
*/
class CensusColumnMonthIfBornWithinYear extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
$birth_jd = $individual->getBirthDate()->julianDay();
$census_jd = $this->date()->julianDay();
if ($birth_jd <= $census_jd && $birth_jd >= $census_jd - 365) {
// Use the GEDCOM month, as we need this in English - for the US census
return ucfirst(strtolower($individual->getBirthDate()->minimumDate()->format('%O')));
} else {
return '';
}
}
}

View file

@ -1,46 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* The month of marriage, if within the last year.
*/
class CensusColumnMonthIfMarriedWithinYear extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
foreach ($individual->getSpouseFamilies() as $family) {
foreach ($family->getFacts('MARR') as $fact) {
$marriage_jd = $fact->getDate()->julianDay();
$census_jd = $this->date()->julianDay();
if ($marriage_jd <= $census_jd && $marriage_jd >= $census_jd - 365) {
// Use the GEDCOM month, as we need this in English - for the US census
return ucfirst(strtolower($fact->getDate()->minimumDate()->format('%O')));
}
}
}
return '';
}
}

View file

@ -1,41 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* Birth place of the individual's mother.
*/
class CensusColumnMotherBirthPlace extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
$mother = $this->mother($individual);
if ($mother) {
return $this->notCountry($mother->getBirthPlace());
} else {
return '';
}
}
}

View file

@ -1,35 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* The individual's mother's birth place.
*/
class CensusColumnMotherBirthPlaceSimple extends CensusColumnMotherBirthPlace implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
return $this->lastPartOfPlace(parent::generate($individual, $head));
}
}

View file

@ -1,41 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* Is the individual's mother a foreigner.
*/
class CensusColumnMotherForeign extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
$mother = $this->mother($individual);
if ($mother && $this->lastPartOfPlace($mother->getBirthPlace()) !== $this->place()) {
return 'Y';
} else {
return '';
}
}
}

View file

@ -1,64 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Date;
use Fisharebest\Webtrees\Individual;
/**
* The nationality of the individual.
*/
class CensusColumnNationality extends AbstractCensusColumn implements CensusColumnInterface {
/** @var array Convert a country name to a nationality */
private $nationalities = array(
'England' => 'British',
'Scotland' => 'British',
'Wales' => 'British',
'Deutschland' => 'Deutsch',
);
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
$place = $individual->getBirthPlace();
// No birthplace? Assume born in the same country.
if ($place === '') {
$place = $this->place();
}
// Did we emigrate or naturalise?
foreach ($individual->getFacts('IMMI|EMIG|NATU', true) as $fact) {
if (Date::compare($fact->getDate(), $this->date()) <= 0) {
$place = $fact->getPlace()->getGedcomName();
}
}
$place = $this->lastPartOfPlace($place);
if (array_key_exists($place, $this->nationalities)) {
return $this->nationalities[$place];
} else {
return $place;
}
}
}

View file

@ -1,35 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* A column that we cannot generate.
*/
class CensusColumnNull extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
return '';
}
}

View file

@ -1,39 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* The individual's occupation.
*/
class CensusColumnOccupation extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
foreach ($individual->getFacts('OCCU') as $fact) {
return $fact->getValue();
}
return '';
}
}

View file

@ -1,42 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Functions\Functions;
use Fisharebest\Webtrees\Individual;
/**
* Relationshiop to head of household.
*/
class CensusColumnRelationToHead extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
if ($head === null) {
return '';
} elseif ($individual == $head) {
return 'head';
} else {
return Functions::getCloseRelationshipName($head, $individual);
}
}
}

View file

@ -1,42 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Functions\Functions;
use Fisharebest\Webtrees\Individual;
/**
* Relationshiop to head of household.
*/
class CensusColumnRelationToHeadGerman extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
if ($head === null) {
return '';
} elseif ($individual == $head) {
return 'Haushaltungsvorstand';
} else {
return Functions::getCloseRelationshipName($head, $individual);
}
}
}

View file

@ -1,37 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* The individual's religion.
*/
class CensusColumnReligion extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @todo Look for RELI tags (or subtags?)
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
return '';
}
}

View file

@ -1,48 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* The individual's sex.
*/
class CensusColumnSexMF extends AbstractCensusColumn implements CensusColumnInterface {
/* Text to display for male individuals */
protected $male = 'M';
/* Text to display for female individuals */
protected $female = 'F';
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
switch ($individual->getSex()) {
case 'M':
return $this->male;
case 'F':
return $this->female;
default:
return '';
}
}
}

View file

@ -1,24 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
/**
* The individual's sex.
*/
class CensusColumnSexMK extends CensusColumnSexMF {
/* Text to display for female individuals */
protected $female = 'K';
}

View file

@ -1,24 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
/**
* The individual's sex.
*/
class CensusColumnSexMZ extends CensusColumnSexMF {
/* Text to display for female individuals */
protected $female = 'Ž';
}

View file

@ -1,39 +0,0 @@
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2016 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Fisharebest\Webtrees\Census;
use Fisharebest\Webtrees\Individual;
/**
* The individual's full name.
*/
class CensusColumnSurname extends AbstractCensusColumn implements CensusColumnInterface {
/**
* Generate the likely value of this census column, based on available information.
*
* @param Individual $individual
* @param Individual|null $head
*
* @return string
*/
public function generate(Individual $individual, Individual $head = null) {
foreach ($individual->getAllNames() as $name) {
return $name['surname'];
}
return '';
}
}

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