mirror of
https://github.com/YunoHost-Apps/webtrees_ynh.git
synced 2024-09-03 18:26:37 +02:00
webtrees install and remove script for yunohot
This commit is contained in:
parent
ac50420028
commit
641e6c4ec8
5820 changed files with 659383 additions and 0 deletions
7
conf/config.ini.php
Executable file
7
conf/config.ini.php
Executable file
|
@ -0,0 +1,7 @@
|
||||||
|
; <?php exit; ?> DO NOT DELETE THIS LINE
|
||||||
|
dbhost="localhost"
|
||||||
|
dbport="3306"
|
||||||
|
dbuser="__dbuser__"
|
||||||
|
dbpass="__dbpass__"
|
||||||
|
dbname="__dbname__"
|
||||||
|
tblpfx="wt_"
|
37
conf/nginx.conf
Normal file
37
conf/nginx.conf
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
location YNH_WWW_PATH {
|
||||||
|
|
||||||
|
# Path to source
|
||||||
|
alias YNH_WWW_ALIAS ;
|
||||||
|
|
||||||
|
# Example PHP configuration (remove if not used)
|
||||||
|
index index.php;
|
||||||
|
|
||||||
|
|
||||||
|
if ($scheme = http) {
|
||||||
|
rewrite ^ https://$server_name$request_uri? permanent;
|
||||||
|
}
|
||||||
|
# Common parameter to increase upload size limit in conjuction with dedicated php-fpm file
|
||||||
|
#client_max_body_size 50M;
|
||||||
|
|
||||||
|
try_files $uri $uri/ index.php;
|
||||||
|
location ~ [^/]\.php(/|$) {
|
||||||
|
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
|
||||||
|
fastcgi_pass unix:/var/run/php5-fpm.sock;
|
||||||
|
|
||||||
|
# Filename to be changed if dedicated php-fpm process is required
|
||||||
|
# This is to be used INSTEAD of line above
|
||||||
|
# Don't forget to adjust scripts install/upgrade/remove/backup accordingly
|
||||||
|
#
|
||||||
|
#fastcgi_pass unix:/var/run/php5-fpm-YNH_WWW_APP.sock;
|
||||||
|
|
||||||
|
fastcgi_index index.php;
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_param REMOTE_USER $remote_user;
|
||||||
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $request_filename;
|
||||||
|
}
|
||||||
|
# PHP configuration end
|
||||||
|
|
||||||
|
# Include SSOWAT user panel.
|
||||||
|
include conf.d/yunohost_panel.conf.inc;
|
||||||
|
}
|
3
conf/sql/admin.sql
Normal file
3
conf/sql/admin.sql
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
INSERT INTO `wt_user` (`user_id`, `user_name`, `real_name`, `email`, `password`) VALUES (NULL, '__USER_NAME__', '__NAME__', '__USER_EMAIL__', '__PASSWORD__');
|
||||||
|
INSERT INTO `wt_user_setting` (`user_id`, `setting_name`, `setting_value`) VALUES ('1', 'canadmin', '1'), ('1', 'verified', '1'), ('1', 'verified_by_admin', '1');
|
||||||
|
|
1298
conf/sql/webtrees.sql
Normal file
1298
conf/sql/webtrees.sql
Normal file
File diff suppressed because it is too large
Load diff
84
manifest.json
Normal file
84
manifest.json
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
{
|
||||||
|
"name": "webtrees",
|
||||||
|
"id": "webtrees",
|
||||||
|
"packaging_format": 1,
|
||||||
|
"description": {
|
||||||
|
"en": "webtrees is a free open source web-based genealogy application.",
|
||||||
|
},
|
||||||
|
"url": "https://www.webtrees.net",
|
||||||
|
"maintainer": {
|
||||||
|
"name": "anmol Sharma",
|
||||||
|
"email": "anmol@datamol.in",
|
||||||
|
},
|
||||||
|
"requirements": {
|
||||||
|
"yunohost": ">> 2.4.0"
|
||||||
|
},
|
||||||
|
"multi_instance": false,
|
||||||
|
"services": [
|
||||||
|
"nginx",
|
||||||
|
"php5-fpm",
|
||||||
|
"mysql"
|
||||||
|
],
|
||||||
|
"arguments": {
|
||||||
|
"install" : [
|
||||||
|
{
|
||||||
|
"name": "domain",
|
||||||
|
"type": "domain",
|
||||||
|
"ask": {
|
||||||
|
"en": "Choose a domain name for ynhexample",
|
||||||
|
},
|
||||||
|
"example": "example.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "path",
|
||||||
|
"type": "path",
|
||||||
|
"ask": {
|
||||||
|
"en": "Choose a path for ynhexample",
|
||||||
|
},
|
||||||
|
"example": "/example",
|
||||||
|
"default": "/webtrees"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "username",
|
||||||
|
"type": "username",
|
||||||
|
"ask": {
|
||||||
|
"en": "Choose an admin username (Not a LDAP User)",
|
||||||
|
},
|
||||||
|
"example": "john"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"type": "name",
|
||||||
|
"ask": {
|
||||||
|
"en": "Name of the user (Not a LDAP User)",
|
||||||
|
},
|
||||||
|
"example": "john doe"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "email",
|
||||||
|
"type": "email",
|
||||||
|
"ask": {
|
||||||
|
"en": "Admin email (All the new registration will be sent on this email)",
|
||||||
|
},
|
||||||
|
"example": "admin@example.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "password",
|
||||||
|
"type": "password",
|
||||||
|
"ask": {
|
||||||
|
"en": "Password for the admin account (At least 6 characters long)",
|
||||||
|
},
|
||||||
|
"example": "admin@example.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "is_public",
|
||||||
|
"type": "boolean",
|
||||||
|
"ask": {
|
||||||
|
"en": "Is it a public application?",
|
||||||
|
"fr": "Est-ce une application publique ?"
|
||||||
|
},
|
||||||
|
"default": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
108
script/install
Executable file
108
script/install
Executable file
|
@ -0,0 +1,108 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Exit on command errors and treat unset variables as an error
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
# This is a multi-instance app, meaning it can be installed several times independently
|
||||||
|
# The id of the app as stated in the manifest is available as $YNH_APP_ID
|
||||||
|
# The instance number is available as $YNH_APP_INSTANCE_NUMBER (equals "1", "2", ...)
|
||||||
|
# The app instance name is available as $YNH_APP_INSTANCE_NAME
|
||||||
|
# - the first time the app is installed, YNH_APP_INSTANCE_NAME = ynhexample
|
||||||
|
# - the second time the app is installed, YNH_APP_INSTANCE_NAME = ynhexample__2
|
||||||
|
# - ynhexample__{N} for the subsequent installations, with N=3,4, ...
|
||||||
|
# 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,
|
||||||
|
# db names, ...
|
||||||
|
app=$YNH_APP_INSTANCE_NAME
|
||||||
|
|
||||||
|
# Retrieve arguments
|
||||||
|
domain=$YNH_APP_ARG_DOMAIN
|
||||||
|
path_url=$YNH_APP_ARG_PATH
|
||||||
|
is_public=$YNH_APP_ARG_IS_PUBLIC
|
||||||
|
admin_username=$YNH_APP_ARG_USERNAME
|
||||||
|
admin_name=$YNH_APP_ARG_NAME
|
||||||
|
admin_email=$YNH_APP_ARG_EMAIL
|
||||||
|
admin_password=openssl passwd -1 -salt xyz $YNH_APP_ARG_PASSWORD
|
||||||
|
|
||||||
|
# Source YunoHost helpers
|
||||||
|
source /usr/share/yunohost/helpers
|
||||||
|
|
||||||
|
# Save app settings
|
||||||
|
ynh_app_setting_set "$app" is_public "$is_public"
|
||||||
|
|
||||||
|
|
||||||
|
# Check domain/path availability
|
||||||
|
sudo yunohost app checkurl "${domain}${path_url}" -a "$app" \
|
||||||
|
|| ynh_die "Path not available: ${domain}${path_url}"
|
||||||
|
|
||||||
|
# Check password strength
|
||||||
|
[[ ${#admin_password} -gt 6 ]] || ynh_die \
|
||||||
|
"The password is too weak, it must be longer than 6 characters"
|
||||||
|
|
||||||
|
|
||||||
|
# Copy source files
|
||||||
|
src_path=/var/www/$app
|
||||||
|
sudo mkdir -p $src_path
|
||||||
|
sudo cp -a ../sources/. $src_path
|
||||||
|
|
||||||
|
# Set permissions to app files
|
||||||
|
# you may need to make some file and/or directory writeable by www-data (nginx user)
|
||||||
|
sudo chown -R root: $src_path
|
||||||
|
|
||||||
|
### MySQL (can be removed if not used) ###
|
||||||
|
# If your app use a MySQL database you can use these lines to bootstrap
|
||||||
|
# a database, an associated user and save the password in app settings.
|
||||||
|
#
|
||||||
|
# # Generate MySQL password and create database
|
||||||
|
dbuser=$app
|
||||||
|
dbname=$app
|
||||||
|
dbpass=$(ynh_string_random 12)
|
||||||
|
ynh_app_setting_set "$app" mysqlpwd "$dbpass"
|
||||||
|
ynh_mysql_create_db "$dbname" "$dbuser" "$dbpass"
|
||||||
|
|
||||||
|
# Adding the details of the database to the config file
|
||||||
|
sed -i "s@__dbuser__@$dbuser@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
|
||||||
|
|
||||||
|
# Copy the config file to the src_path
|
||||||
|
sudo cp ../conf/config.ini.php $src_path/data/.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# # Load initial SQL into the new database
|
||||||
|
ynh_mysql_connect_as "$dbuser" "$dbpass" "$dbname" \
|
||||||
|
< "../sources/sql/webtrees.sql"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Replace variables in sql scripts
|
||||||
|
ynh_replace_string "__USER_NAME__" "$admin_username" ../conf/sql/admin.sql
|
||||||
|
ynh_replace_string "__NAME__" "$admin_name" ../conf/sql/admin.sql
|
||||||
|
ynh_replace_string "__USER_EMAIL__" "$admin_email" ../conf/sql/admin.sql
|
||||||
|
ynh_replace_string "__PASSWORD__" "$admin_password" ../conf/sql/admin.sql
|
||||||
|
|
||||||
|
ynh_mysql_connect_as $db_name $db_pwd $db_name < ../conf/sql/admin.sql
|
||||||
|
|
||||||
|
### MySQL end ###
|
||||||
|
|
||||||
|
# Modify Nginx configuration file and copy it to Nginx conf directory
|
||||||
|
nginx_conf=../conf/nginx.conf
|
||||||
|
sed -i "s@YNH_WWW_PATH@$path_url@g" $nginx_conf
|
||||||
|
sed -i "s@YNH_WWW_ALIAS@$src_path/@g" $nginx_conf
|
||||||
|
# If a dedicated php-fpm process is used:
|
||||||
|
# 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
|
||||||
|
sudo cp $nginx_conf /etc/nginx/conf.d/$domain.d/$app.conf
|
||||||
|
|
||||||
|
|
||||||
|
# If app is public, add url to SSOWat conf as skipped_uris
|
||||||
|
if [[ $is_public -eq 1 ]]; then
|
||||||
|
# unprotected_uris allows SSO credentials to be passed anyway.
|
||||||
|
ynh_app_setting_set "$app" unprotected_uris "/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
sudo chmod -R 777 $src_path/data
|
||||||
|
|
||||||
|
# Reload services
|
||||||
|
sudo service nginx reload
|
34
script/remove
Executable file
34
script/remove
Executable file
|
@ -0,0 +1,34 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# See comments in install script
|
||||||
|
app=$YNH_APP_INSTANCE_NAME
|
||||||
|
|
||||||
|
# Source YunoHost helpers
|
||||||
|
source /usr/share/yunohost/helpers
|
||||||
|
|
||||||
|
# Retrieve app settings
|
||||||
|
domain=$(ynh_app_setting_get "$app" domain)
|
||||||
|
|
||||||
|
# Remove sources
|
||||||
|
sudo rm -rf /var/www/$app
|
||||||
|
|
||||||
|
# Remove nginx configuration file
|
||||||
|
sudo rm -f /etc/nginx/conf.d/$domain.d/$app.conf
|
||||||
|
|
||||||
|
### PHP (remove if not used) ###
|
||||||
|
# If a dedicated php-fpm process is used:
|
||||||
|
# sudo rm -f /etc/php5/fpm/pool.d/$app.conf
|
||||||
|
# sudo service php5-fpm reload
|
||||||
|
### PHP end ###
|
||||||
|
|
||||||
|
### MySQL (remove if not used) ###
|
||||||
|
# If a MySQL database is used:
|
||||||
|
# # Drop MySQL database and user
|
||||||
|
dbname=$app
|
||||||
|
dbuser=$app
|
||||||
|
ynh_mysql_drop_db "$dbname" || true
|
||||||
|
ynh_mysql_drop_user "$dbuser" || true
|
||||||
|
### MySQL end ###
|
||||||
|
|
||||||
|
# Reload nginx service
|
||||||
|
sudo service nginx reload
|
429
sources/README.md
Normal file
429
sources/README.md
Normal file
|
@ -0,0 +1,429 @@
|
||||||
|
[![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 project’s 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): 16–32 MB, 10–20 seconds
|
||||||
|
* Medium systems (5,000 individuals): 32–64 MB, 20–40 seconds
|
||||||
|
* Large systems (50,000 individuals): 64–128 MB, 40–80 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.
|
253
sources/action.php
Normal file
253
sources/action.php
Normal file
|
@ -0,0 +1,253 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can’t find the record/fact, or don’t 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;
|
||||||
|
}
|
719
sources/addmedia.php
Normal file
719
sources/addmedia.php
Normal file
|
@ -0,0 +1,719 @@
|
||||||
|
<?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">';
|
||||||
|
//-- don’t 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 isn’t 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>
|
913
sources/admin.php
Normal file
913
sources/admin.php
Normal file
|
@ -0,0 +1,913 @@
|
||||||
|
<?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&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>
|
698
sources/admin_media.php
Normal file
698
sources/admin_media.php
Normal file
|
@ -0,0 +1,698 @@
|
||||||
|
<?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 can’t 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&ged=' . rawurlencode($media_tree) . '&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&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
|
||||||
|
// browser’s 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>
|
264
sources/admin_media_upload.php
Normal file
264
sources/admin_media_upload.php
Normal file
|
@ -0,0 +1,264 @@
|
||||||
|
<?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('<select>'), ' </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>';
|
113
sources/admin_module_blocks.php
Normal file
113
sources/admin_module_blocks.php
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
<?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>
|
106
sources/admin_module_charts.php
Normal file
106
sources/admin_module_charts.php
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
<?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>
|
131
sources/admin_module_menus.php
Normal file
131
sources/admin_module_menus.php
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
<?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>
|
106
sources/admin_module_reports.php
Normal file
106
sources/admin_module_reports.php
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
<?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>
|
131
sources/admin_module_sidebar.php
Normal file
131
sources/admin_module_sidebar.php
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
<?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>
|
131
sources/admin_module_tabs.php
Normal file
131
sources/admin_module_tabs.php
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
<?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>
|
263
sources/admin_modules.php
Normal file
263
sources/admin_modules.php
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
<?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 can’t 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>
|
1154
sources/admin_pgv_to_wt.php
Normal file
1154
sources/admin_pgv_to_wt.php
Normal file
File diff suppressed because it is too large
Load diff
385
sources/admin_site_access.php
Normal file
385
sources/admin_site_access.php
Normal file
|
@ -0,0 +1,385 @@
|
||||||
|
<?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&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;
|
||||||
|
}
|
396
sources/admin_site_change.php
Normal file
396
sources/admin_site_change.php
Normal file
|
@ -0,0 +1,396 @@
|
||||||
|
<?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 ?>
|
117
sources/admin_site_clean.php
Normal file
117
sources/admin_site_clean.php
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
<?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 don’t 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>
|
655
sources/admin_site_config.php
Normal file
655
sources/admin_site_config.php
Normal file
|
@ -0,0 +1,655 @@
|
||||||
|
<?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 today’s 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 server’s 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>
|
81
sources/admin_site_info.php
Normal file
81
sources/admin_site_info.php
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
<?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>
|
336
sources/admin_site_logs.php
Normal file
336
sources/admin_site_logs.php
Normal file
|
@ -0,0 +1,336 @@
|
||||||
|
<?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 ?>
|
398
sources/admin_site_merge.php
Normal file
398
sources/admin_site_merge.php
Normal file
|
@ -0,0 +1,398 @@
|
||||||
|
<?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; ?>
|
45
sources/admin_site_readme.php
Normal file
45
sources/admin_site_readme.php
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<?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>
|
460
sources/admin_site_upgrade.php
Normal file
460
sources/admin_site_upgrade.php
Normal file
|
@ -0,0 +1,460 @@
|
||||||
|
<?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 module’s 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 theme’s 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
284
sources/admin_trees_check.php
Normal file
284
sources/admin_trees_check.php
Normal file
|
@ -0,0 +1,284 @@
|
||||||
|
<?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>';
|
||||||
|
}
|
2167
sources/admin_trees_config.php
Normal file
2167
sources/admin_trees_config.php
Normal file
File diff suppressed because it is too large
Load diff
210
sources/admin_trees_download.php
Normal file
210
sources/admin_trees_download.php
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
<?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>
|
184
sources/admin_trees_duplicates.php
Normal file
184
sources/admin_trees_duplicates.php
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
<?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(); ?>&gid1=<?php echo $duplicates[0]->getXref(); ?>&gid2=<?php echo $duplicates[1]->getXref(); ?>&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; ?>
|
50
sources/admin_trees_export.php
Normal file
50
sources/admin_trees_export.php
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
<?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');
|
774
sources/admin_trees_manage.php
Normal file
774
sources/admin_trees_manage.php
Normal file
|
@ -0,0 +1,774 @@
|
||||||
|
<?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&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&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&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="[^<>&"#^$*?{}()\[\]/\\]*"
|
||||||
|
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 PhpGedView’s'); ?>
|
||||||
|
</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>
|
205
sources/admin_trees_merge.php
Normal file
205
sources/admin_trees_merge.php
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
<?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>';
|
||||||
|
}
|
132
sources/admin_trees_places.php
Normal file
132
sources/admin_trees_places.php
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
<?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); ?>
|
||||||
|
→
|
||||||
|
<?php echo Filter::escapeHtml($new_place); ?>
|
||||||
|
</li>
|
||||||
|
<?php } ?>
|
||||||
|
</ul>
|
||||||
|
<?php } else { ?>
|
||||||
|
<p>
|
||||||
|
<?php echo I18N::translate('No places have been found.'); ?>
|
||||||
|
</p>
|
||||||
|
<?php } ?>
|
||||||
|
<?php } ?>
|
||||||
|
|
287
sources/admin_trees_renumber.php
Normal file
287
sources/admin_trees_renumber.php
Normal file
|
@ -0,0 +1,287 @@
|
||||||
|
<?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 server’s 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>';
|
||||||
|
}
|
91
sources/admin_trees_unconnected.php
Normal file
91
sources/admin_trees_unconnected.php
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
<?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 ?>
|
862
sources/admin_users.php
Normal file
862
sources/admin_users.php
Normal file
|
@ -0,0 +1,862 @@
|
||||||
|
<?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&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 user’s “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 today’s 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 user’s 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&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&user_id=<?php echo $user->getUserId(); ?>">
|
||||||
|
<?php echo Filter::escapeHtml($user->getUserName()); ?>
|
||||||
|
—
|
||||||
|
<?php echo $user->getRealNameHtml(); ?>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<?php echo I18N::translate('User’s 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&user_id=<?php echo $user->getUserId(); ?>">
|
||||||
|
<?php echo Filter::escapeHtml($user->getUserName()); ?>
|
||||||
|
—
|
||||||
|
<?php echo $user->getRealNameHtml(); ?>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<?php echo I18N::translate('User didn’t 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&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;
|
||||||
|
}
|
53
sources/admin_users_bulk.php
Normal file
53
sources/admin_users_bulk.php
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<?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>
|
174
sources/ancestry.php
Normal file
174
sources/ancestry.php
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
<?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>';
|
194
sources/app/Auth.php
Normal file
194
sources/app/Auth.php
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
<?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);
|
||||||
|
}
|
||||||
|
}
|
172
sources/app/Census/AbstractCensusColumn.php
Normal file
172
sources/app/Census/AbstractCensusColumn.php
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
}
|
186
sources/app/Census/AbstractCensusColumnCondition.php
Normal file
186
sources/app/Census/AbstractCensusColumnCondition.php
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
}
|
37
sources/app/Census/Census.php
Normal file
37
sources/app/Census/Census.php
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<?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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
36
sources/app/Census/CensusColumnAge.php
Normal file
36
sources/app/Census/CensusColumnAge.php
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<?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);
|
||||||
|
}
|
||||||
|
}
|
40
sources/app/Census/CensusColumnAgeFemale.php
Normal file
40
sources/app/Census/CensusColumnAgeFemale.php
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<?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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
sources/app/Census/CensusColumnAgeFemale5Years.php
Normal file
45
sources/app/Census/CensusColumnAgeFemale5Years.php
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
40
sources/app/Census/CensusColumnAgeMale.php
Normal file
40
sources/app/Census/CensusColumnAgeMale.php
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<?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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
sources/app/Census/CensusColumnAgeMale5Years.php
Normal file
45
sources/app/Census/CensusColumnAgeMale5Years.php
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
46
sources/app/Census/CensusColumnAgeMarried.php
Normal file
46
sources/app/Census/CensusColumnAgeMarried.php
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<?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 '';
|
||||||
|
}
|
||||||
|
}
|
35
sources/app/Census/CensusColumnBirthDate.php
Normal file
35
sources/app/Census/CensusColumnBirthDate.php
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<?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');
|
||||||
|
}
|
||||||
|
}
|
35
sources/app/Census/CensusColumnBirthDay.php
Normal file
35
sources/app/Census/CensusColumnBirthDay.php
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<?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');
|
||||||
|
}
|
||||||
|
}
|
41
sources/app/Census/CensusColumnBirthDayDotMonthYear.php
Normal file
41
sources/app/Census/CensusColumnBirthDayDotMonthYear.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?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 '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
sources/app/Census/CensusColumnBirthDayMonthSlashYear.php
Normal file
41
sources/app/Census/CensusColumnBirthDayMonthSlashYear.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?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 '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
sources/app/Census/CensusColumnBirthDaySlashMonth.php
Normal file
41
sources/app/Census/CensusColumnBirthDaySlashMonth.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?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 '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
sources/app/Census/CensusColumnBirthDaySlashMonthYear.php
Normal file
41
sources/app/Census/CensusColumnBirthDaySlashMonthYear.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?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 '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
sources/app/Census/CensusColumnBirthMonth.php
Normal file
35
sources/app/Census/CensusColumnBirthMonth.php
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<?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');
|
||||||
|
}
|
||||||
|
}
|
47
sources/app/Census/CensusColumnBirthPlace.php
Normal file
47
sources/app/Census/CensusColumnBirthPlace.php
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
sources/app/Census/CensusColumnBirthPlaceSimple.php
Normal file
35
sources/app/Census/CensusColumnBirthPlaceSimple.php
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<?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));
|
||||||
|
}
|
||||||
|
}
|
35
sources/app/Census/CensusColumnBirthYear.php
Normal file
35
sources/app/Census/CensusColumnBirthYear.php
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<?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');
|
||||||
|
}
|
||||||
|
}
|
53
sources/app/Census/CensusColumnBornForeignParts.php
Normal file
53
sources/app/Census/CensusColumnBornForeignParts.php
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<?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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
sources/app/Census/CensusColumnChildrenBornAlive.php
Normal file
53
sources/app/Census/CensusColumnChildrenBornAlive.php
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
}
|
55
sources/app/Census/CensusColumnChildrenDied.php
Normal file
55
sources/app/Census/CensusColumnChildrenDied.php
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
}
|
54
sources/app/Census/CensusColumnChildrenLiving.php
Normal file
54
sources/app/Census/CensusColumnChildrenLiving.php
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
}
|
37
sources/app/Census/CensusColumnConditionDanish.php
Normal file
37
sources/app/Census/CensusColumnConditionDanish.php
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<?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';
|
||||||
|
}
|
37
sources/app/Census/CensusColumnConditionEnglish.php
Normal file
37
sources/app/Census/CensusColumnConditionEnglish.php
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<?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';
|
||||||
|
}
|
41
sources/app/Census/CensusColumnConditionFrenchFemme.php
Normal file
41
sources/app/Census/CensusColumnConditionFrenchFemme.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?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 = '';
|
||||||
|
}
|
41
sources/app/Census/CensusColumnConditionFrenchFille.php
Normal file
41
sources/app/Census/CensusColumnConditionFrenchFille.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?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 = '';
|
||||||
|
}
|
41
sources/app/Census/CensusColumnConditionFrenchGarcon.php
Normal file
41
sources/app/Census/CensusColumnConditionFrenchGarcon.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?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 = '';
|
||||||
|
}
|
41
sources/app/Census/CensusColumnConditionFrenchHomme.php
Normal file
41
sources/app/Census/CensusColumnConditionFrenchHomme.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?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 = '';
|
||||||
|
}
|
41
sources/app/Census/CensusColumnConditionFrenchVeuf.php
Normal file
41
sources/app/Census/CensusColumnConditionFrenchVeuf.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?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 = '';
|
||||||
|
}
|
41
sources/app/Census/CensusColumnConditionFrenchVeuve.php
Normal file
41
sources/app/Census/CensusColumnConditionFrenchVeuve.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?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';
|
||||||
|
}
|
41
sources/app/Census/CensusColumnConditionUs.php
Normal file
41
sources/app/Census/CensusColumnConditionUs.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?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';
|
||||||
|
}
|
41
sources/app/Census/CensusColumnFatherBirthPlace.php
Normal file
41
sources/app/Census/CensusColumnFatherBirthPlace.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?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 '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
sources/app/Census/CensusColumnFatherBirthPlaceSimple.php
Normal file
35
sources/app/Census/CensusColumnFatherBirthPlaceSimple.php
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<?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));
|
||||||
|
}
|
||||||
|
}
|
41
sources/app/Census/CensusColumnFatherForeign.php
Normal file
41
sources/app/Census/CensusColumnFatherForeign.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?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 '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
69
sources/app/Census/CensusColumnFullName.php
Normal file
69
sources/app/Census/CensusColumnFullName.php
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
}
|
44
sources/app/Census/CensusColumnGivenNameInitial.php
Normal file
44
sources/app/Census/CensusColumnGivenNameInitial.php
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<?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 '';
|
||||||
|
}
|
||||||
|
}
|
39
sources/app/Census/CensusColumnGivenNames.php
Normal file
39
sources/app/Census/CensusColumnGivenNames.php
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<?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 '';
|
||||||
|
}
|
||||||
|
}
|
62
sources/app/Census/CensusColumnInterface.php
Normal file
62
sources/app/Census/CensusColumnInterface.php
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
<?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();
|
||||||
|
}
|
45
sources/app/Census/CensusColumnMarriedWithinYear.php
Normal file
45
sources/app/Census/CensusColumnMarriedWithinYear.php
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<?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 '';
|
||||||
|
}
|
||||||
|
}
|
42
sources/app/Census/CensusColumnMonthIfBornWithinYear.php
Normal file
42
sources/app/Census/CensusColumnMonthIfBornWithinYear.php
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<?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 '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
46
sources/app/Census/CensusColumnMonthIfMarriedWithinYear.php
Normal file
46
sources/app/Census/CensusColumnMonthIfMarriedWithinYear.php
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<?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 '';
|
||||||
|
}
|
||||||
|
}
|
41
sources/app/Census/CensusColumnMotherBirthPlace.php
Normal file
41
sources/app/Census/CensusColumnMotherBirthPlace.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?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 '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
sources/app/Census/CensusColumnMotherBirthPlaceSimple.php
Normal file
35
sources/app/Census/CensusColumnMotherBirthPlaceSimple.php
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<?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));
|
||||||
|
}
|
||||||
|
}
|
41
sources/app/Census/CensusColumnMotherForeign.php
Normal file
41
sources/app/Census/CensusColumnMotherForeign.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?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 '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
64
sources/app/Census/CensusColumnNationality.php
Normal file
64
sources/app/Census/CensusColumnNationality.php
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
sources/app/Census/CensusColumnNull.php
Normal file
35
sources/app/Census/CensusColumnNull.php
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<?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 '';
|
||||||
|
}
|
||||||
|
}
|
39
sources/app/Census/CensusColumnOccupation.php
Normal file
39
sources/app/Census/CensusColumnOccupation.php
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<?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 '';
|
||||||
|
}
|
||||||
|
}
|
42
sources/app/Census/CensusColumnRelationToHead.php
Normal file
42
sources/app/Census/CensusColumnRelationToHead.php
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<?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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
sources/app/Census/CensusColumnRelationToHeadGerman.php
Normal file
42
sources/app/Census/CensusColumnRelationToHeadGerman.php
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<?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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
sources/app/Census/CensusColumnReligion.php
Normal file
37
sources/app/Census/CensusColumnReligion.php
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<?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 '';
|
||||||
|
}
|
||||||
|
}
|
48
sources/app/Census/CensusColumnSexMF.php
Normal file
48
sources/app/Census/CensusColumnSexMF.php
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
<?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 '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
sources/app/Census/CensusColumnSexMK.php
Normal file
24
sources/app/Census/CensusColumnSexMK.php
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<?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';
|
||||||
|
}
|
24
sources/app/Census/CensusColumnSexMZ.php
Normal file
24
sources/app/Census/CensusColumnSexMZ.php
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<?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 = 'Ž';
|
||||||
|
}
|
39
sources/app/Census/CensusColumnSurname.php
Normal file
39
sources/app/Census/CensusColumnSurname.php
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<?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 '';
|
||||||
|
}
|
||||||
|
}
|
41
sources/app/Census/CensusColumnSurnameGivenNameInitial.php
Normal file
41
sources/app/Census/CensusColumnSurnameGivenNameInitial.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?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 CensusColumnSurnameGivenNameInitial extends CensusColumnFullName {
|
||||||
|
/**
|
||||||
|
* 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());
|
||||||
|
$given = $name['givn'];
|
||||||
|
if (strpos($given, ' ') === false) {
|
||||||
|
return $name['surn'] . ', ' . $given;
|
||||||
|
} else {
|
||||||
|
return $name['surn'] . ', ' . substr($given, 0, strpos($given, ' ') + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue