Merge branch 'dev' into issue-46

This commit is contained in:
Ray 2023-03-02 08:54:59 -05:00
commit 0530eaafa4
17 changed files with 1886 additions and 143 deletions

View file

@ -1,159 +1,125 @@
6077 friendica
4101 zotlabs
2470 redmatrix
2087 Mario Vavti
2044 nobody
1950 Friendika
560 Thomas Willingham
527 Mike Macgirvin
438 marijus
411 Fabio Comuni
402 Andrew Manning
365 Simon L'nu
310 git-marijus
282 jeroenpraat
268 Tobias Diekershoff
245 zottel
234 RedMatrix
213 Haakon Meland Eriksen
189 fabrixxm
173 tommy tomson
144 Simon
140 Stefan Parviainen
128 Christian Vogeley
124 Klaus Weidenbach
9 Sebastian Egbers
9 RedSwede
9 p.tacconi
9 Olivier van Helden
9 Martin Schmitt
9 gia vec
8 tony baldwin
8 Erkan Yilmaz
7 Wave72
7 Oliver
7 DM42.Net (Matt Dent)
79 Habeas Codice
78 root
77 Papa Dragon
76 Michael Vogel
76 Jeroen
71 Mario
6 Tazman DeVille
6 royalterra
6 M. Dent
6 Bob Mottram
69 erik
68 Paolo Tacconi
65 Papa Dragon
64 Zach Prezkuta
61 mrjive
60 Paolo T
6077 friendica
5 Waitman Gobble
5 Tobias Luther
5 sirius
5 ilu33
5 DM42.Net Zap Dev
5 Charles
57 tomtom84
560 Thomas Willingham
5 23n
50 Galette Saucisse
4 Your Name
4 Tony Baldwin
4 Florian Steinel
4 DM42.Net Hubzilla Development
48 hubzilla
47 Max Kostikov
47 Manuel Jiménez Friaza
45 ken restivo
45 Alexandre Hannud Abdo
44 tuscanhobbit
438 marijus
42 Michael Meer
411 Fabio Comuni
4101 zotlabs
40 zzottel
402 Andrew Manning
3 wave72
3 toclimb
3 socialatm
3 msooon
3 MicMee
3 Mathieu "Matbac" Bacou
3 Keith Fernie
3 Hauke Zuehl
3 fadelkon
3 Fabrixxm
3 EinerVonVielen
3 Daniel Frank
3 chris1315
3 Ben Roberts
3 astabski
3 anaqreon
39 Michael
37 Tobias Hößl
37 Andrzej Budziński
37 Abinoam P. Marques Jr
36 Olaf Conradi
36 Alexander Kampmann
365 Simon L'nu
35 zot
32 tobiasd
31 Zot
310 git-marijus
30 Treer
30 Devlon Duthie
2 www-data
2 U-SOUND\mike
2 tonnerkiller
2 Sergey Lukin
2 nostupidzone
2 mike
2 Michal Supler
2 mcnesium
2 Matthew Exon
2 Julian Schweinsberg
2 jeroen
2 Herbert Thielen
2 einervonvielen
2 dragondaddy
29 habeascodice
29 Einer von Vielen
28 Zvi ben Yaakov (a.k.a rdc)
28 Wave
282 jeroenpraat
26 Olivier Migeot
268 Tobias Diekershoff
25 phellmes
24 Michael Johnston
24 M.Dent
2470 redmatrix
245 zottel
23 pafcu
234 RedMatrix
22 Chris Case
21 Klaus
2186 nobody
213 Haakon Meland Eriksen
20 olivierm
19 kostikov
19 Vasudev Kamath
19 Thomas
19 Jeroen van Riet Paap
18 pixelroot
17 Hubzilla
16 Erik Lundin
15 mjfriaza
15 cvogeley
14 sasiflo
12 duthied
12 Max Weller
10 fermionic
10 Abinoam P. Marques Jr.
9 p.tacconi
9 gia vec
9 Sebastian Egbers
9 RedSwede
9 Olivier van Helden
9 Martin Schmitt
8 tony baldwin
8 Erkan Yilmaz
7 Wave72
7 Oliver
7 DM42.Net (Matt Dent)
6 royalterra
6 Tazman DeVille
6 M. Dent
6 Bob Mottram
5 sirius
5 ilu33
5 Waitman Gobble
5 Tobias Luther
5 DM42.Net Zap Dev
5 Charles
5 23n
4 Your Name
4 Tony Baldwin
4 OJ Random
4 Florian Steinel
4 DM42.Net Hubzilla Development
3 wave72
3 toclimb
3 streams
3 msooon
3 fadelkon
3 chris1315
3 astabski
3 anaqreon
3 MicMee
3 Mathieu "Matbac" Bacou
3 Keith Fernie
3 Hauke Zuehl
3 Fabrixxm
3 EinerVonVielen
3 Daniel Frank
3 Ben Roberts
2 www-data
2 tonnerkiller
2 nostupidzone
2 mike
2 mcnesium
2 jeroen
2 einervonvielen
2 U-SOUND\mike
2 Sergey Lukin
2 Michal Supler
2 Matthew Exon
2 Julian Schweinsberg
2 Herbert Thielen
2087 Mario Vavti
1 ZotSocial Admin
1 xxx
1 tonybaldwin
1 superdragon
1 ndurchx
1 mycocham
1 maase2
1 ike
1 goofy-bz
1 felixgilles
1 dsp1986
1 bufalo1973
1 aweiher
1 antil0pa
1 anmol
1 albigro
1 ZotSocial Admin
1 Thomas Citharel
1 Template builder
1 superdragon
1 Stefan Krauth
1 Stanislav Lechev [0xAF]
1 RedMatrixCanada
@ -163,18 +129,55 @@
1 Olivier van Helden (hal)
1 Oliver Lorenz
1 Oda
1 ndurchx
1 mycocham
1 M.Dent (DM42.Net)
1 Matthew Dent
1 Mathieu "Thrar" Bacou
1 Marek Lach
1 Manuel Tancoigne
1 M.Dent (DM42.Net)
1 maase2
1 Ludovic Grossard
1 ike
1 Heiko Förster
1 HappyPony
1 goofy-bz
1 FreeToBe.Social Developer
1 Felix Wolfsteller
1 felixgilles
1 dsp1986
1 DM42 - Usezot
1 Christian Drechsler
1 bufalo1973
1 aweiher
1 Antoine G
1 antil0pa
1 anmol
1 albigro
1 Adam Robertson
1
19 Vasudev Kamath
19 Thomas
19 kostikov
19 Jeroen van Riet Paap
1950 Friendika
18 pixelroot
189 fabrixxm
17 Hubzilla
173 tommy tomson
16 (streams)
16 Erik Lundin
15 mjfriaza
15 cvogeley
1551 Mike Macgirvin
14 sasiflo
144 Simon
140 Stefan Parviainen
13 OJ Random
12 Ray
12 Max Weller
12 duthied
128 Christian Vogeley
124 Klaus Weidenbach
10 streams
10 fermionic
10 Abinoam P. Marques Jr.

View file

@ -157,7 +157,7 @@ class Apps
public static function check_install_system_app($app)
{
if ((!is_array(self::$available_apps)) || (!count(self::$available_apps))) {
if (empty(self::$available_apps)) {
return true;
}
$notfound = true;
@ -203,7 +203,7 @@ class Apps
}
}
}
if (!$installed && in_array($app['name'], self::$base_apps)) {
if (!$installed && is_array(self::$base_apps) && in_array($app['name'], self::$base_apps)) {
return true;
}
return false;
@ -315,9 +315,9 @@ class Apps
break;
default:
if ($config) {
$unset = ((get_config('system', $require[0]) == $require[1]) ? false : true);
$unset = !((get_config('system', $require[0]) == $require[1]));
} else {
$unset = ((local_channel() && Features::enabled(local_channel(), $require)) ? false : true);
$unset = !((local_channel() && Features::enabled(local_channel(), $require)));
}
if ($unset) {
unset($ret);
@ -560,9 +560,9 @@ class Apps
break;
default:
if ($config) {
$unset = ((get_config('system', $require[0]) === $require[1]) ? false : true);
$unset = !((get_config('system', $require[0]) === $require[1]));
} else {
$unset = (($channel_id && Features::enabled($channel_id, $require)) ? false : true);
$unset = !(($channel_id && Features::enabled($channel_id, $require)));
}
if ($unset) {
return '';
@ -610,8 +610,8 @@ class Apps
$featured = $pinned = false;
if (isset($papp['categories'])) {
$featured = ((str_contains($papp['categories'], 'nav_featured_app')) ? true : false);
$pinned = ((str_contains($papp['categories'], 'nav_pinned_app')) ? true : false);
$featured = str_contains($papp['categories'], 'nav_featured_app');
$pinned = str_contains($papp['categories'], 'nav_pinned_app');
}
return replace_macros(Theme::get_template('app.tpl'), [
@ -626,12 +626,12 @@ class Apps
'$undelete' => (($channel_id && $installed && $mode === 'edit') ? t('Undelete') : ''),
'$settings_url' => (($channel_id && $installed && $mode === 'list' && isset($papp['settings_url'])) ? $papp['settings_url'] : ''),
'$deleted' => ((isset($papp['deleted'])) ? intval($papp['deleted']) : false),
'$feature' => (((isset($papp['embed']) && $papp['embed']) || $mode === 'edit') ? false : true),
'$pin' => (((isset($papp['embed']) && $papp['embed']) || $mode === 'edit') ? false : true),
'$feature' => !(((isset($papp['embed']) && $papp['embed']) || $mode === 'edit')),
'$pin' => !(((isset($papp['embed']) && $papp['embed']) || $mode === 'edit')),
'$featured' => $featured,
'$pinned' => $pinned,
'$navapps' => (($mode === 'nav') ? true : false),
'$order' => (($mode === 'nav-order' || $mode === 'nav-order-pinned') ? true : false),
'$navapps' => $mode === 'nav',
'$order' => $mode === 'nav-order' || $mode === 'nav-order-pinned',
'$mode' => $mode,
'$add' => t('Add to app-tray'),
'$remove' => t('Remove from app-tray'),
@ -834,7 +834,7 @@ class Apps
$r = $filter_arr['installed'];
}
return (($r) ? true : false);
return (bool)$r;
}
public static function addon_app_installed($uid, $app, $bypass_filter = false)
@ -855,7 +855,7 @@ class Apps
$r = $filter_arr['installed'];
}
return (($r) ? true : false);
return (bool)$r;
}
public static function system_app_installed($uid, $app, $bypass_filter = false)
@ -876,7 +876,7 @@ class Apps
$r = $filter_arr['installed'];
}
return (($r) ? true : false);
return (bool)$r;
}
public static function app_list($uid, $deleted = false, $cats = [])
@ -945,7 +945,6 @@ class Apps
$x = (($uid) ? get_pconfig($uid, 'system', $conf) : get_config('system', $conf));
if (($x) && (!is_array($x))) {
$y = explode(',', $x);
$y = array_map('trim', $y);
$x = $y;
}
@ -961,20 +960,20 @@ class Apps
}
}
foreach ($apps as $ap) {
if (!self::find_app_in_array($ap['name'], $ret)) {
if (!self::find_app_in_array($ap['guid'], $ret)) {
$ret[] = $ap;
}
}
return $ret;
}
public static function find_app_in_array($name, $arr)
public static function find_app_in_array($guid, $arr)
{
if (!$arr) {
return false;
}
foreach ($arr as $x) {
if ($x['name'] === $name) {
if ($x['guid'] === $guid) {
return $x;
}
}
@ -1022,7 +1021,7 @@ class Apps
$narr = [];
foreach ($syslist as $x) {
$narr[] = $x['name'];
$narr[] = $x['guid'];
}
set_pconfig($uid, 'system', $conf, implode(',', $narr));
@ -1067,7 +1066,7 @@ class Apps
$narr = [];
foreach ($syslist as $x) {
$narr[] = $x['name'];
$narr[] = $x['guid'];
}
set_pconfig($uid, 'system', $conf, implode(',', $narr));
@ -1383,6 +1382,7 @@ class Apps
return chunk_split(base64_encode(json_encode($papp)), 72, "\n");
}
/** @noinspection PhpUnused */
static public function get_papp($app) {
$r = q("select * from app where app_id = '%s' and app_channel = 0 limit 1",
@ -1393,7 +1393,6 @@ class Apps
$papp = self::app_encode(array_shift($r));
return $papp;
}
return false;
}

View file

@ -796,6 +796,11 @@ class Ping extends Controller
if ((!$my_activity) && (!(intval($result['home']) + intval($result['stream']) + intval($result['pubs'])))) {
// PConfig storage for seen_items is common across all sessions.
// In order to reduce conflicts when multiple sessions are active,
// only perform PConfig deletion if there are seen items in
// *this* session.
if ($_SESSION['seen_items']) {
$_SESSION['seen_items'] = [];
PConfig::Delete(local_channel(), 'system', 'seen_items');

View file

@ -0,0 +1,45 @@
# How to install your website
## Disclaimer
- This script does work with Debian GNU/Linux 11 only.
- This script has to be used on a fresh debian install only (it does not take account for a possibly already installed and configured webserver or sql implementation). You may use it to install more than one website on the same computer as long as you use a single webserver.
## First step: setting up your system
After logging into your brand new Debian you will need to run the following commands (as root):
apt-get install git
Git will allow you to download the necessary software from the Streams repository.
mkdir -p /var/www
cd /var/www
This first creates the directory where your web server will find the install folder of your website, then goes to the directory.
git clone https://codeberg.org/streams/streams.git mywebsite
This will download all the software you need in /var/www/mywebsite. You can replace "mywebsite" with any name you like (which you'll have to do if you plan to have more than one website running on your system)
cd mywebsite/contrib/autoinstall
This brings you to the subfolder where the tools are available, i.e. the setup script.
## Next step: installing your website
Simply run the setup script:
./autoinstall.sh
A series of dialog boxes will appear, in which you can enter the necessary information.
Using Nginx as the webserver is not the best choice if you plan to clone or import an existing channel hosted on another website: it will most likely not work.
On a freshly installed Debian server, there are only four mandatory settings you need to provide: your domain name, your e-mail address, the webserver your will be using (Apache or Nginx), a password for your website's database (if you choose to use a randomly generated password, remember you'll have to use it a little later, so take note of it when the install summary is displayed). Once everything is ready, the actual install process will begin. You should not have anything else to do until your website is installed.
## Final step
Open your domain with a browser and step throught the initial configuration of your website. You will need to re-enter a few settings (database name, user and password, admin e-mail…). You will then create your first user, starting with the admin is a great idea.
And thats it, you can now log in your website and start adding content to it!

View file

@ -0,0 +1,52 @@
# Autoinstall setup script
## This installation script was provided by the community and is officially unsupported. Use at your own risk. Only use with a fresh install of Debian GNU//Linux stable. If you have a server that has Apache, PHP, MySQL, SSL, etc. already installed this may break your server.
Here you will find a quick and easy way to set up a website capable of joining the fediverse, using software from the Streams repository. All you have to do is run the setup script, enter some information and the magic will happen. Check the [INSTALL.md](INSTALL.md) file for step-by-step instructions.
## Requirements
Before you start, make sure you have the following:
- A computer/server running a freshly installed Debian GNU/Linux system, on which you can use a command line terminal. It can be a mini-PC or a Raspberry Pi at home, a dedicated server or a VPS.
- A domain name pointing to this computer/server (a few Dynamic DNS providers can automatically be configured as you will read below). You can register a free subdomain with providers such as FreeDNS or NoIP, or buy a domain elsewhere.
- Ports 80 & 443 open on your firewall, forwarded to your computer/server if you use an IPv4 internet connection through a router (i.e. your ISP router at home).
## What the setup script will do for you:
+ Install everything required by your website, basically a web server (Apache or Nginx), PHP, a database server (MariaDB/MySQL), certbot (to obtain Lets Encrypt SSL certificates),
+ Create a database for your website
+ Run certbot to have a secure connection (http*s*)
+ Create a script for daily maintenance:
- renew certfificate (Lets Encrypt)
- update of your website software (git)
- update of your Debian operating system
- restart your computer/server
+ Create cron jobs for
- Run.php for your website every 10 minutes
- daily maintenance script every day at 05:30
- dynamic DNS (works with FreeDNS, Gandi or selfHOST) every 5 minutes
## Some more details
### Dynamic DNS configuration
If you plan to run your website on a computer at home, you may have to deal with the fact that your internet provider doesnt offer a fixed IP address. The setup script has extensions for 3 Dynamic DNS (DDNS) providers, which can help you ensure that your domain name will point to your computer/server even if its IP address changes:
- FreeDNS (freedns.afraid.org) is a free of charge provider that offers free subdomains. You simply need to open an account there and create your subdomain (theres plenty of domains you can choose from). Once your subdomain is created, you will need to find the update key you will use during install.
- Gandi.net is a french domain name registrar that has a nice API for DDNS (Gandi LiveDNS). If you buy a domain there, you can generate an API key for your account that can be used during install.
- selfHOST.de is a german (and german speaking only) registrar. If you have an account and buy a domain there, you will need to provide an ID & password to use the setup scripts DDNS configuration.
### Note on Rasperry Pi install
It is recommended to run the Raspi without graphical frontend. Use the following command to boot the Raspi in console mode only:
sudo raspi-config
*Dont forget to change the default password for user pi!*
## Help wanted
Using Nginx as the webserver is not the best choice if you plan to clone or import a channel currently hosted on another website. The Nginx and/or PHP configuration files probably need some tweaking to have this feature correctly working. If you feel you could help solve this, feel free to contribute.

View file

@ -0,0 +1,501 @@
#!/bin/bash
#
# How to use
# ----------
#
# This file automates the installation of your website using the Streams repository
# (https://codeberg.org/streams/streams), on a freshly installed Debian GNU/Linux 11
# ("Bullseye") server.
#
# 1) Switch to user "root" by typing "su -"
#
# 2) Run with "./autoinstall.sh"
# If this fails check if you can' execute the script:
# - To make it executable type "chmod +x autoinstall.sh"
# - or run "bash autoinstall.sh"
#
# 3) You will be asked you to provide the domain name of your website to be installed and
# database settings you wish to use for it.
#
# On a freshly installed Debian GNU/Linux server, you will be asked for an email address
# (for your Let's Encrypt certificate). You will also be able to configure a Dynamic DNS
# provider (three choices available for the moment).
#
# Once all the necessary info is provided, the install will begin.
#
#
# What does this script do basically?
# -----------------------------------
#
# This file automates the installation of a Nomad/ActivityPub federation capable website
# under Debian GNU/Linux. It will:
# - install (if needed)
# * php8.2,
# * MariaDB (mysql),
# * composer,
# * Nginx or Apache as webserver
# * Let's Encrypt certbot
# - add and configure a certificate for your website domain name
# - add and configure a mysql/MariaDB database for your website
# - configure cron
# * "Run.php" for regular background processes of your website
# * Updates of your website using git to pull from the Streams repository
# * optionally run command to keep the IP up-to-date (if you use a Dynamic DNS provider)
#
#
#
# More information
# ----------------
#
# - You may run the script to install more than one website on the same server.
# - You can try to run the script on a Debian server with stuff already installed
# on it. It might break EVERYTHING though, so use at you own risk.
#
# Credits
# -------
#
# The script is derived from the easyinstall script of the Streams repository, which is based on
# - Tom Wiedenhöfts (OJ Random) script homeinstall (for Hubzilla, ZAP,...) that was based on
# - Thomas Willinghams script "debian-setup.sh" which he used to install the red#matrix.
#
# The documentation for bash is here
# https://www.gnu.org/software/bash/manual/bash.html
#
function check_sanity {
# Do some sanity checking.
print_info "Sanity check..."
if [ $(/usr/bin/id -u) != "0" ]
then
die 'Must be run by root user'
fi
if [ -f /etc/lsb-release ]
then
die "Distribution is not supported"
fi
if [ ! -f /etc/debian_version ]
then
die "Debian is supported only"
fi
if [[ -z "$(grep 'Linux 11' /etc/issue)" ]]
then
die "You can only run this script on a Debian GNU/Linux 11 server"
else
system=debian
print_info "Running the autoinstall script on a Debian GNU/Linux 11 server"
fi
}
function die {
# We remove the website's apache conf files if they exist
if [ ! -z $vhost_added ]
then
a2dissite $domain_name.conf*
rm -f /etc/apache2/sites-available/$domain_name*
systemctl reload apache2
print_info "We delete apache conf files"
fi
# We remove the website's nginx conf files if they exist
if [ ! -z $nginx_conf ]
then
rm -f /etc/nginx/sites-available/$domain_name* /etc/nginx/sites-enabled/$domain_name*
systemctl reload nginx
print_info "We delete nginx conf files"
fi
# We delete database and database user if they exist
if [ ! -z $db_installed ] || [[ ! -z $(mysql -h localhost -u root $opt_mysqlpass -e "SHOW DATABASES;" | grep -w "$website_db_name") ]]
then
mysql -h localhost -u root $opt_mysqlpass -e "DROP DATABASE $website_db_name; DROP USER $website_db_user@localhost;"
print_info "We delete the \"$website_db_name\" database and \"$website_db_user\" database user"
fi
# We remove the addons if they were downloaded
if [ ! -z $addons_installed ]
then
rm -rf $install_path/extend/addon/zaddons
rm -rf $install_path/addon/*
print_info "We delete the addons installed during the install attempt"
fi
# We remove the website's daily update script if it exists
if [ ! -z $daily_update_exists ]
then
rm -f /var/www/$daily_update
print_info "We delete the daily update script"
fi
# We remove .htconfig.php if it exists
if [ -f $install_path/.htconfig.php ]
then
rm -f $install_path/.htconfig.php ]
print_info "We delete .htconfig.php"
fi
# We change ownership of the directory back to root so we can try another install
chown -R root:root $install_path
echo -n -e '\e[1;31m'
echo "ERROR: $1" > /dev/null 1>&2
echo -e '\e[0m'
exit 1
}
function check_install {
if [ -z "`which "$1" 2>/dev/null`" ]
then
# export DEBIAN_FRONTEND=noninteractive ... answers from the package
# configuration database
# - q ... without progress information
# - y ... answer interactive questions with "yes"
# DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends -q -y install $2
DEBIAN_FRONTEND=noninteractive apt-get -q -y install $2
print_info "installed $2 installed for $1"
else
print_warn "$2 already installed"
fi
}
function nocheck_install {
declare DRYRUN=$(DEBIAN_FRONTEND=noninteractive apt-get install --dry-run $1 | grep Remv | sed 's/Remv /- /g')
if [ -z "$DRYRUN" ]
then
# export DEBIAN_FRONTEND=noninteractive ... answers from the package configuration database
# - q ... without progress information
# - y ... answer interactive questions with "yes"
# DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends -q -y install $2
# DEBIAN_FRONTEND=noninteractive apt-get --install-suggests -q -y install $1
DEBIAN_FRONTEND=noninteractive apt-get -q -y install $1
print_info "installed $1"
else
print_info "Did not install $1 as it would require removing the following:"
print_info "$DRYRUN"
die "It seems you are not running this script on a fresh Debian GNU/Linux install. Please consider another installation method."
fi
}
function print_info {
echo -n -e '\e[1;34m'
echo -n $1
echo -e '\e[0m'
}
function print_warn {
echo -n -e '\e[1;31m'
echo -n $1
echo -e '\e[0m'
}
function add_nginx_conf {
print_info "adding nginx conf files"
sed "s|SERVER_NAME|${domain_name}|g;s|INSTALL_PATH|${install_path}|g;s|SERVER_LOG|${domain_name}.log|;s|DOMAIN_CERT|${cert}|;s|CERT_KEY|${cert_key}|;" nginx-server.conf.template >> /etc/nginx/sites-available/${domain_name}.conf
ln -s /etc/nginx/sites-available/${domain_name}.conf /etc/nginx/sites-enabled/
nginx_conf=yes
systemctl restart nginx
}
function install_imagemagick {
if [[ -z "$(which convert)" ]]
then
print_info "installing imagemagick..."
nocheck_install "imagemagick"
fi
}
function php_version {
# We check that we can install the required version (8.2),
print_info "checking that we can install the required PHP version (8.2)..."
check_php=$(apt-cache show php8.2 | grep 'No packages found')
if [ -z "$check_php" ]
then
print_info "We're good!"
else
die "something went wrong, we can't install php8.2."
fi
}
function install_php {
if [[ -z "$(which php-fpm8.2)" ]]
then
print_info "installing php8.2..."
if [[ $webserver == "nginx" ]]
then
nocheck_install "php8.2-fpm php8.2 php8.2-mysql php-pear php8.2-curl php8.2-gd php8.2-mbstring php8.2-xml php8.2-zip"
sed -i "s/^upload_max_filesize =.*/upload_max_filesize = 100M/g" /etc/php/8.2/fpm/php.ini
sed -i "s/^post_max_size =.*/post_max_size = 100M/g" /etc/php/8.2/fpm/php.ini
systemctl restart php8.2-fpm
print_info "php8.2 was installed."
elif [[ $webserver == "apache" ]]
then
nocheck_install "libapache2-mod-php php php-mysql php-pear php-curl php-gd php-mbstring php-xml php-zip"
phpversion=$(php -v|grep --only-matching --perl-regexp "(PHP )\d+\.\\d+\.\\d+"|cut -c 5-7)
sed -i "s/^upload_max_filesize =.*/upload_max_filesize = 100M/g" /etc/php/$phpversion/apache2/php.ini
sed -i "s/^post_max_size =.*/post_max_size = 100M/g" /etc/php/$phpversion/apache2/php.ini
print_info "php ${phpversion} was installed"
fi
fi
}
function install_composer {
print_info "We check if Composer is already installed"
if [ ! -f /usr/local/bin/composer ]
then
EXPECTED_CHECKSUM="$(php -r 'copy("https://composer.github.io/installer.sig", "php://stdout");')"
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
ACTUAL_CHECKSUM="$(php -r "echo hash_file('sha384', 'composer-setup.php');")"
if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ]
then
>&2 echo 'ERROR: Invalid installer checksum'
rm composer-setup.php
die 'ERROR: Invalid installer checksum'
fi
php composer-setup.php --quiet
RESULT=$?
rm composer-setup.php
# exit $RESULT
# We install Composer globally
mv composer.phar /usr/local/bin/composer
print_info "Composer was successfully installed."
else
print_info "Composer is already installed on this system."
fi
}
function create_website_db {
print_info "creating website's database..."
if [ -z "$website_db_name" ]
then
website_db_name=$install_folder
fi
if [ -z "$website_db_user" ]
then
website_db_user=$install_folder
fi
if [ -z "$website_db_pass" ]
then
die "website_db_pass not set in $configfile"
fi
# Make sure we don't write over an already existing database if we install more one website
if [[ -z $(mysql -h localhost -u root $opt_mysqlpass -e "SHOW DATABASES;" | grep -w "$website_db_name") ]]
then
if [[ -z $(mysql -h localhost -u root $opt_mysqlpass -e "use mysql; SELECT user FROM user;" | grep -w "$website_db_user") ]]
then
Q1="CREATE DATABASE IF NOT EXISTS $website_db_name;"
Q2="GRANT USAGE ON *.* TO $website_db_user@localhost IDENTIFIED BY '$website_db_pass';"
Q3="GRANT ALL PRIVILEGES ON $website_db_name.* to $website_db_user@localhost identified by '$website_db_pass';"
Q4="FLUSH PRIVILEGES;"
SQL="${Q1}${Q2}${Q3}${Q4}"
mysql -h localhost -uroot $opt_mysqlpass -e "$SQL"
db_installed=yes
else
die "database user named \"$website_db_user\" already exists..."
fi
else
die "database named \"$website_db_name\" already exists..."
fi
}
function ping_domain {
print_info "ping domain $domain..."
# Is the domain resolved? Try to ping 6 times à 10 seconds
COUNTER=0
for i in {1..6}
do
print_info "loop $i for ping -c 1 $domain_name ..."
if ping -c 4 -W 1 $domain_name
then
print_info "$domain_name resolved"
break
else
if [ $i -gt 5 ]
then
die "Failed to: ping -c 1 $domain_name not resolved."
fi
fi
sleep 10
done
sleep 5
}
function check_https {
print_info "checking httpS > testing ..."
url_https=https://$domain_name
wget_output=$(wget -nv --spider --max-redirect 0 $url_https)
if [ $? -ne 0 ]
then
print_warn "check not ok"
else
print_info "check ok"
fi
}
function repo_name {
# We keep this in case the repository is forked in the future
if git remote -v | grep -i "origin.*streams.*"
then
repository=streams
# elif git remote -v | grep -i "origin.*fork_1.*"
# then
# repository=fork_1
# elif git remote -v | grep -i "origin.*fork_2.*"
# then
# repository=fork_2
else
die "this script is not usable with this repository"
fi
}
function install_website {
cd $install_path/
# Pull in external libraries with composer. Leave off the --no-dev
# option if you are a developer and wish to install addditional CI/CD tools.
COMPOSER_ALLOW_SUPERUSER=1 /usr/local/bin/composer install --no-dev
# We install addons
# We'll keep stuff here for possible future forks so that the script can be the same
print_info "installing addons..."
if [ $repository = "streams" ]
then
print_info "Streams"
if [ ! -d $install_path/extend/addon/zaddons ]
then
util/add_addon_repo https://codeberg.org/streams/streams-addons.git zaddons
else
print_warn "Streams addons already present, we'll remove them"
rm -rf $install_path/extend/addon/zaddons
rm -rf $install_path/addon/*
util/add_addon_repo https://codeberg.org/streams/streams-addons.git zaddons
fi
# elif [ $repository = "fork_1" ]
# then
# print_info "Fork_1"
# util/add_addon_repo ** REPOSITORY HERE **
# elif [ $repository = "fork_2" ]
# then
# print_info "Fork_2"
# util/add_addon_repo **REPOSITORY HERE **
else
die "no addons can be installed for this repository"
fi
mkdir -p "cache/smarty3"
mkdir -p "store"
chmod -R 700 store cache
touch .htconfig.php
chmod ou+w .htconfig.php
cd /var/www/
chown -R www-data:www-data $install_path
chown root:www-data $install_path/
print_info "installed addons"
addons_installed=yes
}
function configure_daily_update {
echo "#!/bin/sh" >> /var/www/$daily_update
echo "#" >> /var/www/$daily_update
echo "# update of $domain_name federation capable website" >> /var/www/$daily_update
echo "echo \"\$(date) - updating core and addons...\"" >> /var/www/$daily_update
echo "echo \"reaching git repository for $domain_name $repository hub/instance...\"" >> /var/www/$daily_update
echo "(cd $install_path ; sudo -u www-data util/udall)" >> /var/www/$daily_update
echo "chown -R www-data:www-data $install_path # make all accessible for the webserver" >> /var/www/$daily_update
if [[ $webserver == "apache" ]]
then
echo "chown root:www-data $install_path/.htaccess" >> /var/www/$daily_update
echo "chmod 0644 $install_path/.htaccess # www-data can read but not write it" >> /var/www/$daily_update
fi
chmod a+x /var/www/$daily_update
daily_update_exists=yes
}
function configure_cron_daily {
print_info "configuring cron..."
# every 10 min for Run.php
if [[ -z $(grep "/var/www/$install_folder; php Code/Daemon/Run.php" /etc/crontab) ]]
then
echo "*/10 * * * * www-data cd $install_path; php Code/Daemon/Run.php Cron >> /dev/null 2>&1" >> /etc/crontab
fi
# Run external script daily at 05:30 to update repository core and addon
echo "#!/bin/sh" > /var/www/$cron_job
echo "#" >> /var/www/$cron_job
echo "echo \" \"" >> /var/www/$cron_job
echo "echo \"+++ \$(date) +++\"" >> /var/www/$cron_job
echo "echo \" \"" >> /var/www/$cron_job
echo "echo \"\$(date) - renew certificate...\"" >> /var/www/$cron_job
echo "certbot renew --noninteractive" >> /var/www/$cron_job
echo "#" >> /var/www/$cron_job
echo "echo \"\$(date) - db size...\"" >> /var/www/$cron_job
echo "du -h /var/lib/mysql/ | grep mysql/" >> /var/www/$cron_job
echo "#" >> /var/www/$cron_job
echo "cd /var/www" >> /var/www/$cron_job
echo "for f in *-daily.sh; do \"./\${f}\"; done" >> /var/www/$cron_job
if [[ $system == "debian" ]]
then
echo "echo \"\$(date) - updating Debian GNU/Linux...\"" >> /var/www/$cron_job
echo "apt-get -q -y update && apt-get -q -y dist-upgrade && apt-get -q -y autoremove # update Debian GNU/Linux and upgrade" >> /var/www/$cron_job
echo "echo \"\$(date) - Update finished. Rebooting...\"" >> /var/www/$cron_job
echo "#" >> /var/www/$cron_job
echo "shutdown -r now" >> /var/www/$cron_job
else
echo "echo \"\$(date) - Update finished.\"" >> /var/www/$cron_job
fi
chmod a+x /var/www/$cron_job
# If global cron job does not exist we add it to /etc/crontab
if grep -q $cron_job /etc/crontab
then
echo "cron job already in /etc/crontab"
else
echo "30 05 * * * root /bin/bash /var/www/$cron_job >> /var/www/daily-updates.log 2>&1" >> /etc/crontab
echo "0 0 1 * * root rm /var/www/daily-updates.log" >> /etc/crontab
fi
# This is active after either "reboot" or cron reload"
systemctl restart cron
print_info "configured cron for updates/upgrades"
}
########################################################################
# START OF PROGRAM
########################################################################
export PATH=/bin:/usr/bin:/sbin:/usr/sbin
check_sanity
repo_name
print_info "We're installing a website using the $repository repository"
install_path="$(dirname $(dirname "$(pwd)"))"
if [ "$install_path" == "/var/www/html" ]
then
die "Please don't install your website in /var/www/html."
fi
install_folder="$(basename $install_path)"
domain_regex="^([a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]\.)+[a-zA-Z]{2,}$"
print_info "Now using scripts/dialogs.sh to obtain all necessary settings for the install"
source scripts/dialogs.sh
#set -x # activate debugging from here
if [[ $system == "debian" ]]
then
source scripts/debian.sh
# Scripts for other Debian based distros could be added later
# elif [[ $system == "other_distro" ]]
# then
# source scripts/other_distro.sh
fi
install_imagemagick
install_composer
create_website_db
install_website
daily_update="${domain_name}-daily.sh"
cron_job="cron_job.sh"
configure_daily_update
configure_cron_daily
check_https
# Put a nice message here no confirm the website was successfully installed
#set +x # stop debugging from here

View file

@ -0,0 +1,144 @@
##
# Nginx block configuration template
# based on the example created by Olaf Conradi
#
# The files generated with this template will be added to
# /etc/nginx/sites-available & /etc/nginx/sites-enabled (symlink)
##
##
# You should look at the following URL's in order to grasp a solid understanding
# of Nginx configuration files in order to fully unleash the power of Nginx.
#
# http://wiki.nginx.org/Pitfalls
# http://wiki.nginx.org/QuickStart
# http://wiki.nginx.org/Configuration
##
##
# This configuration assumes
# You filled the zotserver-config.txt file
# Your domain/subdomain is functionnal
# You want all traffic to be https
# You have PHP FastCGI Process Manager (php-fpm) running on localhost
##
server {
listen 80;
listen [::]:80;
server_name SERVER_NAME;
# HTTP > HTTPS #
return 301 https://$server_name$request_uri;
}
##
# Configure Red with SSL
#
# All requests are routed to the front controller
# except for certain known file types like images, css, etc.
# Those are served statically whenever possible with a
# fall back to the front controller (needed for avatars, for example)
##
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name SERVER_NAME;
ssl_certificate DOMAIN_CERT;
ssl_certificate_key CERT_KEY;
ssl_session_timeout 5m;
# DO WE NEED TO REVIEW THE FOLLOWING SETTINGS?
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS;
ssl_prefer_server_ciphers on;
fastcgi_param HTTPS on;
charset utf-8;
root INSTALL_PATH;
index index.php;
access_log /var/log/nginx/SERVER_LOG;
#Uncomment the following line to include a standard configuration file
#Note that the most specific rule wins and your standard configuration
#will therefore *add* to this file, but not override it.
#include standard.conf
# allow uploads up to 128MB in size
client_max_body_size 128m;
client_body_buffer_size 128k;
include mime.types;
# rewrite to front controller as default rule
location / {
if (!-e $request_filename) {
rewrite ^(.*)$ /index.php?req=$1;
}
}
# make sure webfinger and other well known services aren't blocked
# by denying dot files and rewrite request to the front controller
location ^~ /.well-known/ {
allow all;
if (!-e $request_filename) {
rewrite ^(.*)$ /index.php?req=$1;
}
}
# statically serve these file types when possible
# otherwise fall back to front controller
# allow browser to cache them
# added .htm for advanced source code editor library
# location ~* \.(jpg|jpeg|gif|png|ico|css|js|htm|html|map|ttf|woff|woff2|svg)$ {
# expires 30d;
# try_files $uri /index.php?req=$uri&$args;
# }
# SHOULD WE UNCOMMENT THE ABOVE LINES ?
# block these file types
location ~* \.(tpl|md|tgz|log|out)$ {
deny all;
}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
# or a unix socket
location ~* \.php$ {
# IS THE FOLLOWING STILL RELEVANT AS OF AUGUST 2020?
# Zero-day exploit defense.
# http://forum.nginx.org/read.php?2,88845,page=3
# Won't work properly (404 error) if the file is not stored on this
# server, which is entirely possible with php-fpm/php-fcgi.
# Comment the 'try_files' line out if you set up php-fpm/php-fcgi on
# another machine. And then cross your fingers that you won't get hacked.
try_files $uri =404;
# NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
fastcgi_split_path_info ^(.+\.php)(/.+)$;
# With php5-cgi alone:
# fastcgi_pass 127.0.0.1:9000;
# With php-fpm:
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
include fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_read_timeout 600;
}
# deny access to all dot files
location ~ /\. {
deny all;
}
#deny access to store
location ~ /store {
deny all;
}
}

View file

@ -0,0 +1,75 @@
#!/bin/bash
#
#
#########################################################
# WHAT DOES THIS SCRIPT DO ? #
#########################################################
# This script will do two things :
# - Configure your freedns subdomain so that if points to your server's IP address
# - Create a cron job which will change you freedns IP configuration when needed
#
#########################################################
# INSTRUCTIONS #
#########################################################
#
# Get a free subdomain from freedns and use it for your dynamic ip address
#
# - Register for a Free domain at http://freedns.afraid.org/signup/
# - WATCH THIS: Make sure you choose a domain with as less subdomains as
# possible. Why? Let's encrpyt issues a limited count of certificates each
# day. Possible other users of this domain will try to issue a certificate
# at the same day.
# - Logon to FreeDNS (where you just registered)
# - Goto http://freedns.afraid.org/dynamic/
# - Right click on "Direct URL" and copy the URL and paste it somewhere.
# - You should notice a large and unique alpha-numeric key in the URL
# (after the question mark)
#
# http://freedns.afraid.org/dynamic/update.php?alpha-numeric-key
#
# Provided your url from freedns is
#
# http://freedns.afraid.org/dynamic/update.php?U1Z6aGt2R0NzMFNPNWRjbWxxZGpsd093OjE1Mzg5NDE5
#
# Then you have to provide
#
# freedns_key=U1Z6aGt2R0NzMFNPNWRjbWxxZGpsd093OjE1Mzg5NDE5
#
#
#########################################################
# THIS IS WHERE YOU ADD YOUR KEY #
#########################################################
freedns_key=$ddns_key
##########################################################
# DO NOT EDIT AFTER THIS #
##########################################################
function install_run_freedns {
print_info "run freedns (dynamic IP)..."
if [ -z "$freedns_key" ]
then
die "freedns was not started because 'freedns_key' is empty in ddns/freedns.sh"
exit 0
else
wget --no-check-certificate -O - http://freedns.afraid.org/dynamic/update.php?$freedns_key
fi
}
function configure_cron_freedns {
print_info "configure cron for freedns..."
# Use cron for dynamich ip update
# - at reboot
# - every 30 minutes
grep $freedns_key /etc/crontab
if [ $? != 0 ]
then
echo "@reboot root http://freedns.afraid.org/dynamic/update.php?$freedns_key > /dev/null 2>&1" >> /etc/crontab
echo "*/30 * * * * root wget --no-check-certificate -O - http://freedns.afraid.org/dynamic/update.php?$freedns_key > /dev/null 2>&1" >> /etc/crontab
else
print_info "cron for freedns was configured already"
fi
}

View file

@ -0,0 +1,136 @@
#!/bin/bash
#
#
#########################################################
# WHAT DOES THIS SCRIPT DO ? #
#########################################################
#
# This script will do two things :
# - Configure your domain (or subdomain) Gandi.net DNS records so that if points to your server's IP address
# - Create a cron job which will change you Gandi.net DNS records configuration when needed
#
#########################################################
# INSTRUCTIONS #
#########################################################
#
# 1. Register a domain at gandi.net, pricing will depend on the TLD
# (some domains can cost a few EUR/USD/AUD a year others can cost thousands)
#
# 2. Make sure that your domain is configured with Gandi's LiveDNS nameservers
# (it's enabled by default and an option easy to configure)
#
# 3. Get your API key
# * Go to https://account.gandi.net/en/users/_USERNAME_/security
# (replace _USERNAME_ with your Gandi account username)
# * Click on the "(Re)generate the API key" link
# * Copy the API key which will be as pretty as N8Azky2QxZbQhuP6EQXmD58S
# (IMPORTANT : YOU WON'T BE ABLE TO RETRIEVE IT LATER, ONLY GENERATE A NEW ONE)
# * Add you API key in this script
#
# for example: gandi_api_key=N8Azky2QxZbQhuP6EQXmD58S
#
# 4. Set Gandi as your DDNS provider in server-config.txt (.homeinstall folder)
#
# like this: dns_provider=gandi
#
# That way the ddns/gandi.sh (which you're editing) will be run during install
#
# 5. Run server-setup.sh in the .homeinstall folder
#
#########################################################
# THIS IS WHERE YOU ADD YOUR API KEY #
#########################################################
gandi_api_key=$ddns_key
##########################################################
# SECOND LEVEL DOMAIN NAME (SLD) #
##########################################################
#
# As some people may want to buy a domain name with a SLD
# (for instance ending with *.net.au or *.co.uk) we need to
# make sure that it is recognised as such.
#
# Below is a list of some of the most common sld
sld=".com.au,.net.au,.org.au,.com.br,.net.br,.co.jp,.co.uk,.org.uk,.co.za,.eu.com"
#
# If your use a SLD that's not on the list just put it below
#
# for example: sld=.emp.br
# (uncomment the line below if needed)
# sld=
#
##########################################################
# DO NOT EDIT AFTER THIS #
##########################################################
function fqdn_slice {
# We find the domain name which we'll be needing later in the script
main_domain=$(echo $domain_name | awk -F. 'END {print $(NF-1)"."$NF}')
if [ ! -z $(echo $sld | grep .$main_domain) ]
then
main_domain=$(echo $domain_name | awk -F. 'END {print $(NF-2)"."$(NF-1)"."$NF}')
fi
# The subdomain will also be useful
subdomain=${domain_name//\.$main_domain/}
if [ $domain_name == $main_domain ]
then
subdomain="@"
fi
}
function install_run_gandi {
print_info "install and start Gandi LiveDNS (dynamic IP)..."
if [ -z "$gandi_api_key" ]
then
die "Gandi LiveDNS was not started because 'gandi_api_key' is empty in ddns/gandi.sh"
else
# We clone the git repository (if not already present)
# Repository still exists as of March 2022...
if [ ! -d /opt/gandi-automatic-dns ]
then
git clone https://github.com/brianreumere/gandi-automatic-dns.git /opt/gandi-automatic-dns
fi
fi
print_info "First run of Gandi LiveDNS ddns script..."
if [ -z $ip4 ]
then
die "IP address could not be retrieved. Check your internet connection"
else
echo $ip4 | /opt/gandi-automatic-dns/gad -5 -s -a $gandi_api_key -d $main_domain -r "$subdomain"
if [ $? != 0 ]
then
die "Something went wrong, you should check you API key in ddns/gandi.sh"
fi
if [ $ip4 != $ip6 ]
then
echo $ip6 | /opt/gandi-automatic-dns/gad -5 -6 -s -a $gandi_api_key -d $main_domain -r "$subdomain"
fi
fi
}
function configure_cron_gandi {
print_info "configure cron for Gandi LiveDNS..."
# Use cron for dynamich ip update
# - at reboot
# - every 5 minutes
grep "$main_domain".*"$subdomain" /etc/crontab
if [ $? != 0 ]
then
echo "@reboot root curl ip4.me/ip/ | /bin/bash /opt/gandi-automatic-dns/gad -5 -s -a $gandi_api_key -d $main_domain -r \"$subdomain\" > /dev/null 2>&1" >> /etc/crontab
echo "*/5 * * * * root curl ip4.me/ip/ | /bin/bash /opt/gandi-automatic-dns/gad -5 -s -a $gandi_api_key -d $main_domain -r \"$subdomain\" > /dev/null 2>&1" >> /etc/crontab
if [ $ip4 != $ip6 ]
then
echo "@reboot root curl ip6.me/ip/ | /bin/bash /opt/gandi-automatic-dns/gad -5 -6 -s -a $gandi_api_key -d $main_domain -r \"$subdomain\" > /dev/null 2>&1" >> /etc/crontab
echo "*/5 * * * * root curl ip6.me/ip/ | /bin/bash /opt/gandi-automatic-dns/gad -5 -6 -s -a $gandi_api_key -d $main_domain -r \"$subdomain\" > /dev/null 2>&1" >> /etc/crontab
fi
else
print_info "cron for Gandi LiveDNS was configured already"
fi
}
ip4=$(curl ip4.me/ip/)
ip6=$(curl ip6.me/ip/)
fqdn_slice

View file

@ -0,0 +1,78 @@
#!/bin/bash
#
#
#########################################################
# WHAT DOES THIS SCRIPT DO ? #
#########################################################
# This script will do two things :
# - Configure your selfHOST.de domain so that it points to your server's IP address
# - Create a cron job which will change you selfHOST.de IP configuration when needed
#
#########################################################
# INSTRUCTIONS #
#########################################################
#
# 1. Register a domain at selfhost.de
# - choose offer "DOMAIN dynamisch" 1,50€/mon at 04/2019
# 2. Get your configuration for dynamic IP update
# - Log in at selfhost.de
# - go to "DynDNS Accounte"
# - klick "Details" of your (freshly) registered domain
# - You will find the configuration there
# - Benutzername (user name) > use this for "selfhost_user="
# - Passwort (password) > use this for "selfhost_pass="
#
#########################################################
# THIS IS WHERE YOU ADD YOUR CREDENTIALS #
#########################################################
selfhost_user=$ddns_id
selfhost_pass=$ddns_password
##########################################################
# DO NOT EDIT AFTER THIS #
##########################################################
function install_run_selfhost {
print_info "install and start selfhost (dynamic IP)..."
if [ -z "$selfhost_user" ]
then
die "selfHOST was not started because 'selfhost_user' is empty in ddns/selfhost.sh"
elif [ -z "$selfhost_pass" ]
then
die "selfHOST was not started because 'selfhost_pass' is empty in ddns/selfhots.sh"
else
if [ ! -d $selfhostdir ]
then
mkdir $selfhostdir
fi
# the old way
# https://carol.selfhost.de/update?username=123456&password=supersafe
#
# the prefered way
wget --output-document=$selfhostdir/$selfhostscript http://jonaspasche.de/selfhost-updater
echo "router" > $selfhostdir/device
echo "$selfhost_user" > $selfhostdir/user
echo "$selfhost_pass" > $selfhostdir/pass
bash $selfhostdir/$selfhostscript update
fi
}
function configure_cron_selfhost {
print_info "configure cron for selfhost..."
# Use cron for dynamich ip update
# - at reboot
# - every 5 minutes
grep $selfhostscript /etc/crontab
if [ $? != 0 ]
then
echo "@reboot root bash $selfhostdir/$selfhostscript update > /dev/null 2>&1" >> /etc/crontab
echo "*/5 * * * * root /bin/bash $selfhostdir/$selfhostscript update > /dev/null 2>&1" >> /etc/crontab
else
print_info "cron for selfHOST was configured already"
fi
}
selfhostdir=/etc/selfhost
selfhostscript=selfhost-updater.sh

View file

@ -0,0 +1,179 @@
#!/bin/bash
function update_upgrade {
print_info "updated and upgrade..."
# Run through the apt-get update/upgrade first. This should be done before
# we try to install any package
apt-get -q -y update && apt-get -q -y dist-upgrade
print_info "updated and upgraded linux"
}
function install_curl {
if [[ -z "$(which curl)" ]]
then
print_info "installing curl..."
nocheck_install "curl"
fi
}
function install_wget {
if [[ -z "$(which wget)" ]]
then
print_info "installing wget..."
nocheck_install "wget"
fi
}
function install_sendmail {
if [[ -z "$(which sendmail)" ]]
then
print_info "installing sendmail..."
nocheck_install "sendmail sendmail-bin"
fi
}
function install_sury_repo {
# With Debian 11 (bullseye) we need an extra repo to install php 8.*
if [[ -z $(grep -R "deb https://packages.sury.org/php/" /etc/apt/) ]]
then
print_info "installing sury-php repository..."
apt-get -y install apt-transport-https
curl -sSLo /usr/share/keyrings/deb.sury.org-php.gpg https://packages.sury.org/php/apt.gpg
sh -c 'echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/sury-php.list'
apt-get update -y
else
print_info "sury-php repository is already installed."
fi
}
function install_apache {
if [[ -z "$(which apache2)" ]]
then
print_info "installing apache..."
nocheck_install "apache2 apache2-utils"
a2enmod rewrite
systemctl restart apache2
fi
}
function install_nginx {
if [[ -z "$(which nginx)" ]]
then
print_info "installing nginx..."
nocheck_install "nginx"
systemctl restart nginx
fi
}
function add_vhost {
print_info "adding apache vhost"
echo "<VirtualHost *:80>" >> "/etc/apache2/sites-available/${domain_name}.conf"
echo "ServerName ${domain_name}" >> "/etc/apache2/sites-available/${domain_name}.conf"
echo "DocumentRoot $install_path" >> "/etc/apache2/sites-available/${domain_name}.conf"
echo " <Directory $install_path>" >> "/etc/apache2/sites-available/${domain_name}.conf"
echo " AllowOverride All" >> "/etc/apache2/sites-available/${domain_name}.conf"
echo " </Directory>" >> "/etc/apache2/sites-available/${domain_name}.conf"
echo "</VirtualHost>" >> "/etc/apache2/sites-available/${domain_name}.conf"
a2ensite $domain_name
vhost_added=yes
}
function install_letsencrypt {
if [[ -z "$(which certbot)" ]]
then
print_info "installing let's encrypt ..."
# installing certbot via snapd is the preferred method (10/2022) https://certbot.eff.org/instructions
nocheck_install "snapd"
print_info "ensure that version of snapd is up to date..."
snap install core
snap refresh core
print_info "install certbot via snap..."
snap install --classic certbot
ln -s /snap/bin/certbot /usr/bin/certbot
fi
}
function nginx_conf_le {
print_info "run certbot..."
certbot certonly --nginx -d $domain_name -m $le_email --agree-tos --non-interactive
cert="/etc/letsencrypt/live/$domain_name/fullchain.pem"
cert_key="/etc/letsencrypt/live/$domain_name/privkey.pem"
}
function vhost_le {
print_info "run certbot ..."
certbot --apache -w $install_path -d $domain_name -m $le_email --agree-tos --non-interactive --redirect --hsts --uir
service apache2 restart
vhost_le_configured=yes
}
function install_mysql {
if [ ! -z $(which mysql) ]
then
print_info "MariaDB (or MySQL) is already installed"
else
print_info "we install mariadb-server"
nocheck_install "mariadb-server"
systemctl is-active --quiet mariadb && echo "MariaDB is running"
fi
}
# We need to install basic stuff on a fresh install
update_upgrade
install_curl
install_wget
install_sendmail
if [ ! -z $ddns_provider ]
then
source scripts/ddns/$ddns_provider.sh
if [ ! -f dns_cache_fail ]
then
nocheck_install "dnsutils"
install_run_$ddns_provider
fi
if [ -z $(dig -4 $domain_name +short | grep $(curl ip4.me/ip/)) ]
then
touch dns_cache_fail
die "There seems to be a DNS cache issue here, you need to wait a few minutes before running the script again"
fi
fi
ping_domain
# add something here to remove dns_cache_fail ?
if [ ! -z $ddns_provider ]
then
scripts source ddns/$ddns_provider.sh
configure_cron_$ddns_provider
fi
# We install our webserver
if [[ $webserver = "nginx" ]]
then
install_nginx
elif [[ $webserver = "apache" ]]
then
install_apache
fi
# We install the required PHP version
install_sury_repo
php_version
install_php
install_letsencrypt
# We configure our webserver for our website
if [[ $webserver = "nginx" ]]
then
nginx_conf_le
add_nginx_conf
elif [[ $webserver = "apache" ]]
then
add_vhost
vhost_le
fi
# We install our MariaDB server
install_mysql

View file

@ -0,0 +1,261 @@
#!/bin/bash
function script_debut {
# First we check if we're running the script on a freshly installed Debian 11 server
if [[ $system == "debian" ]]
then
if [[ ! -z "$(which php)" ]] || [[ ! -z "$(which mysql)" ]] || [[ ! -z "$(which apache)" ]] || [[ ! -z "$(which nginx)" ]]
then
warning_no_fresh
fi
fi
whiptail \
--title "Start your website installation" \
--msgbox "So, you're ready to install your website? Very little information is required to start the configuration, this should take 2 minutes tops before the proper install can start." \
10 60
exitstatus=$?
if [ $exitstatus = 0 ]
then
enter_domain
else
die "Wokay, come back when you feel ready to test this!"
fi
}
function warning_no_fresh {
if (whiptail \
--title "WARNING: Not a fresh Debian install" \
--yesno "Hi there, you are not running this script on a freshly installed Debian 11 server. If you choose to continue, this might break your system to the point that you won't be able to fix it, and this would be entirely your fault because, you know, we told you so. Do you want to continue anyway?" \
--yes-button "Yes" --no-button "No" \
12 80)
then
print_info "Running the script on your server"
else
print_info "Nothing was installed on your server"
exit 0
fi
}
function enter_domain {
# This is where the domain name is choosed
if [ -z "$inputbox_domain" ]
then
inputbox_domain="Please enter your website's address/domain name\n(i.e. \"mywebsite.example.com\", \"example.com\")"
fi
domain_name=$(whiptail \
--title "Domain name" \
--inputbox "$inputbox_domain" \
12 80 $domain_name 3>&1 1>&2 2>&3)
exitstatus=$?
if [ $exitstatus = 0 ]
then
if [ -z "$domain_name" ]
then
inputbox_domain="You need to put something here otherwise you won't be able to configure a website.\nPlease enter the domain name you plan to use for your website:"
enter_domain
else
# Validate domain name (we check if the input looks like a FQDN of a domain name not that it is an actual one)
if [[ "$domain_name" =~ $domain_regex ]]
then
source scripts/dialogs_debian.sh
else
inputbox_domain="\"$domain_name\" is not a valid address/domain name for your website. Please enter something that looks like \"example.com\" or \"subdomain.example.com\":"
enter_domain
fi
fi
else
# In case the user has a change of mind and presses Esc key
die "Run the script again when you're ready to enter a valid domain name"
fi
}
function enter_db_pass {
# Here we enter the MariaDB main password (will also be used as the default website's DB password)
website_db_pass=$(whiptail \
--title "Set your database password" \
--passwordbox "Enter your website database password and choose \"OK\"continue. If you leave the field empty a random password will be generated, you will be able to retrieve it later." \
--cancel-button "Go Back" \
10 60 $website_db_pass 3>&1 1>&2 2>&3)
exitstatus=$?
if [ $exitstatus = 0 ]
then
if [ -z "$website_db_pass" ]
then
# If no password is entered, we generate a random one
website_db_pass=$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-16};echo;)
fi
advanced_db
else
die "Okay, come back when when you feel like going a little further."
fi
}
function advanced_db {
if (whiptail \
--title "Advanced DB settings" \
--yesno "Default setting is to use the installation folder's name as database name and user (i.e. if your website is installed in /var/www/social, database name and user will be \"social\". Do you wish to keep it that way or to customize those settings (database name and database user)?" \
--yes-button "Keep it simple" --no-button "Customize" \
10 80)
then
website_db_name=$install_folder
website_db_user=$install_folder
db_name_check
db_user_check
summary
else
advanced_db_name
fi
}
function advanced_db_name {
# Here we can set the website database name (if left empty, main script will name it after the install folder's name)
if [ -z "$inputbox_db_name" ]
then
inputbox_db_name="Please enter your website database name, you can only use letters, numbers and \"_\" (do not use spaces). If left empty it will be named as the install folder (here \"$install_folder\" as your install path is \"$install_path\"):"
fi
website_db_name=$(whiptail \
--title "Website database name" \
--inputbox "$inputbox_db_name" \
10 80 $website_db_name 3>&1 1>&2 2>&3)
exitstatus=$?
if [ $exitstatus = 0 ]
then
if [ ! -z "$website_db_name" ]
then
# Validate database name (we want it to be rather simple)
db_name_regex="^([a-zA-Z0-9_]){2,25}$"
if [[ "$website_db_name" =~ $db_name_regex ]]
then
db_name_check
if [ ! -z "$website_db_user" ]
db_user_check
then
advanced_db_user
fi
else
unset website_db_name
inputbox_db_name="Please enter a usable database name (or leave empty to use \"$install_folder\":"
advanced_db_name
fi
else
website_db_name=$install_folder
db_name_check
advanced_db_user
fi
else
# If Esc key is pressed we go back to choosing if advanced DB settings are needed or not
advanced_db
fi
}
function db_name_check {
# Make sure we don't write over an already existing database if we install more than one Streams website with this script
if [[ ! -z $(mysql -h localhost -u root $opt_mysqlpass -e "SHOW DATABASES;" | grep -w "$website_db_name") ]]
then
inputbox_db_name="A database named \"$website_db_name\" already exists, please choose another name:"
unset website_db_name
advanced_db_name
fi
}
function advanced_db_user {
# Here we can set the website database user (if left empty, main script will name it after the install folder's name)
if [ -z "$inputbox_db_user" ]
then
inputbox_db_user="Please enter your website database username, do not use spaces. If left empty it will be named after the install folder (here \"$install_folder\" as your install path is \"$install_path\"):"
fi
website_db_user=$(whiptail \
--title "Website database username" \
--inputbox "$inputbox_db_user" \
10 80 $website_db_user 3>&1 1>&2 2>&3)
exitstatus=$?
if [ $exitstatus = 0 ]
then
if [ ! -z "$website_db_user" ]
then
# Validate database username
db_user_regex="^([a-zA-Z0-9_]){2,25}$"
if [[ "$website_db_user" =~ $db_user_regex ]]
then
db_user_check
summary
else
unset website_db_user
inputbox_db_user="Please enter a usable database username, or leave empty to use \"$install_folder\":"
advanced_db_user
fi
else
website_db_user=$install_folder
db_user_check
summary
fi
else
# If Esc key is pressed
die "Okay, come back when when you feel like going a little further."
fi
}
function db_user_check {
# Make sure we don't use an already existing database user if we install more than one website
if [[ ! -z $(mysql -h localhost -u root $opt_mysqlpass -e "use mysql; SELECT user FROM user;" | grep -w "$website_db_user") ]]
then
inputbox_db_user="A mysql user named \"$website_db_user\" already exists, please choose another name:"
unset website_db_user
advanced_db_user
fi
# - Hey why wouldn't we allow the user to choose an already existing mysql user name?
# - Because this script is intended for n00bs and we don't want it to break anything
# Feel free to comment the db_user_check command if you are sure to know what you're doing,
# but don't come crying after that if you break your system.
}
function summary {
summary_domain="Website address : https://$domain_name/\n\n"
summary_db_pass="Website database password : $website_db_pass\n"
summary_db_name="Website database name : $website_db_name\n"
summary_db_user="Website database user : $website_db_user\n"
# This will be used to display the settings for our install
summary_display="$summary_domain$summary_db_name$summary_db_user$summary_db_pass"
summary_display="$summary_domain$summary_email$summary_webserver$summary_ddns_provider$summary_ddns_key$summary_ddns_id$summary_ddns_password$summary_db_pass$summary_db_name$summary_db_user"
# We display all settings
if (whiptail \
--title "Check your settings" \
--yesno "$summary_display" \
--yes-button "Continue" --no-button "Start over" \
20 80)
then
launch_install
else
# Reset all settings before sarting over. We keep domain name, email address for Let's Encrypt
# and mysql root, which will most likely remain the same
unset webserver summary_webserver
unset ddns_provider ddns_provider_name
unset ddns_key_type ddns_key summary_ddns_key
unset ddns_id ddns_password summary_ddns_id summary_ddns_password
unset website_db_pass website_db_name website_db_user
enter_domain
fi
}
function launch_install {
whiptail \
--title "Launch install" \
--msgbox "Everything is now ready for the installation of your website. Press \"OK\" to start the automated installation (you can press Esc to cancel)". \
10 80
exitstatus=$?
if [ $exitstatus = 0 ]
then
print_info "Website install will now start"
else
die "Too bad, you were all set... Come back when you feel ready to test this!"
fi
}
#set -x
script_debut

View file

@ -0,0 +1,262 @@
#!/bin/bash
function enter_email {
# A Let's Encrypt certificate will be requested for FQDN or domain name, an e-mail is needed
if [ -z "$inputbox_email" ]
then
inputbox_email="Please enter the e-mail address that will be use for your Let's Encrypt certificate request (and nothing else):"
fi
le_email=$(whiptail \
--title "E-mail address (for Let's Encrypt)" \
--inputbox "$inputbox_email" \
10 60 $le_email 3>&1 1>&2 2>&3)
exitstatus=$?
if [ $exitstatus = 0 ]
then
if [ -z "$le_email" ]
then
inputbox_email="The e-mail address is mandatory to obtain a Let's Encrypt certificate, so please enter one:"
enter_email
else
# Validate email address structure (we don't check if it atually exists)
email_regex="^[[:alnum:]._%+-]+@[[:alnum:].-]+\.[[:alpha:].]{2,12}$"
if [[ "$le_email" =~ $email_regex ]]
then
summary_email="Mail address : $le_email\n"
webserver_check
else
inputbox_email="\"$le_email\" doesn't remotely look like an e-mail address. Please enter something that looks like \"someone@example.com\" or \"somebody@subdomain.example.com\":"
enter_email
fi
fi
else
die "Not ready to try yet? Come back when you feel you are!"
fi
}
function webserver_check {
# Here we check if a Nginx or Apache webserver is already installed and running
# We can't have both at the same time
if [ "$(systemctl is-active nginx)" == "active" ]
then
webserver_name="a Nginx"
webserver=nginx
summary_webserver="\nWeb server : Nginx\n\n"
elif [ "$(systemctl is-active apache2)" == "active" ]
then
webserver_name="an Apache"
webserver=apache
summary_webserver="\nWeb server : Apache\n\n"
fi
if [ ! -z "$webserver_name" ]
then
# If a running webserver is found, It will be used, there can't be another one installed
whiptail \
--title "A web server is already running" \
--msgbox "You already have $webserver_name web server running on this computer, it will also be used for this install. Or you can press Esc and solve this issue by yourself." \
10 60
exitstatus=$?
if [ $exitstatus = 0 ]
then
ddns_choice
else
# In case the user presses the Esc key
die "Brokay, come back when you feel ready to test this!"
fi
else
# If no running webserver is found, we can choose one
select_webserver
fi
}
function select_webserver {
which_web_server=$(whiptail \
--title "Choose your web server" \
--menu "Please choose the webserver you will be using:" \
18 80 3 \
"1" "Apache"\
"2" "Nginx (EXPERIMENTAL - channel import/cloning doesn't work)" 3>&1 1>&2 2>&3)
exitstatus=$?
if [ $exitstatus = 0 ]
then
case "$which_web_server" in
1) webserver=apache
summary_webserver="\nWeb server : Apache\n\n"
ddns_choice ;;
2) webserver=nginx
summary_webserver="\nWeb server : Nginx\n\n"
# After choosing the Web server, we need to check if Dynamic DNS will be needed
ddns_choice
esac
else
die "Trokay, come back when you feel ready to test this!"
fi
}
function ddns_choice {
# We can automatically configure Dynamic DNS (DDNS) with a few providers
# This is of course to be used only with a FQDN of domain name
provider=$(whiptail \
--title "Optional - Dynamic DNS configuration" \
--menu "If you plan to use a Dynamic DNS (DDNS) provider, you may choose one here. Currently supported providers are FreeDNS, Gandi and selHOST.de. You must already have an account with the selected provider and own a domain/subdomain. Please choose one of the following options:"\
18 80 4 \
"1" "None, I won't be using a DDNS provider"\
"2" "FreeDNS (offers free of charge subdomains)"\
"3" "Gandi (French domain name registrar with a nice API)"\
"4" "selfHOST.de (German language provider & registrar)" 3>&1 1>&2 2>&3)
### "5" "Sorry, what now?" 3>&1 1>&2 2>&3) ### Maybe an explanation short text about DDNS could be useful
exitstatus=$?
if [ $exitstatus = 0 ]
then
case "$provider" in
# If no Dynamic DNS provider is used
1) enter_root_db_pass ;;
2|3|4) ddns_config ;;
### 5) ddns_ELIF ;; ### Could link to a short explanation text
esac
else
die "Lost your way? Feel free to try again!"
fi
}
function ddns_config {
case "$provider" in
2) ddns_provider=freedns
ddns_provider_name="FreeDNS"
ddns_key_type="update key" ;;
3) ddns_provider=gandi
ddns_provider_name="Gandi LiveDNS"
ddns_key_type="API key" ;;
4) ddns_provider=selfhost
ddns_provider_name="selfHOST.de" ;;
esac
summary_ddns_provider="Dynamic DNS provider : $ddns_provider_name\n"
if [ $provider == 4 ]
# This is for selfHOST.de
then
if [ -z "$inputbox_ddns_id" ]
then
inputbox_ddns_id="Please provide your $ddns_provider_name ID :"
fi
ddns_id=$(whiptail \
--title "$ddns_provider_name ID" \
--inputbox "$inputbox_ddns_id" \
10 60 $ddns_id 3>&1 1>&2 2>&3)
exitstatus=$?
if [ $exitstatus = 0 ]
then
if [ -z "$ddns_id" ]
then
# We don't allow an empty ID
inputbox_ddns_id="You need a $ddns_provider_name ID to finish your DDNS configuration:"
ddns_config
else
summary_ddns_id="$ddns_provider_name ID : $ddns_id\n"
if [ -z "$inputbox_ddns_password" ]
then
inputbox_ddns_password="Please provide your $ddns_provider_name password :"
fi
ddns_password=$(whiptail \
--title "$ddns_provider_name password" \
--inputbox "$inputbox_ddns_password" \
10 60 $ddns_password 3>&1 1>&2 2>&3)
exitstatus=$?
if [ $exitstatus = 0 ]
then
if [ -z "$ddns_password" ]
then
# We don't allow an empty password
inputbox_ddns_password="You need a $ddns_provider_name password to finish your DDNS configuration:"
ddns_config
else
# If we swith from another DDNS provider settings , we need to unset some variables
unset ddns_key summary_ddns_key
summary_ddns_password="$ddns_provider_name password : $ddns_password\n\n"
enter_root_db_pass
fi
else
# If Esc key is pressed
die "Run the script again when you're ready"
fi
fi
else
# If Esc key is pressed
die "Run the script again when you're ready"
fi
else
# The following part is for FreeDNS and Gandi which both only need a single key
if [ -z "$inputbox_ddns_key" ]
then
inputbox_ddns_key="Please provide your $ddns_provider_name $ddns_key_type :"
fi
ddns_key=$(whiptail \
--title "$ddns_provider_name $ddns_key_type" \
--inputbox "$inputbox_ddns_key" \
10 60 $ddns_key 3>&1 1>&2 2>&3)
exitstatus=$?
if [ $exitstatus = 0 ]
then
if [ -z "$ddns_key" ]
then
inputbox_ddns_key="You need a $ddns_provider_name $ddns_key_type to finish your DDNS configuration:"
ddns_config
else
# If we switch from a selfHOST.de configuration, we unset some variables
unset ddns_id summary_ddns_id ddns_password summary_ddns_password
summary_ddns_key="$ddns_provider_name $ddns_key_type : $ddns_key\n\n"
enter_root_db_pass
fi
else
# If Esc key is pressed
die "Run the script again when you're ready"
fi
fi
}
function enter_root_db_pass {
# We check if mysql is already installed
if [ -z $(which mysql) ]
then
# If mysql is not installed we can go straight to choosing our website database name and user
enter_db_pass
else
# If mysql is already installed we check if root needs a password to use mysql
# On first run $opt_mysqlpass is empty
mysql -h localhost -u root $opt_mysqlpass -e 'quit' &> /dev/null
exitstatus=$?
if [ $exitstatus = 0 ]
then
# 1st function call : if root needs no password we can directly choose our website database name and user
# Next function calls, we do the same if the password entered is the right one
enter_db_pass
else
# If root has a password configured for mysql it has to be entered here
root_db_pass=$(whiptail \
--title "MariaDB root password" \
--passwordbox "Your MariaDB server has a password configured for root. Please enter it here. If you don't know it, please cancel and try again when you've found it." \
--cancel-button "Cancel" \
10 60 $root_db_pass 3>&1 1>&2 2>&3)
exitstatus=$?
if [ $exitstatus = 0 ]
then
opt_mysqlpass="-p$root_db_pass"
enter_root_db_pass
else
die "Come back when when you've found root password for MariaDB"
fi
fi
fi
}
enter_email

View file

@ -2222,7 +2222,6 @@ function attach_recursive_perms($arr_allow_cid, $arr_allow_gid, $arr_deny_cid, $
function filepath_macro($s)
{
return str_replace(
[ '%Y', '%m', '%d' ],
[ datetime_convert('UTC', date_default_timezone_get(), 'now', 'Y'),
@ -2237,7 +2236,6 @@ function attach_export_data($channel, $resource_id, $deleted = false)
{
$ret = [];
$paths = [];
$hash_ptr = $resource_id;
@ -2309,7 +2307,6 @@ function attach_export_data($channel, $resource_id, $deleted = false)
}
}
}
return $ret;
}
@ -2327,7 +2324,6 @@ function get_attach_binname($s)
$p = substr($s, 6);
$p = substr($p, strpos($p, '/') + 1);
}
return $p;
}

View file

@ -785,7 +785,7 @@ function bb_map_location($match)
function bb_qr($match)
{
$str = $match[1];
return str_replace($match[0], '<img src="' . (new QRCode())->render($str) . '" alt="$str" loading="eager" />', $match[0]);
return str_replace($match[0], '<img src="' . (new QRCode())->render($str) . '" alt="' . $str . '" title="' . $str . '" loading="eager" />', $match[0]);
}
function bb_opentag($match)

View file

@ -303,6 +303,9 @@ nav {
filter:alpha(opacity=$nav_percent_min_opacity);
}
.collapsed-divider {
border-top: 1px solid #ccc;
}
#powered-by {
font-size: 0.5rem;
position: absolute;

View file

@ -182,9 +182,13 @@
</div>
<div id="sys-apps-collapsed" style="display:none;">
{{/if}}
{{if $navbar_apps}}
{{foreach $navbar_apps as $navbar_app}}
{{$navbar_app}}
{{$navbar_app|replace:'dropdown-item':'nav-link'}}
{{/foreach}}
<div class="dropdown-divider collapsed-divider"></div>
{{/if}}
{{foreach $nav_apps as $nav_app}}
{{$nav_app|replace:'dropdown-item':'nav-link'}}
{{/foreach}}
@ -192,10 +196,10 @@
</div>
{{/if}}
{{if $is_owner}}
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="/apps"><i class="generic-icons-nav fa fa-fw fa-asterisk"></i>{{$manageapps}}</a>
<a class="dropdown-item" href="/apps/available"><i class="generic-icons-nav fa fa-fw fa-plus-circle"></i>{{$addapps}}</a>
<a class="dropdown-item" href="/apporder"><i class="generic-icons-nav fa fa-fw fa-sort"></i>{{$orderapps}}</a>
<div class="dropdown-divider collapsed-divider"></div>
<a class="nav-link" href="/apps"><i class="generic-icons-nav fa fa-fw fa-asterisk"></i>{{$manageapps}}</a>
<a class="nav-link" href="/apps/available"><i class="generic-icons-nav fa fa-fw fa-plus-circle"></i>{{$addapps}}</a>
<a class="nav-link" href="/apporder"><i class="generic-icons-nav fa fa-fw fa-sort"></i>{{$orderapps}}</a>
{{/if}}
</div>
</div>