23 Commits
v1.0 ... master

Author SHA1 Message Date
Michael Dollinger
a84a07ce47 Fixed invalid password-forgotten-link 2022-05-15 10:39:37 +02:00
Michael Dollinger
615464eae5 UI improvement - light layout changes #17 2022-05-15 10:38:16 +02:00
Michael Dollinger
3eda42ad1a UI improvement - light layout changes #17 2022-05-15 10:37:02 +02:00
Christian Süßenguth
40bf54a504 „README.md“ ändern 2022-02-11 11:40:57 +01:00
82ff17132d Final commit for v1.2, forgot to save edit_list, resolved issues #4 and #6 2022-02-01 13:40:47 +01:00
d7621582da Added display of list owners and list descriptions on index page, Added save function for list description 2022-02-01 13:31:41 +01:00
fcd2a0e395 Version bump to v1.2 2022-02-01 13:12:52 +01:00
b4cd6b8628 Security check if list parameter is not set for edit_list and save_list 2022-02-01 13:12:28 +01:00
15482bf028 Merge branch 'master' into v1.2 2022-02-01 10:31:35 +01:00
Christian Süßenguth
8524670e59 „README.md“ ändern 2021-11-25 13:55:13 +01:00
Christian Süßenguth
834ba43292 „ROADMAP.md“ ändern 2021-11-25 13:53:41 +01:00
b79f0e1844 Updated README 2021-11-25 13:37:18 +01:00
35b510fd16 Automatically sort subscribers and moderators alphabetically on page load 2021-11-25 13:24:24 +01:00
6edf9a6d47 Added an info box regarding the needed user consent fixes #7 2021-11-25 13:16:52 +01:00
2717d1fd90 Slightly text change in consent notice 2021-11-25 13:02:58 +01:00
2231b80846 Get and display list description if you edit a list (Preparation for #6) 2021-10-28 13:40:15 +02:00
82741bc121 Added functionality to sort and count lines of subs and mods (#2 and #3) 2021-10-28 13:10:18 +02:00
bbb3f35767 Fixed bug #5 2021-10-28 12:33:53 +02:00
ead32ad051 Switched to version 1.1 2021-10-28 12:31:27 +02:00
4c8137281c Merge branch 'master' of https://git.ecogood.org/services/mlmmj-light-web-ecg 2021-10-28 12:22:17 +02:00
Christian Süßenguth
9a9ae1d350 „ROADMAP.md“ ändern 2021-10-27 15:14:10 +02:00
10df9b50dd Removed unnecessary files, Added autofocus to login form 2021-08-23 12:09:32 +02:00
e7d5a4b981 Added error handling of missing ldap server connection and audit message for failed login 2021-08-18 18:02:56 +02:00
19 changed files with 242 additions and 4983 deletions

View File

@@ -13,7 +13,9 @@ A light PHP web interface for managing [mlmmj](http://mlmmj.org/) mailing lists.
### For users ### For users
- Authentication via LDAP - Authentication via LDAP
- List all available mailinglists on the server - List all available mailinglists on the server
- Display owners and listdescription of the respective mailing lists on the index page
- Only show the edit function for mailing lists where the user is set as owner - Only show the edit function for mailing lists where the user is set as owner
- Edit functions per mailing list: subscribers, moderators, prefix and listdescription
### For admins ### For admins
- Error handling regarding invalid user input - Error handling regarding invalid user input
@@ -21,6 +23,13 @@ A light PHP web interface for managing [mlmmj](http://mlmmj.org/) mailing lists.
- Audit log of changes - Audit log of changes
- Notify admins about errors via Rocket:Chat bot implementation - Notify admins about errors via Rocket:Chat bot implementation
---
## IMPORTANT
In case of login issues: Please be aware that the password input field gets sanitized using the filter [FILTER_SANITIZE_FULL_SPECIAL_CHARS](https://www.php.net/manual/en/filter.filters.sanitize.php)
---
## Installation ## Installation
Clone the git repository to your webserver: Clone the git repository to your webserver:
@@ -69,7 +78,9 @@ Check if the values from `init.php` are still valid or need to be adapted.
## Changelog ## Changelog
v1.0 - Initial release (08/13/2021) [v1.2](https://git.ecogood.org/services/mlmmj-light-web-ecg/releases/tag/v1.2) - Version 1.2 (2022-02-01)
[v1.1](https://git.ecogood.org/services/mlmmj-light-web-ecg/releases/tag/v1.1) - Version 1.1 (2021-11-25)
[v1.0](https://git.ecogood.org/services/mlmmj-light-web-ecg/releases/tag/v1.0) - Initial release (2021-08-13)
## Roadmap ## Roadmap

View File

@@ -1,15 +1,3 @@
# ROADMAP # ROADMAP
Updated at: 08/13/2021 refer to [Milestones](https://git.ecogood.org/services/mlmmj-light-web-ecg/milestones)
## v1.1
nothing there yet
## Feature wishes
- [ ] Automatically put square brackets around the prefix `[prefix]`
- [ ] Count number of subscribers and moderators in the text areas
- [ ] Allow admins to edit all mailing lists
- [ ] Check for duplicates in the subscriber / moderator text area
- [ ] Allow users to subscribe to a list by clicking a button
- [ ] Show the list-description
- [ ] Someone enters his email address into an input field and gets an email with all mailing lists he is subscribed to

View File

@@ -28,7 +28,7 @@ if ( strlen($list_name) > 30 )
} }
// Test list existence // Test list existence
if( !is_dir("$lists_path/$domain/$list_name") ) if( !is_dir("$lists_path/$domain/$list_name") || $list_name == "" )
{ {
header("Location: error.php"); header("Location: error.php");
exit(); exit();
@@ -71,6 +71,20 @@ $prefix = file_get_contents("$lists_path/$domain/$list_name/control/prefix");
// Remove trailing empty symbols // Remove trailing empty symbols
$prefix = trim($prefix); $prefix = trim($prefix);
# Check whether there is a listdescription file
if (file_exists("$lists_path/$domain/$list_name/control/listdescription"))
{
// Get list description
$listdescription = file_get_contents("$lists_path/$domain/$list_name/control/listdescription");
// Remove trailing empty symbols
$listdescription = trim($listdescription);
}
else
{
$listdescription = NULL;
}
// Load page // Load page
$smarty->assign("headline", $headline); $smarty->assign("headline", $headline);
$smarty->assign("web_url", $web_url); $smarty->assign("web_url", $web_url);
@@ -79,6 +93,7 @@ $smarty->assign("list_name", $list_name);
$smarty->assign("domain", $domain); $smarty->assign("domain", $domain);
$smarty->assign("moderators", $moderators); $smarty->assign("moderators", $moderators);
$smarty->assign("prefix", $prefix); $smarty->assign("prefix", $prefix);
$smarty->assign("listdescription", $listdescription);
$smarty->assign("username", $_SESSION["username"]); $smarty->assign("username", $_SESSION["username"]);
$smarty->assign("success", $success); $smarty->assign("success", $success);
$smarty->display("edit_list.tpl"); $smarty->display("edit_list.tpl");

View File

@@ -1,5 +1,8 @@
<?php <?php
# Scan loading time
$time_start = microtime(true);
require("init.php"); require("init.php");
if (!isset($_SESSION["auth"]) || $_SESSION["auth"] != 1) if (!isset($_SESSION["auth"]) || $_SESSION["auth"] != 1)
@@ -27,16 +30,40 @@ if (isset($lists))
} }
$lists_new = []; $lists_new = [];
# Iterate through all lists
foreach($lists as $list) foreach($lists as $list)
{ {
# If list is in array of owned lists
if (!in_array($list, $_SESSION["array_lists_owned"])) if (!in_array($list, $_SESSION["array_lists_owned"]))
{ {
$lists_new[$list] = 0; $lists_new[$list]["iamowner"] = 0;
} }
else else
{ {
$lists_new[$list] = 1; $lists_new[$list]["iamowner"] = 1;
} }
# Get the owners of the list and put them into the array
$owners = explode("\n", trim(shell_exec("/usr/bin/mlmmj-list -o -L $lists_path/$domain/$list")));
$lists_new[$list]["owners"] = $owners;
# Check whether there is a listdescription file
if (file_exists("$lists_path/$domain/$list/control/listdescription") && @file_get_contents("$lists_path/$domain/$list/control/listdescription") != "")
{
// Get list description
$listdescription = file_get_contents("$lists_path/$domain/$list/control/listdescription");
// Remove trailing empty symbols
$listdescription = trim($listdescription);
}
else
{
# Set listdescription to none
$listdescription = "none";
}
# Add the listdescription to the array
$lists_new[$list]["description"] = $listdescription;
} }
} }
else else
@@ -44,11 +71,18 @@ else
$lists = NULL; $lists = NULL;
} }
# Scan loading time
$time_end = microtime(true);
# Calculate loading time
$loadingtime = round(($time_end - $time_start), 2);
$smarty->assign("headline", $headline); $smarty->assign("headline", $headline);
$smarty->assign("web_url", $web_url); $smarty->assign("web_url", $web_url);
$smarty->assign("lists", $lists_new); $smarty->assign("lists", $lists_new);
$smarty->assign("domain", $domain); $smarty->assign("domain", $domain);
$smarty->assign("username", $_SESSION["username"]); $smarty->assign("username", $_SESSION["username"]);
$smarty->assign("loadingtime", $loadingtime);
$smarty->display("index.tpl"); $smarty->display("index.tpl");
?> ?>

1
info.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" enable-background="new 0 0 64 64"><path d="m32 2c-16.568 0-30 13.432-30 30s13.432 30 30 30 30-13.432 30-30-13.432-30-30-30m5 49.75h-10v-24h10v24m-5-29.5c-2.761 0-5-2.238-5-5s2.239-5 5-5c2.762 0 5 2.238 5 5s-2.238 5-5 5" fill="#dddddd"/></svg>

After

Width:  |  Height:  |  Size: 303 B

View File

@@ -22,7 +22,7 @@ $domain_global = "mlmmj";
$rc_webhook = ""; $rc_webhook = "";
# No need to change this values # No need to change this values
$current_version = "v1.0"; $current_version = "v1.2";
$headline = "Manage your ECG mailing lists " . $current_version; $headline = "Manage your ECG mailing lists " . $current_version;
$debug = false; $debug = false;

View File

@@ -18,7 +18,20 @@ if (!empty($login_username) && !empty($login_pass))
$ldap_server = "localhost"; $ldap_server = "localhost";
$ldap_port = 30389; $ldap_port = 30389;
$connect = ldap_connect($ldap_server, $ldap_port); #or die("Failed to connect to the LDAP server."); $connect = ldap_connect($ldap_server, $ldap_port);
if (!$connect)
{
# If debug mode is on show error message
if ($debug)
{
echo "Failed to connect to the LDAP server.";
}
else
{
shell_exec('curl -X POST -H \'Content-Type: application/json\' --data \'{"alias":"ECG Notification Bot","emoji":":ghost:","text":"Failed to connect to the LDAP server."}\' https://chat.ecogood.org/hooks/A' . $rc_webhook);
}
exit;
}
ldap_set_option($connect, LDAP_OPT_PROTOCOL_VERSION, 3); ldap_set_option($connect, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($connect, LDAP_OPT_REFERRALS, 0); ldap_set_option($connect, LDAP_OPT_REFERRALS, 0);
@@ -26,7 +39,7 @@ if (!empty($login_username) && !empty($login_pass))
# bind user # bind user
$auth_user = "uid=" . $login_username . ",ou=users,ou=ecg"; $auth_user = "uid=" . $login_username . ",ou=users,ou=ecg";
$auth_pass = $login_pass; $auth_pass = $login_pass;
$bind = ldap_bind($connect, $auth_user, $auth_pass); #or die("Failed to bind to LDAP server."); $bind = ldap_bind($connect, $auth_user, $auth_pass);
# If the bind was successfull # If the bind was successfull
if ($bind) if ($bind)
@@ -62,6 +75,9 @@ if (!empty($login_username) && !empty($login_pass))
} }
else else
{ {
# Send audit message on failed login
shell_exec('curl -X POST -H \'Content-Type: application/json\' --data \'{"alias":"ECG Notification Bot","emoji":":ghost:","text":"Login failed: ' . $login_username . ' (' . $_SERVER["REMOTE_ADDR"] . ')"}\' https://chat.ecogood.org/hooks/' . $rc_webhook);
// Incorrect password // Incorrect password
$_SESSION["error_code"] = 3; $_SESSION["error_code"] = 3;
header("Location: error.php"); header("Location: error.php");

File diff suppressed because it is too large Load Diff

View File

@@ -1,2 +0,0 @@
if $message_body contains "DISCARD_THIS_MAIL" and not error_message
then seen finish endif

View File

@@ -1,14 +0,0 @@
all: foot_filter
dev: tags splint foot_filter
.PHONY: splint clean clobber
tags: foot_filter.c
ctags --excmd=number '--regex-c=-/\*[[:blank:]]*tag:[[:blank:]]*([[:alnum:]_]+)-\1-' foot_filter.c
splint:
splint +unixlib -exitarg -initallelements foot_filter.c
foot_filter: foot_filter.c
gcc -Wall -g -o foot_filter foot_filter.c -O3
clean:
-rm tags
clobber: clean
-rm foot_filter
-rm test

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +0,0 @@
#!/bin/bash
#
# mlmmj-footer-receive
#
# Adds the footer to incoming message
#
/usr/bin/foot_filter -P /$1/$2/control/footer-text -H /$1/$2/control/footer-html | /usr/bin/mlmmj-receive -F -L /$1/$2/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

View File

@@ -44,7 +44,7 @@
//{/literal} //{/literal}
</script> </script>
</head> </head>
<body> <body onload="document.getElementById('subscribers').value = document.getElementById('subscribers').value.split('\n').sort().join('\n'); document.getElementById('moderators').value = document.getElementById('moderators').value.split('\n').sort().join('\n');">
<div id="header"> <div id="header">
<div id="header_left"> <div id="header_left">
<a href="{$web_url}">{$headline}</a> <a href="{$web_url}">{$headline}</a>
@@ -56,6 +56,9 @@
<div id="breadcrumbs"> <div id="breadcrumbs">
<a href="index.php">Home</a>&nbsp;/&nbsp;{$list_name} <a href="index.php">Home</a>&nbsp;/&nbsp;{$list_name}
</div> </div>
<div style="width: 75%; border: 2px solid #000; margin: 0 auto 30px; text-align: center; padding: 20px; border-radius: 10px; background-color: #FFF7A4; border-color: #C1AE00;">
Please be aware that you need the user's consent to receive mails from the list <strong>before</strong> you add him to the list of subscribers.<br />This tool <strong>won't send a double opt-in message</strong> to new subscribers automatically.
</div>
{if $success eq true}<p class="success">List was successfully updated.</p>{/if} {if $success eq true}<p class="success">List was successfully updated.</p>{/if}
<form method="post" action="save_list.php" id="save_list" onsubmit="return validate_form()"> <form method="post" action="save_list.php" id="save_list" onsubmit="return validate_form()">
<div id="edit_page"> <div id="edit_page">
@@ -66,9 +69,10 @@
<div class="tooltip"> <div class="tooltip">
<img src="help.svg" width=15 height=15> <img src="help.svg" width=15 height=15>
<span class="help_sub"> <span class="help_sub">
Please provide one email address per line.<br /><br />Please be aware that you need the user's consent to receive mails from the list <strong>before</strong> you add him to the list of subscribers. This tool won't send a double opt-in message to new subscribers. Please provide one email address per line.<br /><br />Please be aware that you need the user's consent to receive mails from the list <strong>before</strong> you add him to the list of subscribers. This tool won't send a double opt-in message to new subscribers automatically.
</span> </span>
</div> </div>
&nbsp;|&nbsp;<a href="#" onclick="document.getElementById('subscribers').value = document.getElementById('subscribers').value.split('\n').sort().join('\n'); alert('Subscribers list has been sorted alphabetically.');">A-Z</a>&nbsp;|&nbsp;<a href="#" onclick="alert('Current subscribers count: ' + document.getElementById('subscribers').value.trim().split('\n').length);">Count</a>
</div> </div>
<div id="subscribers_body"> <div id="subscribers_body">
<textarea name="subscribers" id="subscribers">{$subscribers}</textarea> <textarea name="subscribers" id="subscribers">{$subscribers}</textarea>
@@ -77,7 +81,7 @@
<div id="column_middle"> <div id="column_middle">
<div id="column_middle_inner"> <div id="column_middle_inner">
<div id="table_div"> <div id="table_div">
<table id="table_middle"> <table id="table_middle" class="table_middle">
<tr> <tr>
<td> <td>
<div id="prefix_header"> <div id="prefix_header">
@@ -90,11 +94,33 @@
&nbsp;Prefix: &nbsp;Prefix:
</div> </div>
</td> </td>
</tr>
<tr>
<td> <td>
<input type="text" name="prefix" value="{$prefix|escape:'htmlall'}" id="prefix"> <input type="text" name="prefix" value="{$prefix|escape:'htmlall'}" id="prefix">
</td> </td>
</tr> </tr>
</table> </table>
<table class="table_middle">
<tr>
<td >
<div id="listdescription_header">
<div class="tooltip">
<img src="help.svg" width=15 height=15>
<span class="help_prefix">
This is the list description which is displayed in the overview.<br /><br />Can be left blank.
</span>
</div>
&nbsp;List description:
</div>
</td>
</tr>
<tr>
<td>
<textarea name="listdescription" id="listdescription" style="height: 100%; width: 100%;">{$listdescription|escape:'htmlall'}</textarea>
</td>
</tr>
</table>
</div> </div>
<div id="save_btn"> <div id="save_btn">
<input type="submit" name="submit" value="Save"> <input type="submit" name="submit" value="Save">
@@ -112,6 +138,7 @@
In case of a moderated list the messages will be send to these recipients before they get published to the list.<br /><br />Please be aware that you need the user's consent to receive mails from the list <strong>before</strong> you add him to the list of moderators. This tool won't send a double opt-in message to new moderators. In case of a moderated list the messages will be send to these recipients before they get published to the list.<br /><br />Please be aware that you need the user's consent to receive mails from the list <strong>before</strong> you add him to the list of moderators. This tool won't send a double opt-in message to new moderators.
</span> </span>
</div> </div>
&nbsp;|&nbsp;<a href="#" onclick="document.getElementById('moderators').value = document.getElementById('moderators').value.split('\n').sort().join('\n'); alert('Moderators list has been sorted alphabetically.');">A-Z</a>&nbsp;|&nbsp;<a href="#" onclick="alert('Current moderators count: ' + document.getElementById('moderators').value.trim().split('\n').length);">Count</a>
</div> </div>
<div id="moderators_body"> <div id="moderators_body">
<textarea name="moderators" id="moderators">{$moderators}</textarea> <textarea name="moderators" id="moderators">{$moderators}</textarea>

View File

@@ -58,13 +58,19 @@
</tr> </tr>
{foreach $lists as $list} {foreach $lists as $list}
{if $list == 1} {if $list.iamowner == 1}
<tr> <tr>
<td> <td>
&check; &check;
</td> </td>
<td> <td>
<a href="edit_list.php?list_name={$list@key}">{$list@key}</a> <a href="edit_list.php?list_name={$list@key}">{$list@key}</a>
<div class="tooltip">
<img src="info.svg" width=15 height=15>
<span class="help_add_list">
<strong>Description</strong><br />{$list.description}<br /><br /><strong>List owner(s)</strong><br />{foreach $list.owners as $owner}{$owner}<br />{/foreach}
</span>
</div>
</td> </td>
</tr> </tr>
{/if} {/if}
@@ -94,18 +100,26 @@
</tr> </tr>
{foreach $lists as $list} {foreach $lists as $list}
{if $list == 0} {if $list.iamowner == 0}
<tr> <tr>
<td> <td>
&cross; &cross;
</td> </td>
<td> <td>
{$list@key} {$list@key}
<div class="tooltip">
<img src="info.svg" width=15 height=15>
<span class="help_add_list">
<strong>Description</strong><br />{$list.description}<br /><br /><strong>List owner(s)</strong><br />{foreach $list.owners as $owner}{$owner}<br />{/foreach}
</span>
</div>
</td> </td>
</tr> </tr>
{/if} {/if}
{/foreach} {/foreach}
</table> </table>
<br />
<span>Loading time: {$loadingtime} seconds</span>
</div> </div>
</body> </body>
</html> </html>

View File

@@ -35,14 +35,15 @@
</div> </div>
<div id="login"> <div id="login">
<div id="login_form"> <div id="login_form">
<p>Please enter the credentials of your ECG account<br />(<strong>without</strong> @ecogood.org)</p> <p>Please enter the credentials of your ECG account<br />(<strong>without</strong> <i>@ecogood.org</I>)</p>
<br />
<form method="post" action="login.php" onsubmit="return validate_form()"> <form method="post" action="login.php" onsubmit="return validate_form()">
<div id="username"> <div id="username">
<div id="username_left"> <div id="username_left">
Username: Username:
</div> </div>
<div id="username_right"> <div id="username_right">
<input type="text" name="login_username" id="username_input"> <input type="text" name="login_username" id="username_input" autofocus>
</div> </div>
</div> </div>
<div id="password"> <div id="password">
@@ -53,10 +54,12 @@
<input type="password" name="login_pass" id="password_input"> <input type="password" name="login_pass" id="password_input">
</div> </div>
</div> </div>
<a href="https://wiki.ecogood.org/display/PUBLIC/IT-Support" target="_blank"><p>Forgot your password?</p></a>
<div id="enter"> <div id="enter">
<input type="submit" name="submit" value="Login"> <input type="submit" name="submit" value="Login">
</div> </div>
<br />
<br />
<a href=" https://wiki.ecogood.org/x/DYQjB " target="_blank"><p>Forgot your password?</p></a>
</form> </form>
</div> </div>
</div> </div>

View File

@@ -19,6 +19,7 @@ function trim_array($arr)
$list_name = isset( $_POST["list_name"] ) ? $_POST["list_name"] : NULL; $list_name = isset( $_POST["list_name"] ) ? $_POST["list_name"] : NULL;
$prefix = isset ( $_POST["prefix"] ) ? $_POST["prefix"] : NULL; $prefix = isset ( $_POST["prefix"] ) ? $_POST["prefix"] : NULL;
$listdescription = isset ( $_POST["listdescription"] ) ? $_POST["listdescription"] : NULL;
$new_subscribers = isset ( $_POST["subscribers"] ) ? $_POST["subscribers"] : NULL; $new_subscribers = isset ( $_POST["subscribers"] ) ? $_POST["subscribers"] : NULL;
$moderators = isset ( $_POST["moderators"] ) ? $_POST["moderators"] : NULL; $moderators = isset ( $_POST["moderators"] ) ? $_POST["moderators"] : NULL;
@@ -46,7 +47,7 @@ if ( strlen($list_name) > 30 )
} }
// Test list existence // Test list existence
if( !is_dir("$lists_path/$domain/$list_name") ) if( !is_dir("$lists_path/$domain/$list_name") || $list_name == "" )
{ {
header("Location: error.php"); header("Location: error.php");
exit(); exit();
@@ -89,7 +90,7 @@ if ($new_subscribers != NULL)
header("Location: error.php"); header("Location: error.php");
exit(); exit();
} }
shell_exec("/usr/bin/mlmmj-sub -L $lists_path/$domain/$list_name -a $new_subscriber -fq"); shell_exec("/usr/bin/mlmmj-sub -L $lists_path/$domain/$list_name -a $new_subscriber -fsq");
} }
} }
@@ -158,11 +159,18 @@ if ($moderators !== NULL)
} }
} }
# Add prefix to the respective file
if ($prefix !== NULL) if ($prefix !== NULL)
{ {
file_put_contents("$lists_path/$domain/$list_name/control/prefix", "$prefix"); file_put_contents("$lists_path/$domain/$list_name/control/prefix", "$prefix");
} }
# Add listdescription to the respective file
if ($listdescription !== NULL)
{
file_put_contents("$lists_path/$domain/$list_name/control/listdescription", "$listdescription");
}
# The following code section is for audit log only # The following code section is for audit log only
# ------------------------------------------------------------- # -------------------------------------------------------------

235
style.css
View File

@@ -1,5 +1,4 @@
body body {
{
margin: 0; margin: 0;
padding: 0; padding: 0;
font-family: sans-serif; font-family: sans-serif;
@@ -7,14 +6,12 @@ body
height: 100%; height: 100%;
} }
form form {
{
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
a a {
{
margin-top: 0; margin-top: 0;
margin-bottom: 0; margin-bottom: 0;
cursor: pointer; cursor: pointer;
@@ -22,18 +19,15 @@ a
text-decoration: none; text-decoration: none;
} }
a:hover a:hover {
{
color: #66ccff; color: #66ccff;
} }
td td {
{
padding-right: 8px; padding-right: 8px;
} }
#header #header {
{
font-size: 30px; font-size: 30px;
background-color: #222222; background-color: #222222;
color: #9d9d9d; color: #9d9d9d;
@@ -48,48 +42,40 @@ td
justify-content: space-between; justify-content: space-between;
} }
#error #error {
{
padding-top: 15px; padding-top: 15px;
padding-left: 30px; padding-left: 30px;
} }
#header_left #header_left {
{
float: left; float: left;
} }
#header_left a #header_left a {
{
color: #9d9d9d; color: #9d9d9d;
} }
#header_left a:hover #header_left a:hover {
{
color: #66ccff; color: #66ccff;
} }
#header_right #header_right {
{
display: flex; display: flex;
align-items: center; align-items: center;
font-size: 20px; font-size: 20px;
} }
#header_right a #header_right a {
{
color: #9d9d9d; color: #9d9d9d;
} }
#header_right a:hover #header_right a:hover {
{
color: #66ccff; color: #66ccff;
} }
#login #login {
{
width: 100%; width: 100%;
position: absolute; position: absolute;
top: 60px; top: 60px;
@@ -97,45 +83,45 @@ td
display: flex; display: flex;
} }
#login_form #login_form {
{ width: 400px;
width: 250px;
padding: 10px; padding: 10px;
margin: auto; margin: auto;
align-self: center; align-self: center;
} }
#login_form > form {
width: 300px;
}
#username, #password #username, #password {
{
width: 100%; width: 100%;
padding-bottom: 5px; padding-bottom: 5px;
display: flex; display: flex;
} }
#username_left, #password_left #username_left, #password_left {
{
align-self: center; align-self: center;
} }
#username_right, #password_right #username_right, #password_right {
{
margin-left: auto; margin-left: auto;
} }
#username_input, #password_input #username_input, #password_input {
{
width: 170px; width: 170px;
} }
#enter #enter {
{ width: 100%;
padding-top: 5px; padding-top: 5px;
display: inline-block; display: inline-block;
margin-left: 80px; # margin-left: 80px;
}
#enter > input {
width: 100%;
} }
#breadcrumbs #breadcrumbs {
{
height: 40px; height: 40px;
background-color: #f5f5f5; background-color: #f5f5f5;
margin-top: 20px; margin-top: 20px;
@@ -147,43 +133,36 @@ td
align-items: center; align-items: center;
} }
#index #index {
{
margin-left: 30px; margin-left: 30px;
} }
#lists_header #lists_header {
{
margin-bottom: 10px; margin-bottom: 10px;
display: flex; display: flex;
align-items: center; align-items: center;
} }
#lists #lists {
{
padding-left: 17px; padding-left: 17px;
margin-bottom: 5px; margin-bottom: 5px;
} }
#add_list #add_list {
{
display: flex; display: flex;
padding-left: 20px; padding-left: 20px;
} }
#add_list_input #add_list_input {
{
width: 170px; width: 170px;
} }
.tooltip .tooltip {
{
position: relative; position: relative;
display: inline-block; display: inline-block;
} }
.tooltip .help_add_list .tooltip .help_add_list {
{
visibility: hidden; visibility: hidden;
width: 450px; width: 450px;
background-color: #111; background-color: #111;
@@ -197,8 +176,7 @@ td
left: 170%; left: 170%;
} }
.tooltip .help_add_list::after .tooltip .help_add_list::after {
{
content: ""; content: "";
position: absolute; position: absolute;
top: 5px; top: 5px;
@@ -209,142 +187,131 @@ td
border-color: transparent #111 transparent transparent; border-color: transparent #111 transparent transparent;
} }
.tooltip:hover .help_add_list .tooltip:hover .help_add_list {
{
visibility: visible; visibility: visible;
} }
#edit_page #edit_page {
{
display: table; display: table;
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.success .success {
{
font-weight: bold; font-weight: bold;
color: #00aa00; color: #00aa00;
text-align: center; text-align: center;
} }
#save_list #save_list {
{ background-color: #f5f5f5;
margin-top: 20px; margin-top: 20px;
margin-bottom: 20px; margin-bottom: 20px;
margin-left: 30px; margin-left: 30px;
margin-right: 30px; margin-right: 30px;
height: calc(100% - 170px); height: calc(100% - 280px);
min-height: 400px; min-height: 400px;
width: calc(100% - 60px); width: calc(100% - 60px);
} }
#column_left #column_left {
{
height: 100%; height: 100%;
display: table-cell; display: table-cell;
min-width: 300px; min-width: 300px;
} }
#subscribers_header #subscribers_header {
{
height: 30px; height: 30px;
width: 300px; width: 350px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
margin-left: auto; margin-left: auto;
} }
#subscribers_body #subscribers_body {
{ height: calc(100% - 50px);
height: calc(100% - 100px); width: 350px;
width: 300px;
margin-left: auto; margin-left: auto;
} }
#subscribers #subscribers {
{
height: 100%; height: 100%;
width: 100%; width: 100%;
} }
#column_right #column_right {
{
height: 100%; height: 100%;
display: table-cell; display: table-cell;
min-width: 320px; min-width: 320px;
} }
#moderators_header #moderators_header {
{
height: 30px; height: 30px;
width: 300px; width: 350px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
#moderators_body #moderators_body {
{ height: calc(100% - 50px);
height: calc(100% - 100px); width: 350px;
width: 300px;
} }
#moderators #moderators {
{
height: 100%; height: 100%;
width: 100%; width: 100%;
} }
#column_middle #column_middle {
{
padding-top: 30px; padding-top: 30px;
width: 500px; width: 500px;
height: 100%;
display: table-cell; display: table-cell;
min-width: 440px; min-width: 440px;
vertical-align: top; vertical-align: top;
padding-bottom: 20px; padding-bottom: 20px;
} }
#column_middle_inner #column_middle_inner {
{
height: 100%; height: 100%;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
flex-direction: column; flex-direction: column;
} }
#table_middle .table_middle {
{ width: 100%;
text-align: right; # text-align: center;
margin: auto; margin-bottom: 30px;
padding: 0 25px 0 25px;
} }
#table_middle td #table_middle td {
{
padding-bottom: 10px; padding-bottom: 10px;
} }
#prefix, #list_type #prefix, #list_type {
{
width: 100%; width: 100%;
} }
#footer #footer {
{
width: 100%; width: 100%;
height: 100px; height: 100px;
} }
#save_btn #save_btn {
{
text-align: center; text-align: center;
width: 100%;
height: 30px;
}
#save_btn > input {
text-align: center;
height: 30px;
width: 50%;
} }
.tooltip .help_sub, .tooltip .help_mod .tooltip .help_sub, .tooltip .help_mod {
{
visibility: hidden; visibility: hidden;
width: 250px; width: 250px;
background-color: #111; background-color: #111;
@@ -359,8 +326,7 @@ td
margin-left: -133px; margin-left: -133px;
} }
.tooltip .help_sub::after, .tooltip .help_mod::after .tooltip .help_sub::after, .tooltip .help_mod::after {
{
content: ""; content: "";
position: absolute; position: absolute;
bottom: 100%; bottom: 100%;
@@ -371,13 +337,11 @@ td
border-color: transparent transparent #111 transparent; border-color: transparent transparent #111 transparent;
} }
.tooltip:hover .help_sub, .tooltip:hover .help_mod .tooltip:hover .help_sub, .tooltip:hover .help_mod {
{
visibility: visible; visibility: visible;
} }
.tooltip .help_list_type .tooltip .help_list_type {
{
visibility: hidden; visibility: hidden;
width: 300px; width: 300px;
background-color: #111; background-color: #111;
@@ -391,8 +355,7 @@ td
right: 170%; right: 170%;
} }
.tooltip .help_list_type::after .tooltip .help_list_type::after {
{
content: ""; content: "";
position: absolute; position: absolute;
top: 5px; top: 5px;
@@ -403,19 +366,16 @@ td
border-color: transparent transparent transparent #111; border-color: transparent transparent transparent #111;
} }
.tooltip:hover .help_list_type .tooltip:hover .help_list_type {
{
visibility: visible; visibility: visible;
} }
#list_type_header, #prefix_header, #footer_header, #notmetoo_header #list_type_header, #prefix_header, #footer_header, #notmetoo_header, #listdescription_header {
{
display: flex; display: flex;
align-items: center; align-items: center;
} }
.tooltip .help_prefix .tooltip .help_prefix {
{
visibility: hidden; visibility: hidden;
width: 200px; width: 200px;
background-color: #111; background-color: #111;
@@ -429,8 +389,7 @@ td
right: 160%; right: 160%;
} }
.tooltip .help_prefix::after .tooltip .help_prefix::after {
{
content: ""; content: "";
position: absolute; position: absolute;
top: 50%; top: 50%;
@@ -441,13 +400,11 @@ td
border-color: transparent transparent transparent #111; border-color: transparent transparent transparent #111;
} }
.tooltip:hover .help_prefix .tooltip:hover .help_prefix {
{
visibility: visible; visibility: visible;
} }
.tooltip .help_footer .tooltip .help_footer {
{
visibility: hidden; visibility: hidden;
width: 200px; width: 200px;
background-color: #111; background-color: #111;
@@ -461,8 +418,7 @@ td
right: 160%; right: 160%;
} }
.tooltip .help_footer::after .tooltip .help_footer::after {
{
content: ""; content: "";
position: absolute; position: absolute;
top: 50%; top: 50%;
@@ -473,19 +429,16 @@ td
border-color: transparent transparent transparent #111; border-color: transparent transparent transparent #111;
} }
.tooltip:hover .help_footer .tooltip:hover .help_footer {
{
visibility: visible; visibility: visible;
} }
#notmetoo_checkbox #notmetoo_checkbox {
{
margin-left: 5px; margin-left: 5px;
margin-right: 5px; margin-right: 5px;
} }
.tooltip .help_notmetoo .tooltip .help_notmetoo {
{
visibility: hidden; visibility: hidden;
width: 300px; width: 300px;
background-color: #111; background-color: #111;
@@ -499,8 +452,7 @@ td
right: 160%; right: 160%;
} }
.tooltip .help_notmetoo::after .tooltip .help_notmetoo::after {
{
content: ""; content: "";
position: absolute; position: absolute;
top: 50%; top: 50%;
@@ -511,7 +463,6 @@ td
border-color: transparent transparent transparent #111; border-color: transparent transparent transparent #111;
} }
.tooltip:hover .help_notmetoo .tooltip:hover .help_notmetoo {
{
visibility: visible; visibility: visible;
} }