Fix various Bootstrap container/row/column abuses resulting in broken layouts

Every column has to be in a row, and every row has to be in a container (except
where a row is nested directly within a column).

This also includes a change to index.html that pushes the footer to the bottom
of the page on those where it otherwise wouldn't be.
This commit is contained in:
David Piggott 2014-10-19 22:40:22 +01:00
parent 6585384daa
commit 3e261dee66
12 changed files with 1300 additions and 1172 deletions

View File

@ -1,15 +1,20 @@
<style> <div class="container-fluid">
#alias_table .actions > * { padding-right: 3px; }
#alias_table .alias-required .remove { display: none }
</style>
<h2>Aliases</h2> <style>
#alias_table .actions > * { padding-right: 3px; }
#alias_table .alias-required .remove { display: none }
</style>
<h3>Add a mail alias</h3> <div class="row">
<div class="col-sm-12">
<p>Aliases are email forwarders. An alias can forward email to a <a href="javascript:show_panel('users')">mail user</a> or to any email address.</p> <h2>Aliases</h2>
<form class="form-horizontal" role="form" onsubmit="do_add_alias(); return false;"> <h3>Add a mail alias</h3>
<p>Aliases are email forwarders. An alias can forward email to a <a href="javascript:show_panel('users')">mail user</a> or to any email address.</p>
<form class="form-horizontal" role="form" onsubmit="do_add_alias(); return false;">
<div class="form-group"> <div class="form-group">
<div class="col-sm-offset-1 col-sm-11"> <div class="col-sm-offset-1 col-sm-11">
<div id="alias_type_buttons" class="btn-group btn-group-xs"> <div id="alias_type_buttons" class="btn-group btn-group-xs">
@ -37,10 +42,10 @@
<button id="alias-cancel" class="btn btn-default hidden" onclick="aliases_reset_form(); return false;">Cancel</button> <button id="alias-cancel" class="btn btn-default hidden" onclick="aliases_reset_form(); return false;">Cancel</button>
</div> </div>
</div> </div>
</form> </form>
<h3>Existing mail aliases</h3> <h3>Existing mail aliases</h3>
<table id="alias_table" class="table" style="width: auto"> <table id="alias_table" class="table" style="width: auto">
<thead> <thead>
<tr> <tr>
<th></th> <th></th>
@ -50,11 +55,11 @@
</thead> </thead>
<tbody> <tbody>
</tbody> </tbody>
</table> </table>
<p style="margin-top: 1.5em"><small>Hostmaster@, postmaster@, and admin@ email addresses are required on some domains.</small></p> <p style="margin-top: 1.5em"><small>Hostmaster@, postmaster@ and admin@ email addresses are required on some domains.</small></p>
<div style="display: none"> <div style="display: none">
<table> <table>
<tr id="alias-template"> <tr id="alias-template">
<td class='actions'> <td class='actions'>
@ -69,11 +74,13 @@
<td class='target'> </td> <td class='target'> </td>
</tr> </tr>
</table> </table>
</div> </div>
</div>
</div>
<script> <script>
function show_aliases() { function show_aliases() {
$('#alias_table tbody').html("<tr><td colspan='2' class='text-muted'>Loading...</td></tr>") $('#alias_table tbody').html("<tr><td colspan='2' class='text-muted'>Loading...</td></tr>")
api( api(
"/mail/aliases", "/mail/aliases",
@ -117,10 +124,10 @@ function show_aliases() {
} }
}) })
}) })
} }
var is_alias_add_update = false; var is_alias_add_update = false;
function do_add_alias() { function do_add_alias() {
var title = (!is_alias_add_update) ? "Add Alias" : "Update Alias"; var title = (!is_alias_add_update) ? "Add Alias" : "Update Alias";
var email = $("#addaliasEmail").val(); var email = $("#addaliasEmail").val();
var targets = $("#addaliasTargets").val(); var targets = $("#addaliasTargets").val();
@ -142,18 +149,18 @@ function do_add_alias() {
show_modal_error(title, r); show_modal_error(title, r);
}); });
return false; return false;
} }
function aliases_reset_form() { function aliases_reset_form() {
$("#addaliasEmail").prop('disabled', false); $("#addaliasEmail").prop('disabled', false);
$("#addaliasEmail").val('') $("#addaliasEmail").val('')
$("#addaliasTargets").val('') $("#addaliasTargets").val('')
$('#alias-cancel').addClass('hidden'); $('#alias-cancel').addClass('hidden');
$('#add-alias-button').text('Add Alias'); $('#add-alias-button').text('Add Alias');
is_alias_add_update = false; is_alias_add_update = false;
} }
function aliases_edit(elem) { function aliases_edit(elem) {
var email = $(elem).parents('tr').attr('data-email'); var email = $(elem).parents('tr').attr('data-email');
var targetdivs = $(elem).parents('tr').find('.target div'); var targetdivs = $(elem).parents('tr').find('.target div');
var targets = ""; var targets = "";
@ -167,9 +174,9 @@ function aliases_edit(elem) {
$("#addaliasTargets").val(targets); $("#addaliasTargets").val(targets);
$('#add-alias-button').text('Update'); $('#add-alias-button').text('Update');
$('body').animate({ scrollTop: 0 }) $('body').animate({ scrollTop: 0 })
} }
function aliases_remove(elem) { function aliases_remove(elem) {
var email = $(elem).parents('tr').attr('data-email'); var email = $(elem).parents('tr').attr('data-email');
show_modal_confirm( show_modal_confirm(
"Remove Alias", "Remove Alias",
@ -188,11 +195,7 @@ function aliases_remove(elem) {
show_aliases(); show_aliases();
}); });
}); });
} }
</script>
function scroll_top() { </div>
$('html, body').animate({
scrollTop: $("#panel_aliases").offset().top
}, 1000);
}
</script>

View File

@ -1,17 +1,18 @@
<style> <div class="container-fluid">
</style> <div class="row">
<div class="col-sm-12">
<h2>Custom DNS</h2> <h2>Custom DNS</h2>
<p class="text-warning">This is an advanced configuration page.</p> <p class="text-warning">This is an advanced configuration page.</p>
<p>It is possible to set custom DNS records on domains hosted here.</p> <p>It is possible to set custom DNS records on domains hosted here.</p>
<h3>Using a Secondary Nameserver</h3> <h3>Using a Secondary Nameserver</h3>
<p>If your TLD requires you to have two separate nameservers, you can either set up a secondary (aka &ldquo;slave&rdquo;) nameserver or, alternatively, set up <a href="#" onclick="return show_panel('external_dns')">external DNS</a> and ignore the DNS server on this box. If you choose to use a seconday/slave nameserver, you must find a seconday/slave nameserver service provider. Your domain name registrar or virtual cloud provider may provide this service for you. Once you set up the seconday/slave nameserver service, enter the hostname of <em>their</em> secondary nameserver:</p> <p>If your TLD requires you to have two separate nameservers, you can either set up a secondary (aka &ldquo;slave&rdquo;) nameserver or, alternatively, set up <a href="#" onclick="return show_panel('external_dns')">external DNS</a> and ignore the DNS server on this box. If you choose to use a seconday/slave nameserver, you must find a seconday/slave nameserver service provider. Your domain name registrar or virtual cloud provider may provide this service for you. Once you set up the seconday/slave nameserver service, enter the hostname of <em>their</em> secondary nameserver:</p>
<form class="form-horizontal" role="form" onsubmit="do_set_secondary_dns(); return false;"> <form class="form-horizontal" role="form" onsubmit="do_set_secondary_dns(); return false;">
<div class="form-group"> <div class="form-group">
<label for="secondarydnsHostname" class="col-sm-1 control-label">Hostname</label> <label for="secondarydnsHostname" class="col-sm-1 control-label">Hostname</label>
<div class="col-sm-10"> <div class="col-sm-10">
@ -28,48 +29,51 @@
<p class="small">Clear the input field above and click Update to use this machine itself as secondary DNS, which is the default/normal setup.</p> <p class="small">Clear the input field above and click Update to use this machine itself as secondary DNS, which is the default/normal setup.</p>
</div> </div>
</div> </div>
</form> </form>
<h3>Custom DNS API</h3> <h3>Custom DNS API</h3>
<p>Use your box&rsquo;s DNS API to set custom DNS records on domains hosted here. For instance, you can create your own dynamic DNS service.</p> <p>Use your box&rsquo;s DNS API to set custom DNS records on domains hosted here. For instance, you can create your own dynamic DNS service.</p>
<p>Send a POST request like this:</p> <p>Send a POST request like this:</p>
<pre>curl -d "" --user {email}:{password} https://{{hostname}}/admin/dns/set/<b>qname</b>[/<b>rtype</b>[/<b>value</b>]]</pre> <pre>curl -d "" --user {email}:{password} https://{{hostname}}/admin/dns/set/<b>qname</b>[/<b>rtype</b>[/<b>value</b>]]</pre>
<h4>HTTP POST parameters</h4> <h4>HTTP POST parameters</h4>
<table class="table"> <table class="table">
<thead><th>Parameter</th> <th>Value</th></thead> <thead><th>Parameter</th> <th>Value</th></thead>
<tr><td>email</td> <td>The email address of any administrative user here.</td></tr> <tr><td>email</td> <td>The email address of any administrative user here.</td></tr>
<tr><td>password</td> <td>That user&rsquo;s password.</td></tr> <tr><td>password</td> <td>That user&rsquo;s password.</td></tr>
<tr><td>qname</td> <td>The fully qualified domain name for the record you are trying to set.</td></tr> <tr><td>qname</td> <td>The fully qualified domain name for the record you are trying to set.</td></tr>
<tr><td>rtype</td> <td>The resource type. <code>A</code> if omitted. Possible values: <code>A</code> (an IPv4 address), <code>AAAA</code> (an IPv6 address), <code>TXT</code> (a text string), or <code>CNAME</code> (an alias, which is a fully qualified domain name).</td></tr> <tr><td>rtype</td> <td>The resource type. <code>A</code> if omitted. Possible values: <code>A</code> (an IPv4 address), <code>AAAA</code> (an IPv6 address), <code>TXT</code> (a text string), or <code>CNAME</code> (an alias, which is a fully qualified domain name).</td></tr>
<tr><td>value</td> <td>The new record&rsquo;s value. If omitted, the IPv4 address of the remote host is used. This is handy for dynamic DNS! To delete a record, use &ldquo;__delete__&rdquo;.</td></tr> <tr><td>value</td> <td>The new record&rsquo;s value. If omitted, the IPv4 address of the remote host is used. This is handy for dynamic DNS! To delete a record, use &ldquo;__delete__&rdquo;.</td></tr>
</table> </table>
<p style="margin-top: 1em">Note that <code>-d ""</code> is merely to ensure curl sends a POST request. You do not need to put anything inside the quotes. You can also pass the value using typical form encoding in the POST body.</p> <p style="margin-top: 1em">Note that <code>-d ""</code> is merely to ensure curl sends a POST request. You do not need to put anything inside the quotes. You can also pass the value using typical form encoding in the POST body.</p>
<p>Strict <a href="http://tools.ietf.org/html/rfc4408">SPF</a> and <a href="https://datatracker.ietf.org/doc/draft-kucherawy-dmarc-base/?include_text=1">DMARC</a> records will be added to all custom domains unless you override them.</p> <p>Strict <a href="http://tools.ietf.org/html/rfc4408">SPF</a> and <a href="https://datatracker.ietf.org/doc/draft-kucherawy-dmarc-base/?include_text=1">DMARC</a> records will be added to all custom domains unless you override them.</p>
<h4>Examples:</h4> <h4>Examples:</h4>
<pre># sets laptop.mydomain.com to point to the IP address of the machine you are executing curl on <pre># sets laptop.mydomain.com to point to the IP address of the machine you are executing curl on
curl -d "" --user me@mydomain.com:###### https://{{hostname}}/admin/dns/set/laptop.mydomain.com curl -d "" --user me@mydomain.com:###### https://{{hostname}}/admin/dns/set/laptop.mydomain.com
# sets an alias # sets an alias
curl -d "" --user me@mydomain.com:###### https://{{hostname}}/admin/dns/set/foo.mydomain.com/cname/bar.mydomain.com curl -d "" --user me@mydomain.com:###### https://{{hostname}}/admin/dns/set/foo.mydomain.com/cname/bar.mydomain.com
# clears the alias # clears the alias
curl -d "" --user me@mydomain.com:###### https://{{hostname}}/admin/dns/set/bar.mydomain.com/cname/__delete__ curl -d "" --user me@mydomain.com:###### https://{{hostname}}/admin/dns/set/bar.mydomain.com/cname/__delete__
# sets a TXT record using the alternate value syntax # sets a TXT record using the alternate value syntax
curl -d "value=something%20here" --user me@mydomain.com:###### https://{{hostname}}/admin/dns/set/foo.mydomain.com/txt curl -d "value=something%20here" --user me@mydomain.com:###### https://{{hostname}}/admin/dns/set/foo.mydomain.com/txt
</pre> </pre>
<script> </div>
function show_custom_dns() { </div>
<script>
function show_custom_dns() {
api( api(
"/dns/secondary-nameserver", "/dns/secondary-nameserver",
"GET", "GET",
@ -78,9 +82,9 @@ function show_custom_dns() {
$('#secondarydnsHostname').val(data.hostname ? data.hostname : ''); $('#secondarydnsHostname').val(data.hostname ? data.hostname : '');
$('#secondarydns-clear-instructions').toggle(data.hostname != null); $('#secondarydns-clear-instructions').toggle(data.hostname != null);
}); });
} }
function do_set_secondary_dns() { function do_set_secondary_dns() {
api( api(
"/dns/secondary-nameserver", "/dns/secondary-nameserver",
"POST", "POST",
@ -95,5 +99,7 @@ function do_set_secondary_dns() {
function(err) { function(err) {
show_modal_error("Secondary DNS", $("<pre/>").text(err)); show_modal_error("Secondary DNS", $("<pre/>").text(err));
}); });
} }
</script> </script>
</div>

View File

@ -1,39 +1,44 @@
<style> <div class="container-fluid">
#external_dns_settings .heading td {
<style>
#external_dns_settings .heading td {
font-weight: bold; font-weight: bold;
font-size: 120%; font-size: 120%;
padding-top: 1.5em; padding-top: 1.5em;
} }
#external_dns_settings .heading.first td { #external_dns_settings .heading.first td {
border-top: none; border-top: none;
padding-top: 0; padding-top: 0;
} }
#external_dns_settings .values td { #external_dns_settings .values td {
border: 0; border: 0;
padding-top: .75em; padding-top: .75em;
padding-bottom: 0; padding-bottom: 0;
max-width: 50vw; max-width: 50vw;
word-wrap: break-word; word-wrap: break-word;
} }
#external_dns_settings .explanation td { #external_dns_settings .explanation td {
border: 0; border: 0;
padding-top: .5em; padding-top: .5em;
padding-bottom: .75em; padding-bottom: .75em;
font-style: italic; font-style: italic;
font-size: 95%; font-size: 95%;
color: #777; color: #777;
} }
</style> </style>
<h2>External DNS</h2> <div class="row">
<div class="col-sm-12">
<p class="text-warning">This is an advanced configuration page.</p> <h2>External DNS</h2>
<p>Although your box is configured to serve its own DNS, it is possible to host your DNS elsewhere &mdash; such as in the DNS control panel provided by your domain name registrar or virtual cloud provider &mdash; by copying the DNS zone information shown in the table below into your external DNS server&rsquo;s control panel.</p> <p class="text-warning">This is an advanced configuration page.</p>
<p>If you do so, you are responsible for keeping your DNS entries up to date! If you previously enabled DNSSEC on your domain name by setting a DS record at your registrar, you will likely have to turn it off before changing nameservers.</p> <p>Although your box is configured to serve its own DNS, it is possible to host your DNS elsewhere &mdash; such as in the DNS control panel provided by your domain name registrar or virtual cloud provider &mdash; by copying the DNS zone information shown in the table below into your external DNS server&rsquo;s control panel.</p>
<table id="external_dns_settings" class="table"> <p>If you do so, you are responsible for keeping your DNS entries up to date! If you previously enabled DNSSEC on your domain name by setting a DS record at your registrar, you will likely have to turn it off before changing nameservers.</p>
<table id="external_dns_settings" class="table">
<thead> <thead>
<tr> <tr>
<th>QName</th> <th>QName</th>
@ -43,10 +48,13 @@
</thead> </thead>
<tbody> <tbody>
</tbody> </tbody>
</table> </table>
<script> </div>
function show_external_dns() { </div>
<script>
function show_external_dns() {
$('#external_dns_settings tbody').html("<tr><td colspan='2' class='text-muted'>Loading...</td></tr>") $('#external_dns_settings tbody').html("<tr><td colspan='2' class='text-muted'>Loading...</td></tr>")
api( api(
"/dns/dump", "/dns/dump",
@ -73,5 +81,7 @@ function show_external_dns() {
} }
} }
}) })
} }
</script> </script>
</div>

View File

@ -21,9 +21,39 @@
overflow-y: scroll; overflow-y: scroll;
} }
html,
body {
height: 100%;
}
#wrap {
min-height: 100%;
height: auto !important;
height: 100%;
margin: 0 auto -50px;
}
#push,
#footer {
height: 50px;
}
@media (max-width: 767px) {
#footer {
margin-left: -20px;
margin-right: -20px;
padding-left: 20px;
padding-right: 20px;
}
}
body { body {
padding-top: 50px; padding-top: 50px;
padding-bottom: 20px; }
footer {
padding-top: 10px;
font: bold italic medium;
border-top: 1px solid #e5e5e5;
} }
p { p {
@ -73,6 +103,7 @@
<!--[if lt IE 7]> <!--[if lt IE 7]>
<p class="chromeframe">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> or <a href="http://www.google.com/chromeframe/?redirect=true">activate Google Chrome Frame</a> to improve your experience.</p> <p class="chromeframe">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> or <a href="http://www.google.com/chromeframe/?redirect=true">activate Google Chrome Frame</a> to improve your experience.</p>
<![endif]--> <![endif]-->
<div id="wrap">
<div class="navbar navbar-inverse navbar-fixed-top"> <div class="navbar navbar-inverse navbar-fixed-top">
<div class="container"> <div class="container">
<div class="navbar-header"> <div class="navbar-header">
@ -115,20 +146,22 @@
</div> </div>
</div> </div>
<div class="container-fluid"> <div class="container">
<div id="panel_system_status" class="container panel"> <div class="row">
<div class="col-sm-12">
<div id="panel_system_status" class="panel">
{% include "system-status.html" %} {% include "system-status.html" %}
</div> </div>
<div id="panel_system_backup" class="container panel"> <div id="panel_system_backup" class="panel">
{% include "system-backup.html" %} {% include "system-backup.html" %}
</div> </div>
<div id="panel_external_dns" class="container panel"> <div id="panel_external_dns" class="panel">
{% include "external-dns.html" %} {% include "external-dns.html" %}
</div> </div>
<div id="panel_custom_dns" class="container panel"> <div id="panel_custom_dns" class="panel">
{% include "custom-dns.html" %} {% include "custom-dns.html" %}
</div> </div>
@ -136,36 +169,45 @@
{% include "login.html" %} {% include "login.html" %}
</div> </div>
<div id="panel_mail-guide" class="container panel"> <div id="panel_mail-guide" class="panel">
{% include "mail-guide.html" %} {% include "mail-guide.html" %}
</div> </div>
<div id="panel_users" class="container panel"> <div id="panel_users" class="panel">
{% include "users.html" %} {% include "users.html" %}
</div> </div>
<div id="panel_aliases" class="container panel"> <div id="panel_aliases" class="panel">
{% include "aliases.html" %} {% include "aliases.html" %}
</div> </div>
<div id="panel_sync_guide" class="container panel"> <div id="panel_sync_guide" class="panel">
{% include "sync-guide.html" %} {% include "sync-guide.html" %}
</div> </div>
<div id="panel_web" class="container panel"> <div id="panel_web" class="panel">
{% include "web.html" %} {% include "web.html" %}
</div> </div>
<div id="panel_ssl" class="container panel"> <div id="panel_ssl" class="panel">
{% include "ssl.html" %} {% include "ssl.html" %}
</div> </div>
</div>
<hr> </div>
<div id="push"></div>
</div>
</div>
<div id="footer">
<div class="container">
<div class="row">
<div class="col-sm-12">
<footer> <footer>
<p>This is a <a href="https://mailinabox.email">Mail-in-a-Box</a>.</p> <p>This is a <a href="https://mailinabox.email">Mail-in-a-Box</a>.</p>
</footer> </footer>
</div> <!-- /container --> </div>
</div>
</div>
</div>
<div id="ajax_loading_indicator" style="display: none; position: fixed; left: 0; top: 0; width: 100%; height: 100%; text-align: center; background-color: rgba(255,255,255,.75)"> <div id="ajax_loading_indicator" style="display: none; position: fixed; left: 0; top: 0; width: 100%; height: 100%; text-align: center; background-color: rgba(255,255,255,.75)">
<div style="margin: 20% auto"> <div style="margin: 20% auto">

View File

@ -1,19 +1,27 @@
<h1 style="margin: 1em; text-align: center">{{hostname}}</h1> <div class="container-fluid">
<div class="row">
<div class="col-sm-12">
<h1 style="margin: 1em; text-align: center">{{hostname}}</h1>
</div>
</div>
{% if no_admins_exist %} {% if no_admins_exist %}
<div class="container"> <div class="row">
<div class="col-md-offset-2 col-md-8"> <div class="col-md-offset-2 col-md-8">
<p class="text-danger">There are no administrative users on this system! To make an administrative user, <p class="text-danger">There are no administrative users on this system! To make an administrative user,
log into this machine using SSH (like when you first set it up) and run:</p> log into this machine using SSH (like when you first set it up) and run:</p>
<pre>cd mailinabox <pre>cd mailinabox
sudo tools/mail.py user make-admin your@emailaddress.com</pre> sudo tools/mail.py user make-admin your@emailaddress.com</pre>
<hr> <hr>
</div>
</div>
{% endif %}
<div class="row"> </div>
<div class="col-sm-offset-2 col-sm-8 col-md-offset-3 col-md-6 col-lg-offset-4 col-lg-4"> </div>
{% endif %}
<div class="row">
<div class="col-sm-12 col-md-offset-2 col-md-10 col-lg-offset-3 col-lg-6">
<center> <center>
<p style="margin: 2em">Log in here for your Mail-in-a-Box control panel.</p> <p style="margin: 2em">Log in here for your Mail-in-a-Box control panel.</p>
</center> </center>
@ -46,12 +54,12 @@ sudo tools/mail.py user make-admin your@emailaddress.com</pre>
</div> </div>
</div> </div>
</form> </form>
</div>
</div> </div>
</div>
<script>
<script> function do_login() {
function do_login() {
if ($('#loginEmail').val() == "") { if ($('#loginEmail').val() == "") {
show_modal_error("Login Failed", "Enter your email address.") show_modal_error("Login Failed", "Enter your email address.")
return false; return false;
@ -105,14 +113,16 @@ function do_login() {
setTimeout(function() { show_panel(!switch_back_to_panel ? 'system_status' : switch_back_to_panel) }, 300); setTimeout(function() { show_panel(!switch_back_to_panel ? 'system_status' : switch_back_to_panel) }, 300);
} }
}) })
} }
function do_logout() { function do_logout() {
api_credentials = ["", ""]; api_credentials = ["", ""];
if (typeof localStorage != 'undefined') if (typeof localStorage != 'undefined')
localStorage.removeItem("miab-cp-credentials"); localStorage.removeItem("miab-cp-credentials");
if (typeof sessionStorage != 'undefined') if (typeof sessionStorage != 'undefined')
sessionStorage.removeItem("miab-cp-credentials"); sessionStorage.removeItem("miab-cp-credentials");
show_panel('login'); show_panel('login');
} }
</script> </script>
</div>

View File

@ -1,10 +1,17 @@
<style>#panel_mail-guide table.table { width: auto; margin-left: .5em; }</style> <div class="container-fluid">
<div class="container"> <style>#panel_mail-guide table.table { width: auto; margin-left: .5em; }</style>
<h2 style="margin-bottom: 0">Checking and Sending Mail</h2>
<div class="row">
<div class="col-sm-12">
<h2 style="margin-bottom: 0">Checking and Sending Mail</h2>
</div>
</div>
<div class="row"> <div class="row">
<div class="col-sm-6"> <div class="col-sm-6">
<h3>How to log in</h3> <h3>How to log in</h3>
<p>Your username and password are the same no matter how you check your mail:</p> <p>Your username and password are the same no matter how you check your mail:</p>
@ -30,6 +37,7 @@
<div class="row"> <div class="row">
<div class="col-lg-6"> <div class="col-lg-6">
<h4>IMAP/SMTP settings</h4> <h4>IMAP/SMTP settings</h4>
<p>This method is preferred on Android devices but is not available on iOS devices.</p> <p>This method is preferred on Android devices but is not available on iOS devices.</p>
@ -45,9 +53,10 @@
</table> </table>
<p>In addition to setting up your email, you&rsquo;ll also need to set up <a href="#sync_guide" onclick="return show_panel(this);">contacts and calendar synchronization</a> separately.</p> <p>In addition to setting up your email, you&rsquo;ll also need to set up <a href="#sync_guide" onclick="return show_panel(this);">contacts and calendar synchronization</a> separately.</p>
</div>
</div>
<div class="col-lg-6"> <div class="col-lg-6">
<h4>Exchange/ActiveSync settings</h4> <h4>Exchange/ActiveSync settings</h4>
<p>On iOS devices and devices on this <a href="http://z-push.org/compatibility/">compatibility list</a>, set up your mail as an Exchange or ActiveSync server. Use these settings when prompted:</p> <p>On iOS devices and devices on this <a href="http://z-push.org/compatibility/">compatibility list</a>, set up your mail as an Exchange or ActiveSync server. Use these settings when prompted:</p>
@ -58,11 +67,13 @@
</table> </table>
<p>Your device should also provide a contacts list and calendar that syncs to this box when you use this method.</p> <p>Your device should also provide a contacts list and calendar that syncs to this box when you use this method.</p>
</div>
</div> </div>
</div> </div>
</div>
<div class="col-sm-6"> <div class="col-sm-6">
<h3>Other information about mail on your box</h3> <h3>Other information about mail on your box</h3>
<h4>Greylisting</h4> <h4>Greylisting</h4>
@ -70,6 +81,7 @@
<h4>Use this box to send as you</h4> <h4>Use this box to send as you</h4>
<p>Your box sets strict email sending policies for your domain names to make it harder for spam and other fraudulent mail to claim to be you. Only this machine is authorized to send email on behalf of your domain names. If you use any other service to send email as you, it will likely get spam filtered by recipients.</p> <p>Your box sets strict email sending policies for your domain names to make it harder for spam and other fraudulent mail to claim to be you. Only this machine is authorized to send email on behalf of your domain names. If you use any other service to send email as you, it will likely get spam filtered by recipients.</p>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,12 +1,12 @@
<style> <div class="container-fluid">
</style> <div class="row">
<div class="col-sm-12">
<h2>SSL Certificates</h2> <h2>SSL Certificates</h2>
<h3>Certificate Status</h3> <h3>Certificate Status</h3>
<table id="ssl_domains" class="table" style="margin-bottom: 2em; width: auto;">
<table id="ssl_domains" class="table" style="margin-bottom: 2em; width: auto;">
<thead> <thead>
<tr> <tr>
<th>Domain</th> <th>Domain</th>
@ -16,17 +16,17 @@
</thead> </thead>
<tbody> <tbody>
</tbody> </tbody>
</table> </table>
<h3 id="ssl_install_header">Install SSL Certificate</h3> <h3 id="ssl_install_header">Install SSL Certificate</h3>
<p>There are many places where you can get a free or cheap SSL certificate. We recommend <a href="https://www.namecheap.com/cart/remove.aspx?itemid=47016639&i=i2">Namecheap&rsquo;s $9 certificate</a> or <a href="https://www.startssl.com/">StartSSL&rsquo;s free express lane</a>.</p> <p>There are many places where you can get a free or cheap SSL certificate. We recommend <a href="https://www.namecheap.com/cart/remove.aspx?itemid=47016639&i=i2">Namecheap&rsquo;s $9 certificate</a> or <a href="https://www.startssl.com/">StartSSL&rsquo;s free express lane</a>.</p>
<p>Which domain are you getting an SSL certificate for?</p> <p>Which domain are you getting an SSL certificate for?</p>
<p><select id="ssldomain" onchange="show_csr()" class="form-control" style="width: auto"></select></p> <p><select id="ssldomain" onchange="show_csr()" class="form-control" style="width: auto"></select></p>
<div id="csr_info" style="display: none"> <div id="csr_info" style="display: none">
<p>You will need to provide the SSL certificate provider this Certificate Signing Request (CSR):</p> <p>You will need to provide the SSL certificate provider this Certificate Signing Request (CSR):</p>
<pre id="ssl_csr"></pre> <pre id="ssl_csr"></pre>
@ -44,10 +44,13 @@
<p>After you paste in the information, click the install button.</p> <p>After you paste in the information, click the install button.</p>
<button class="btn-primary" onclick="install_cert()">Install</button> <button class="btn-primary" onclick="install_cert()">Install</button>
</div> </div>
<script> </div>
function show_ssl() { </div>
<script>
function show_ssl() {
api( api(
"/web/domains", "/web/domains",
"GET", "GET",
@ -75,9 +78,9 @@ function show_ssl() {
$('#ssldomain').append($('<option>').text(domains[i].domain)); $('#ssldomain').append($('<option>').text(domains[i].domain));
} }
}); });
} }
function ssl_install(elem) { function ssl_install(elem) {
var domain = $(elem).parents('tr').attr('data-domain'); var domain = $(elem).parents('tr').attr('data-domain');
$('#ssldomain').val(domain); $('#ssldomain').val(domain);
$('#csr_info').slideDown(); $('#csr_info').slideDown();
@ -85,9 +88,9 @@ function ssl_install(elem) {
show_csr(); show_csr();
$('html, body').animate({ scrollTop: $('#ssl_install_header').offset().top }) $('html, body').animate({ scrollTop: $('#ssl_install_header').offset().top })
return false; return false;
} }
function show_csr() { function show_csr() {
api( api(
"/ssl/csr/" + $('#ssldomain').val(), "/ssl/csr/" + $('#ssldomain').val(),
"POST", "POST",
@ -96,9 +99,9 @@ function show_csr() {
function(data) { function(data) {
$('#ssl_csr').text(data); $('#ssl_csr').text(data);
}); });
} }
function install_cert() { function install_cert() {
api( api(
"/ssl/install", "/ssl/install",
"POST", "POST",
@ -114,5 +117,7 @@ function install_cert() {
show_modal_error("SSL Certificate Installation", status); show_modal_error("SSL Certificate Installation", status);
} }
}); });
} }
</script> </script>
</div>

View File

@ -1,10 +1,15 @@
<div class="container"> <div class="container-fluid">
<div class="row">
<div class="col-sm-12">
<h2>Contacts &amp; Calendar Synchronization</h2> <h2>Contacts &amp; Calendar Synchronization</h2>
<p>This box can hold your contacts and calendar, just like it holds your email.</p> <p>This box can hold your contacts and calendar, just like it holds your email.</p>
<hr> <hr>
</div>
</div>
<div class="row"> <div class="row">
<div class="col-sm-6"> <div class="col-sm-6">
<h4>In your browser</h4> <h4>In your browser</h4>
@ -19,8 +24,8 @@
<p>Log in settings are the same as with <a href="#mail-guide" onclick="return show_panel(this);">mail</a>: your <p>Log in settings are the same as with <a href="#mail-guide" onclick="return show_panel(this);">mail</a>: your
complete email address and your mail password.</p> complete email address and your mail password.</p>
</div>
</div>
<div class="col-sm-6"> <div class="col-sm-6">
<h4>On your mobile device</h4> <h4>On your mobile device</h4>
@ -44,6 +49,7 @@
<tr><td>Username</td> <td>Your complete email address.</td></tr> <tr><td>Username</td> <td>Your complete email address.</td></tr>
<tr><td>Password</td> <td>Your mail password.</td></tr> <tr><td>Password</td> <td>Your mail password.</td></tr>
</table> </table>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,23 +1,28 @@
<style> <div class="container-fluid">
#backup-status th { text-align: center; }
#backup-status tr.full-backup td { font-weight: bold; }
</style>
<h2>Backup Status</h2> <style>
#backup-status th { text-align: center; }
#backup-status tr.full-backup td { font-weight: bold; }
</style>
<h3>Copying Backup Files</h3> <div class="row">
<div class="col-sm-12">
<p>The box makes an incremental backup each night. The backup is stored on the machine itself. You are responsible for copying the backup files off of the machine.</p> <h2>Backup Status</h2>
<p>Many cloud providers make this easy by allowing you to take snapshots of the machine's disk.</p> <h3>Copying Backup Files</h3>
<p>You can also use SFTP (FTP over SSH) to copy files from <tt id="backup-location"></tt>. These files are encrpyted, so they are safe to store anywhere. Copy the encryption password from <tt id="backup-encpassword-file"></tt> also but keep it in a safe location.</p> <p>The box makes an incremental backup each night. The backup is stored on the machine itself. You are responsible for copying the backup files off of the machine.</p>
<h3>Current Backups</h3> <p>Many cloud providers make this easy by allowing you to take snapshots of the machine's disk.</p>
<p>The backup directory currently contains the backups listed below. The total size on disk of the backups is <span id="backup-total-size"></span>.</p> <p>You can also use SFTP (FTP over SSH) to copy files from <tt id="backup-location"></tt>. These files are encrpyted, so they are safe to store anywhere. Copy the encryption password from <tt id="backup-encpassword-file"></tt> also but keep it in a safe location.</p>
<table id="backup-status" class="table" style="width: auto"> <h3>Current Backups</h3>
<p>The backup directory currently contains the backups listed below. The total size on disk of the backups is <span id="backup-total-size"></span>.</p>
<table id="backup-status" class="table" style="width: auto">
<thead> <thead>
<th colspan="2">When</th> <th colspan="2">When</th>
<th>Type</th> <th>Type</th>
@ -26,12 +31,15 @@
</thead> </thead>
<tbody> <tbody>
</tbody> </tbody>
</table> </table>
<p style="margin-top: 2em"><small>The size column in the table indicates the size of the encrpyted backup, but the total size on disk shown above includes storage for unencrpyted intermediate files.</small></p> <p style="margin-top: 2em"><small>The size column in the table indicates the size of the encrpyted backup, but the total size on disk shown above includes storage for unencrpyted intermediate files.</small></p>
<script> </div>
function nice_size(bytes) { </div>
<script>
function nice_size(bytes) {
var powers = ['bytes', 'KB', 'MB', 'GB', 'TB']; var powers = ['bytes', 'KB', 'MB', 'GB', 'TB'];
while (true) { while (true) {
if (powers.length == 1) break; if (powers.length == 1) break;
@ -45,9 +53,9 @@ function nice_size(bytes) {
else else
bytes = Math.round(bytes*10)/10; bytes = Math.round(bytes*10)/10;
return bytes + " " + powers[0]; return bytes + " " + powers[0];
} }
function show_system_backup() { function show_system_backup() {
$('#backup-status tbody').html("<tr><td colspan='2' class='text-muted'>Loading...</td></tr>") $('#backup-status tbody').html("<tr><td colspan='2' class='text-muted'>Loading...</td></tr>")
api( api(
"/system/backup/status", "/system/backup/status",
@ -85,5 +93,7 @@ function show_system_backup() {
$('#backup-total-size').text(nice_size(total_disk_size)); $('#backup-total-size').text(nice_size(total_disk_size));
}) })
} }
</script> </script>
</div>

View File

@ -1,50 +1,58 @@
<h2>System Status Checks</h2> <div class="container-fluid">
<style> <style>
#system-checks .heading td { #system-checks .heading td {
font-weight: bold; font-weight: bold;
font-size: 120%; font-size: 120%;
padding-top: 1.5em; padding-top: 1.5em;
} }
#system-checks .heading.first td { #system-checks .heading.first td {
border-top: none; border-top: none;
padding-top: 0; padding-top: 0;
} }
#system-checks .status-error td { #system-checks .status-error td {
color: #733; color: #733;
} }
#system-checks .status-warning td { #system-checks .status-warning td {
color: #770; color: #770;
} }
#system-checks .status-ok td { #system-checks .status-ok td {
color: #040; color: #040;
} }
#system-checks div.extra { #system-checks div.extra {
display: none; display: none;
margin-top: 1em; margin-top: 1em;
max-width: 50em; max-width: 50em;
word-wrap: break-word; word-wrap: break-word;
} }
#system-checks a.showhide { #system-checks a.showhide {
display: none; display: none;
font-size: 85%; font-size: 85%;
} }
#system-checks .pre { #system-checks .pre {
margin: 1em; margin: 1em;
font-family: monospace; font-family: monospace;
white-space: pre-wrap; white-space: pre-wrap;
} }
</style> </style>
<table id="system-checks" class="table" style="max-width: 60em"> <div class="row">
<div class="col-sm-12">
<h2>System Status Checks</h2>
<table id="system-checks" class="table" style="max-width: 60em">
<thead> <thead>
</thead> </thead>
<tbody> <tbody>
</tbody> </tbody>
</table> </table>
<script> </div>
function show_system_status() { </div>
<script>
function show_system_status() {
$('#system-checks tbody').html("<tr><td colspan='2' class='text-muted'>Loading...</td></tr>") $('#system-checks tbody').html("<tr><td colspan='2' class='text-muted'>Loading...</td></tr>")
api( api(
"/system/status", "/system/status",
@ -82,5 +90,7 @@ function show_system_status() {
} }
} }
}) })
} }
</script> </script>
</div>

View File

@ -1,20 +1,25 @@
<h2>Users</h2> <div class="container-fluid">
<style> <style>
#user_table tr.account_inactive td.address { color: #888; text-decoration: line-through; } #user_table tr.account_inactive td.address { color: #888; text-decoration: line-through; }
#user_table .aliases { font-size: 90%; } #user_table .aliases { font-size: 90%; }
#user_table .aliases div:before { content: "⇖ "; } #user_table .aliases div:before { content: "⇖ "; }
#user_table .aliases div { } #user_table .aliases div { }
#user_table .actions { margin-top: .33em; font-size: 95%; } #user_table .actions { margin-top: .33em; font-size: 95%; }
#user_table .account_inactive .if_active { display: none; } #user_table .account_inactive .if_active { display: none; }
#user_table .account_active .if_inactive { display: none; } #user_table .account_active .if_inactive { display: none; }
</style> </style>
<h3>Add a mail user</h3> <div class="row">
<div class="col-sm-12">
<p>Add an email address to this system. This will create a new login username/password. (Use <a href="javascript:show_panel('aliases')">aliases</a> to create email addresses that forward to existing accounts.)</p> <h2>Users</h2>
<form class="form-inline" role="form" onsubmit="return do_add_user(); return false;"> <h3>Add a mail user</h3>
<p>Add an email address to this system. This will create a new login username/password. (Use <a href="javascript:show_panel('aliases')">aliases</a> to create email addresses that forward to existing accounts.)</p>
<form class="form-inline" role="form" onsubmit="return do_add_user(); return false;">
<div class="form-group"> <div class="form-group">
<label class="sr-only" for="adduserEmail">Email address</label> <label class="sr-only" for="adduserEmail">Email address</label>
<input type="email" class="form-control" id="adduserEmail" placeholder="Email Address"> <input type="email" class="form-control" id="adduserEmail" placeholder="Email Address">
@ -30,14 +35,14 @@
</select> </select>
</div> </div>
<button type="submit" class="btn btn-primary">Add User</button> <button type="submit" class="btn btn-primary">Add User</button>
</form> </form>
<p style="margin-top: .5em"><small> <p style="margin-top: .5em"><small>
Passwords must be at least four characters and may not contain spaces. Passwords must be at least four characters and may not contain spaces.
Administrators get access to this control panel. Administrators get access to this control panel.
</small></p> </small></p>
<h3>Existing mail users</h3> <h3>Existing mail users</h3>
<table id="user_table" class="table" style="width: auto"> <table id="user_table" class="table" style="width: auto">
<thead> <thead>
<tr> <tr>
<th width="50%">Email Address</th> <th width="50%">Email Address</th>
@ -47,9 +52,9 @@
</thead> </thead>
<tbody> <tbody>
</tbody> </tbody>
</table> </table>
<div style="display: none"> <div style="display: none">
<table> <table>
<tr id="user-template"> <tr id="user-template">
<td class='address'> <td class='address'>
@ -79,15 +84,17 @@
<td colspan="3" style="border-top: 0; padding-top: 0"> <td colspan="3" style="border-top: 0; padding-top: 0">
<div class='if_inactive restore_info' style='color: #888; font-size: 90%'>To restore account, create a new account with this email address. Or to permanently delete the mailbox, delete the directory <tt></tt> on the machine.</div> <div class='if_inactive restore_info' style='color: #888; font-size: 90%'>To restore account, create a new account with this email address. Or to permanently delete the mailbox, delete the directory <tt></tt> on the machine.</div>
<div class='aliases' style='display: none'> </div> <div class='aliases' style='display: none'></div>
</td> </td>
</tr> </tr>
</table> </table>
</div> </div>
</div>
</div>
<script> <script>
function show_users() { function show_users() {
$('#user_table tbody').html("<tr><td colspan='2' class='text-muted'>Loading...</td></tr>") $('#user_table tbody').html("<tr><td colspan='2' class='text-muted'>Loading...</td></tr>")
api( api(
"/mail/users", "/mail/users",
@ -148,9 +155,9 @@ function show_users() {
} }
} }
}) })
} }
function do_add_user() { function do_add_user() {
var email = $("#adduserEmail").val(); var email = $("#adduserEmail").val();
var pw = $("#adduserPassword").val(); var pw = $("#adduserPassword").val();
var privs = $("#adduserPrivs").val(); var privs = $("#adduserPrivs").val();
@ -171,9 +178,9 @@ function do_add_user() {
show_modal_error("Add User", r); show_modal_error("Add User", r);
}); });
return false; return false;
} }
function users_set_password(elem) { function users_set_password(elem) {
var email = $(elem).parents('tr').attr('data-email'); var email = $(elem).parents('tr').attr('data-email');
show_modal_confirm( show_modal_confirm(
"Archive User", "Archive User",
@ -195,9 +202,9 @@ function users_set_password(elem) {
show_modal_error("Set Password", r); show_modal_error("Set Password", r);
}); });
}); });
} }
function users_remove(elem) { function users_remove(elem) {
var email = $(elem).parents('tr').attr('data-email'); var email = $(elem).parents('tr').attr('data-email');
show_modal_confirm( show_modal_confirm(
"Archive User", "Archive User",
@ -219,9 +226,9 @@ function users_remove(elem) {
show_modal_error("Remove User", r); show_modal_error("Remove User", r);
}); });
}); });
} }
function mod_priv(elem, add_remove) { function mod_priv(elem, add_remove) {
var email = $(elem).parents('tr').attr('data-email'); var email = $(elem).parents('tr').attr('data-email');
var priv = $(elem).parents('td').find('.name').text(); var priv = $(elem).parents('td').find('.name').text();
@ -248,5 +255,7 @@ function mod_priv(elem, add_remove) {
show_users(); show_users();
}); });
}); });
} }
</script> </script>
</div>

View File

@ -1,26 +1,27 @@
<style> <div class="container-fluid">
</style> <div class="row">
<div class="col-sm-12">
<h2>Static Web Hosting</h2> <h2>Static Web Hosting</h2>
<p>This machine is serving a simple, static website at <a href="https://{{hostname}}">https://{{hostname}}</a> and at all domain names that you set up an email user or alias for.</p> <p>This machine is serving a simple, static website at <a href="https://{{hostname}}">https://{{hostname}}</a> and at all domain names that you set up an email user or alias for.</p>
<h3>Uploading web files</h3> <h3>Uploading web files</h3>
<p>You can replace the default website with your own HTML pages and other static files. This control panel won&rsquo;t help you design a website, but once you have <tt>.html</tt> files you can upload it following these instructions:</p> <p>You can replace the default website with your own HTML pages and other static files. This control panel won&rsquo;t help you design a website, but once you have <tt>.html</tt> files you can upload it following these instructions:</p>
<ol> <ol>
<li>Ensure that any domains you are publishing a website for have no problems on the <a href="#system_status" onclick="return show_panel(this);">Status Checks</a> page.</li> <li>Ensure that any domains you are publishing a website for have no problems on the <a href="#system_status" onclick="return show_panel(this);">Status Checks</a> page.</li>
<li>On your personal computer, install an SSH file transfer program such as <a href="https://filezilla-project.org/">FileZilla</a> or <a href="http://linuxcommand.org/man_pages/scp1.html">scp</a>.</li> <li>On your personal computer, install an SSH file transfer program such as <a href="https://filezilla-project.org/">FileZilla</a> or <a href="http://linuxcommand.org/man_pages/scp1.html">scp</a>.</li>
<li>Log in to this machine with the file transfer program. The server is <strong>{{hostname}}</strong>, the protocol is SSH or SFTP, and use the <strong>SSH login credentials</strong> that you used when you originally created this machine at your cloud host provider. This is <strong>not</strong> what you use to log in either for email or this control panel. Your SSH credentials probably involves a private key file.</li> <li>Log in to this machine with the file transfer program. The server is <strong>{{hostname}}</strong>, the protocol is SSH or SFTP, and use the <strong>SSH login credentials</strong> that you used when you originally created this machine at your cloud host provider. This is <strong>not</strong> what you use to log in either for email or this control panel. Your SSH credentials probably involves a private key file.</li>
<li>Upload your <tt>.html</tt> or other files to the directory <tt>{{storage_root}}/www/default</tt> on this machine. They will appear directly and immediately on the web.</li> <li>Upload your <tt>.html</tt> or other files to the directory <tt>{{storage_root}}/www/default</tt> on this machine. They will appear directly and immediately on the web.</li>
<li>The websites set up on this machine are listed in the table below with where to put the files for each website (if you have customized that, see next section).</li> <li>The websites set up on this machine are listed in the table below with where to put the files for each website (if you have customized that, see next section).</li>
<table id="web_domains_existing" class="table" style="margin-bottom: 2em; width: auto;"> <table id="web_domains_existing" class="table" style="margin-bottom: 2em; width: auto;">
<thead> <thead>
<tr> <tr>
<th>Site</th> <th>Site</th>
@ -29,19 +30,19 @@
</thead> </thead>
<tbody> <tbody>
</tbody> </tbody>
</table> </table>
<li>If you want to have this box host a static website on a domain that is not listed in the table, create a dummy <a href="#users" onclick="return show_panel(this);">mail user</a> or <a href="#aliases" onclick="return show_panel(this);">alias</a> on the domain first.</li> <li>If you want to have this box host a static website on a domain that is not listed in the table, create a dummy <a href="#users" onclick="return show_panel(this);">mail user</a> or <a href="#aliases" onclick="return show_panel(this);">alias</a> on the domain first.</li>
</ol> </ol>
<h3>Different sites for different domains</h3> <h3>Different sites for different domains</h3>
<p>Create one of the directories shown in the table below to create a space for different files for one of the websites.</p> <p>Create one of the directories shown in the table below to create a space for different files for one of the websites.</p>
<p>After you create one of these directories, click <button id="web_update" class="btn btn-primary" onclick="do_web_update()">Web Update</button> to restart the box&rsquo;s web server so that it sees the new website file location.</p> <p>After you create one of these directories, click <button id="web_update" class="btn btn-primary" onclick="do_web_update()">Web Update</button> to restart the box&rsquo;s web server so that it sees the new website file location.</p>
<table id="web_domains_custom" class="table" style="margin-bottom: 2em; width: auto;"> <table id="web_domains_custom" class="table" style="margin-bottom: 2em; width: auto;">
<thead> <thead>
<tr> <tr>
<th>Site</th> <th>Site</th>
@ -50,11 +51,13 @@
</thead> </thead>
<tbody> <tbody>
</tbody> </tbody>
</table> </table>
</div>
</div>
<script> <script>
function show_web() { function show_web() {
api( api(
"/web/domains", "/web/domains",
"GET", "GET",
@ -83,9 +86,9 @@ function show_web() {
} }
} }
}); });
} }
function do_web_update() { function do_web_update() {
api( api(
"/web/update", "/web/update",
"POST", "POST",
@ -98,5 +101,7 @@ function do_web_update() {
data = $("<pre/>").text(data); data = $("<pre/>").text(data);
show_modal_error("Web Update", data, function() { show_web() }); show_modal_error("Web Update", data, function() { show_web() });
}); });
} }
</script> </script>
</div>