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:
parent
6585384daa
commit
3e261dee66
|
@ -1,198 +1,201 @@
|
|||
<style>
|
||||
#alias_table .actions > * { padding-right: 3px; }
|
||||
#alias_table .alias-required .remove { display: none }
|
||||
</style>
|
||||
<div class="container-fluid">
|
||||
|
||||
<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;">
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-1 col-sm-11">
|
||||
<div id="alias_type_buttons" class="btn-group btn-group-xs">
|
||||
<button type="button" class="btn btn-default active">Regular</button>
|
||||
<button type="button" class="btn btn-default">Catch-All</button>
|
||||
</div>
|
||||
<div id="alias_catchall_info" class="text-info small" style="display: none; margin: .5em 0 0 0;">A catch-all alias captures all otherwise unmatched email to a domain. Enter just a part of an email address starting with the @-sign.</div>
|
||||
<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="col-sm-offset-1 col-sm-11">
|
||||
<div id="alias_type_buttons" class="btn-group btn-group-xs">
|
||||
<button type="button" class="btn btn-default active">Regular</button>
|
||||
<button type="button" class="btn btn-default">Catch-All</button>
|
||||
</div>
|
||||
<div id="alias_catchall_info" class="text-info small" style="display: none; margin: .5em 0 0 0;">A catch-all alias captures all otherwise unmatched email to a domain. Enter just a part of an email address starting with the @-sign.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="addaliasEmail" class="col-sm-1 control-label">Alias</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="email" class="form-control" id="addaliasEmail" placeholder="incoming email address (you@yourdomain.com)">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="addaliasTargets" class="col-sm-1 control-label">Forward To</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea class="form-control" rows="3" id="addaliasTargets" placeholder="forward to these email addresses (one per line or separated by commas)"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-1 col-sm-11">
|
||||
<button id="add-alias-button" type="submit" class="btn btn-primary">Add Alias</button>
|
||||
<button id="alias-cancel" class="btn btn-default hidden" onclick="aliases_reset_form(); return false;">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<h3>Existing mail aliases</h3>
|
||||
<table id="alias_table" class="table" style="width: auto">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Alias<br></th>
|
||||
<th>Forwards To</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p style="margin-top: 1.5em"><small>Hostmaster@, postmaster@ and admin@ email addresses are required on some domains.</small></p>
|
||||
|
||||
<div style="display: none">
|
||||
<table>
|
||||
<tr id="alias-template">
|
||||
<td class='actions'>
|
||||
<a href="#" onclick="aliases_edit(this); scroll_top(); return false;" class='edit' title="Edit Alias">
|
||||
<span class="glyphicon glyphicon-pencil"></span>
|
||||
</a>
|
||||
<a href="#" onclick="aliases_remove(this); return false;" class='remove' title="Remove Alias">
|
||||
<span class="glyphicon glyphicon-trash"></span>
|
||||
</a>
|
||||
</td>
|
||||
<td class='email'> </td>
|
||||
<td class='target'> </td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="addaliasEmail" class="col-sm-1 control-label">Alias</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="email" class="form-control" id="addaliasEmail" placeholder="incoming email address (you@yourdomain.com)">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="addaliasTargets" class="col-sm-1 control-label">Forward To</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea class="form-control" rows="3" id="addaliasTargets" placeholder="forward to these email addresses (one per line or separated by commas)"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-1 col-sm-11">
|
||||
<button id="add-alias-button" type="submit" class="btn btn-primary">Add Alias</button>
|
||||
<button id="alias-cancel" class="btn btn-default hidden" onclick="aliases_reset_form(); return false;">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<h3>Existing mail aliases</h3>
|
||||
<table id="alias_table" class="table" style="width: auto">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Alias<br></th>
|
||||
<th>Forwards To</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p style="margin-top: 1.5em"><small>Hostmaster@, postmaster@, and admin@ email addresses are required on some domains.</small></p>
|
||||
|
||||
<div style="display: none">
|
||||
<table>
|
||||
<tr id="alias-template">
|
||||
<td class='actions'>
|
||||
<a href="#" onclick="aliases_edit(this); scroll_top(); return false;" class='edit' title="Edit Alias">
|
||||
<span class="glyphicon glyphicon-pencil"></span>
|
||||
</a>
|
||||
<a href="#" onclick="aliases_remove(this); return false;" class='remove' title="Remove Alias">
|
||||
<span class="glyphicon glyphicon-trash"></span>
|
||||
</a>
|
||||
</td>
|
||||
<td class='email'> </td>
|
||||
<td class='target'> </td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
function show_aliases() {
|
||||
$('#alias_table tbody').html("<tr><td colspan='2' class='text-muted'>Loading...</td></tr>")
|
||||
api(
|
||||
"/mail/aliases",
|
||||
"GET",
|
||||
{ format: 'json' },
|
||||
function(r) {
|
||||
$('#alias_table tbody').html("");
|
||||
for (var i = 0; i < r.length; i++) {
|
||||
var hdr = $("<tr><td colspan='3'><h4/></td></tr>");
|
||||
hdr.find('h4').text(r[i].domain);
|
||||
$('#alias_table tbody').append(hdr);
|
||||
|
||||
for (var k = 0; k < r[i].aliases.length; k++) {
|
||||
var alias = r[i].aliases[k];
|
||||
|
||||
var n = $("#alias-template").clone();
|
||||
n.attr('id', '');
|
||||
|
||||
if (alias.required) n.addClass('alias-required');
|
||||
n.attr('data-email', alias.source);
|
||||
n.find('td.email').text(alias.source)
|
||||
for (var j = 0; j < alias.destination.length; j++)
|
||||
n.find('td.target').append($("<div></div>").text(alias.destination[j]))
|
||||
$('#alias_table tbody').append(n);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
$(function() {
|
||||
$('#alias_type_buttons button').off('click').click(function() {
|
||||
$('#alias_type_buttons button').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
if ($(this).text() == "Regular") {
|
||||
$('#addaliasEmail').attr('type', 'email');
|
||||
$('#addaliasEmail').attr('placeholder', 'incoming email address (you@yourdomain.com)');
|
||||
$('#alias_catchall_info').slideUp();
|
||||
} else {
|
||||
$('#addaliasEmail').attr('type', 'text');
|
||||
$('#addaliasEmail').attr('placeholder', 'incoming catch-all address (@yourdomain.com)');
|
||||
$('#alias_catchall_info').slideDown();
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
var is_alias_add_update = false;
|
||||
function do_add_alias() {
|
||||
var title = (!is_alias_add_update) ? "Add Alias" : "Update Alias";
|
||||
var email = $("#addaliasEmail").val();
|
||||
var targets = $("#addaliasTargets").val();
|
||||
api(
|
||||
"/mail/aliases/add",
|
||||
"POST",
|
||||
{
|
||||
update_if_exists: is_alias_add_update ? '1' : '0',
|
||||
source: email,
|
||||
destination: targets
|
||||
},
|
||||
function(r) {
|
||||
// Responses are multiple lines of pre-formatted text.
|
||||
show_modal_error(title, $("<pre/>").text(r));
|
||||
show_aliases()
|
||||
aliases_reset_form();
|
||||
},
|
||||
function(r) {
|
||||
show_modal_error(title, r);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
function aliases_reset_form() {
|
||||
$("#addaliasEmail").prop('disabled', false);
|
||||
$("#addaliasEmail").val('')
|
||||
$("#addaliasTargets").val('')
|
||||
$('#alias-cancel').addClass('hidden');
|
||||
$('#add-alias-button').text('Add Alias');
|
||||
is_alias_add_update = false;
|
||||
}
|
||||
|
||||
function aliases_edit(elem) {
|
||||
var email = $(elem).parents('tr').attr('data-email');
|
||||
var targetdivs = $(elem).parents('tr').find('.target div');
|
||||
var targets = "";
|
||||
for (var i = 0; i < targetdivs.length; i++)
|
||||
targets += $(targetdivs[i]).text() + "\n";
|
||||
|
||||
is_alias_add_update = true;
|
||||
$('#alias-cancel').removeClass('hidden');
|
||||
$("#addaliasEmail").prop('disabled', true);
|
||||
$("#addaliasEmail").val(email);
|
||||
$("#addaliasTargets").val(targets);
|
||||
$('#add-alias-button').text('Update');
|
||||
$('body').animate({ scrollTop: 0 })
|
||||
}
|
||||
|
||||
function aliases_remove(elem) {
|
||||
var email = $(elem).parents('tr').attr('data-email');
|
||||
show_modal_confirm(
|
||||
"Remove Alias",
|
||||
"Remove " + email + "?",
|
||||
"Remove",
|
||||
function() {
|
||||
<script>
|
||||
function show_aliases() {
|
||||
$('#alias_table tbody').html("<tr><td colspan='2' class='text-muted'>Loading...</td></tr>")
|
||||
api(
|
||||
"/mail/aliases/remove",
|
||||
"/mail/aliases",
|
||||
"GET",
|
||||
{ format: 'json' },
|
||||
function(r) {
|
||||
$('#alias_table tbody').html("");
|
||||
for (var i = 0; i < r.length; i++) {
|
||||
var hdr = $("<tr><td colspan='3'><h4/></td></tr>");
|
||||
hdr.find('h4').text(r[i].domain);
|
||||
$('#alias_table tbody').append(hdr);
|
||||
|
||||
for (var k = 0; k < r[i].aliases.length; k++) {
|
||||
var alias = r[i].aliases[k];
|
||||
|
||||
var n = $("#alias-template").clone();
|
||||
n.attr('id', '');
|
||||
|
||||
if (alias.required) n.addClass('alias-required');
|
||||
n.attr('data-email', alias.source);
|
||||
n.find('td.email').text(alias.source)
|
||||
for (var j = 0; j < alias.destination.length; j++)
|
||||
n.find('td.target').append($("<div></div>").text(alias.destination[j]))
|
||||
$('#alias_table tbody').append(n);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
$(function() {
|
||||
$('#alias_type_buttons button').off('click').click(function() {
|
||||
$('#alias_type_buttons button').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
if ($(this).text() == "Regular") {
|
||||
$('#addaliasEmail').attr('type', 'email');
|
||||
$('#addaliasEmail').attr('placeholder', 'incoming email address (you@yourdomain.com)');
|
||||
$('#alias_catchall_info').slideUp();
|
||||
} else {
|
||||
$('#addaliasEmail').attr('type', 'text');
|
||||
$('#addaliasEmail').attr('placeholder', 'incoming catch-all address (@yourdomain.com)');
|
||||
$('#alias_catchall_info').slideDown();
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
var is_alias_add_update = false;
|
||||
function do_add_alias() {
|
||||
var title = (!is_alias_add_update) ? "Add Alias" : "Update Alias";
|
||||
var email = $("#addaliasEmail").val();
|
||||
var targets = $("#addaliasTargets").val();
|
||||
api(
|
||||
"/mail/aliases/add",
|
||||
"POST",
|
||||
{
|
||||
source: email
|
||||
update_if_exists: is_alias_add_update ? '1' : '0',
|
||||
source: email,
|
||||
destination: targets
|
||||
},
|
||||
function(r) {
|
||||
// Responses are multiple lines of pre-formatted text.
|
||||
show_modal_error("Remove User", $("<pre/>").text(r));
|
||||
show_aliases();
|
||||
show_modal_error(title, $("<pre/>").text(r));
|
||||
show_aliases()
|
||||
aliases_reset_form();
|
||||
},
|
||||
function(r) {
|
||||
show_modal_error(title, r);
|
||||
});
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function scroll_top() {
|
||||
$('html, body').animate({
|
||||
scrollTop: $("#panel_aliases").offset().top
|
||||
}, 1000);
|
||||
}
|
||||
</script>
|
||||
function aliases_reset_form() {
|
||||
$("#addaliasEmail").prop('disabled', false);
|
||||
$("#addaliasEmail").val('')
|
||||
$("#addaliasTargets").val('')
|
||||
$('#alias-cancel').addClass('hidden');
|
||||
$('#add-alias-button').text('Add Alias');
|
||||
is_alias_add_update = false;
|
||||
}
|
||||
|
||||
function aliases_edit(elem) {
|
||||
var email = $(elem).parents('tr').attr('data-email');
|
||||
var targetdivs = $(elem).parents('tr').find('.target div');
|
||||
var targets = "";
|
||||
for (var i = 0; i < targetdivs.length; i++)
|
||||
targets += $(targetdivs[i]).text() + "\n";
|
||||
|
||||
is_alias_add_update = true;
|
||||
$('#alias-cancel').removeClass('hidden');
|
||||
$("#addaliasEmail").prop('disabled', true);
|
||||
$("#addaliasEmail").val(email);
|
||||
$("#addaliasTargets").val(targets);
|
||||
$('#add-alias-button').text('Update');
|
||||
$('body').animate({ scrollTop: 0 })
|
||||
}
|
||||
|
||||
function aliases_remove(elem) {
|
||||
var email = $(elem).parents('tr').attr('data-email');
|
||||
show_modal_confirm(
|
||||
"Remove Alias",
|
||||
"Remove " + email + "?",
|
||||
"Remove",
|
||||
function() {
|
||||
api(
|
||||
"/mail/aliases/remove",
|
||||
"POST",
|
||||
{
|
||||
source: email
|
||||
},
|
||||
function(r) {
|
||||
// Responses are multiple lines of pre-formatted text.
|
||||
show_modal_error("Remove User", $("<pre/>").text(r));
|
||||
show_aliases();
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
</div>
|
|
@ -1,99 +1,105 @@
|
|||
<style>
|
||||
</style>
|
||||
<div class="container-fluid">
|
||||
<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 “slave”) 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 “slave”) 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;">
|
||||
<div class="form-group">
|
||||
<label for="secondarydnsHostname" class="col-sm-1 control-label">Hostname</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" id="secondarydnsHostname" placeholder="ns1.cloudprovider.com">
|
||||
<form class="form-horizontal" role="form" onsubmit="do_set_secondary_dns(); return false;">
|
||||
<div class="form-group">
|
||||
<label for="secondarydnsHostname" class="col-sm-1 control-label">Hostname</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" id="secondarydnsHostname" placeholder="ns1.cloudprovider.com">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-1 col-sm-11">
|
||||
<button type="submit" class="btn btn-primary">Update</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="secondarydns-clear-instructions" class="form-group" style="display: none">
|
||||
<div class="col-sm-offset-1 col-sm-11">
|
||||
<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>
|
||||
</form>
|
||||
|
||||
<h3>Custom DNS API</h3>
|
||||
|
||||
<p>Use your box’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>
|
||||
|
||||
<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>
|
||||
|
||||
<table class="table">
|
||||
<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>password</td> <td>That user’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>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’s value. If omitted, the IPv4 address of the remote host is used. This is handy for dynamic DNS! To delete a record, use “__delete__”.</td></tr>
|
||||
</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>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>
|
||||
|
||||
<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
|
||||
|
||||
# sets an alias
|
||||
curl -d "" --user me@mydomain.com:###### https://{{hostname}}/admin/dns/set/foo.mydomain.com/cname/bar.mydomain.com
|
||||
|
||||
# clears the alias
|
||||
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
|
||||
curl -d "value=something%20here" --user me@mydomain.com:###### https://{{hostname}}/admin/dns/set/foo.mydomain.com/txt
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-1 col-sm-11">
|
||||
<button type="submit" class="btn btn-primary">Update</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="secondarydns-clear-instructions" class="form-group" style="display: none">
|
||||
<div class="col-sm-offset-1 col-sm-11">
|
||||
<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>
|
||||
</form>
|
||||
|
||||
<h3>Custom DNS API</h3>
|
||||
<script>
|
||||
function show_custom_dns() {
|
||||
api(
|
||||
"/dns/secondary-nameserver",
|
||||
"GET",
|
||||
{ },
|
||||
function(data) {
|
||||
$('#secondarydnsHostname').val(data.hostname ? data.hostname : '');
|
||||
$('#secondarydns-clear-instructions').toggle(data.hostname != null);
|
||||
});
|
||||
}
|
||||
|
||||
<p>Use your box’s DNS API to set custom DNS records on domains hosted here. For instance, you can create your own dynamic DNS service.</p>
|
||||
function do_set_secondary_dns() {
|
||||
api(
|
||||
"/dns/secondary-nameserver",
|
||||
"POST",
|
||||
{
|
||||
hostname: $('#secondarydnsHostname').val()
|
||||
},
|
||||
function(data) {
|
||||
if (data == "") return; // nothing updated
|
||||
show_modal_error("Secondary DNS", $("<pre/>").text(data));
|
||||
$('#secondarydns-clear-instructions').slideDown();
|
||||
},
|
||||
function(err) {
|
||||
show_modal_error("Secondary DNS", $("<pre/>").text(err));
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<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>
|
||||
|
||||
<h4>HTTP POST parameters</h4>
|
||||
|
||||
<table class="table">
|
||||
<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>password</td> <td>That user’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>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’s value. If omitted, the IPv4 address of the remote host is used. This is handy for dynamic DNS! To delete a record, use “__delete__”.</td></tr>
|
||||
</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>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>
|
||||
|
||||
<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
|
||||
|
||||
# sets an alias
|
||||
curl -d "" --user me@mydomain.com:###### https://{{hostname}}/admin/dns/set/foo.mydomain.com/cname/bar.mydomain.com
|
||||
|
||||
# clears the alias
|
||||
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
|
||||
curl -d "value=something%20here" --user me@mydomain.com:###### https://{{hostname}}/admin/dns/set/foo.mydomain.com/txt
|
||||
</pre>
|
||||
|
||||
<script>
|
||||
function show_custom_dns() {
|
||||
api(
|
||||
"/dns/secondary-nameserver",
|
||||
"GET",
|
||||
{ },
|
||||
function(data) {
|
||||
$('#secondarydnsHostname').val(data.hostname ? data.hostname : '');
|
||||
$('#secondarydns-clear-instructions').toggle(data.hostname != null);
|
||||
});
|
||||
}
|
||||
|
||||
function do_set_secondary_dns() {
|
||||
api(
|
||||
"/dns/secondary-nameserver",
|
||||
"POST",
|
||||
{
|
||||
hostname: $('#secondarydnsHostname').val()
|
||||
},
|
||||
function(data) {
|
||||
if (data == "") return; // nothing updated
|
||||
show_modal_error("Secondary DNS", $("<pre/>").text(data));
|
||||
$('#secondarydns-clear-instructions').slideDown();
|
||||
},
|
||||
function(err) {
|
||||
show_modal_error("Secondary DNS", $("<pre/>").text(err));
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</div>
|
|
@ -1,77 +1,87 @@
|
|||
<style>
|
||||
#external_dns_settings .heading td {
|
||||
font-weight: bold;
|
||||
font-size: 120%;
|
||||
padding-top: 1.5em;
|
||||
}
|
||||
#external_dns_settings .heading.first td {
|
||||
border-top: none;
|
||||
padding-top: 0;
|
||||
}
|
||||
#external_dns_settings .values td {
|
||||
border: 0;
|
||||
padding-top: .75em;
|
||||
padding-bottom: 0;
|
||||
max-width: 50vw;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
#external_dns_settings .explanation td {
|
||||
border: 0;
|
||||
padding-top: .5em;
|
||||
padding-bottom: .75em;
|
||||
font-style: italic;
|
||||
font-size: 95%;
|
||||
color: #777;
|
||||
}
|
||||
</style>
|
||||
<div class="container-fluid">
|
||||
|
||||
<h2>External DNS</h2>
|
||||
|
||||
<p class="text-warning">This is an advanced configuration page.</p>
|
||||
|
||||
<p>Although your box is configured to serve its own DNS, it is possible to host your DNS elsewhere — such as in the DNS control panel provided by your domain name registrar or virtual cloud provider — by copying the DNS zone information shown in the table below into your external DNS server’s control panel.</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>
|
||||
|
||||
<table id="external_dns_settings" class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>QName</th>
|
||||
<th>Type</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<script>
|
||||
function show_external_dns() {
|
||||
$('#external_dns_settings tbody').html("<tr><td colspan='2' class='text-muted'>Loading...</td></tr>")
|
||||
api(
|
||||
"/dns/dump",
|
||||
"GET",
|
||||
{ },
|
||||
function(zones) {
|
||||
$('#external_dns_settings tbody').html("");
|
||||
for (var j = 0; j < zones.length; j++) {
|
||||
var h = $("<tr class='heading'><td colspan='3'></td></tr>");
|
||||
h.find("td").text(zones[j][0]);
|
||||
$('#external_dns_settings tbody').append(h);
|
||||
|
||||
var r = zones[j][1];
|
||||
for (var i = 0; i < r.length; i++) {
|
||||
var n = $("<tr class='values'><td class='qname'/><td class='rtype'/><td class='value'/></tr>");
|
||||
n.find('.qname').text(r[i].qname);
|
||||
n.find('.rtype').text(r[i].rtype);
|
||||
n.find('.value').text(r[i].value);
|
||||
$('#external_dns_settings tbody').append(n);
|
||||
|
||||
var n = $("<tr class='explanation'><td colspan='3'/></tr>");
|
||||
n.find('td').text(r[i].explanation);
|
||||
$('#external_dns_settings tbody').append(n);
|
||||
<style>
|
||||
#external_dns_settings .heading td {
|
||||
font-weight: bold;
|
||||
font-size: 120%;
|
||||
padding-top: 1.5em;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
#external_dns_settings .heading.first td {
|
||||
border-top: none;
|
||||
padding-top: 0;
|
||||
}
|
||||
#external_dns_settings .values td {
|
||||
border: 0;
|
||||
padding-top: .75em;
|
||||
padding-bottom: 0;
|
||||
max-width: 50vw;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
#external_dns_settings .explanation td {
|
||||
border: 0;
|
||||
padding-top: .5em;
|
||||
padding-bottom: .75em;
|
||||
font-style: italic;
|
||||
font-size: 95%;
|
||||
color: #777;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
|
||||
<h2>External DNS</h2>
|
||||
|
||||
<p class="text-warning">This is an advanced configuration page.</p>
|
||||
|
||||
<p>Although your box is configured to serve its own DNS, it is possible to host your DNS elsewhere — such as in the DNS control panel provided by your domain name registrar or virtual cloud provider — by copying the DNS zone information shown in the table below into your external DNS server’s control panel.</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>
|
||||
|
||||
<table id="external_dns_settings" class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>QName</th>
|
||||
<th>Type</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function show_external_dns() {
|
||||
$('#external_dns_settings tbody').html("<tr><td colspan='2' class='text-muted'>Loading...</td></tr>")
|
||||
api(
|
||||
"/dns/dump",
|
||||
"GET",
|
||||
{ },
|
||||
function(zones) {
|
||||
$('#external_dns_settings tbody').html("");
|
||||
for (var j = 0; j < zones.length; j++) {
|
||||
var h = $("<tr class='heading'><td colspan='3'></td></tr>");
|
||||
h.find("td").text(zones[j][0]);
|
||||
$('#external_dns_settings tbody').append(h);
|
||||
|
||||
var r = zones[j][1];
|
||||
for (var i = 0; i < r.length; i++) {
|
||||
var n = $("<tr class='values'><td class='qname'/><td class='rtype'/><td class='value'/></tr>");
|
||||
n.find('.qname').text(r[i].qname);
|
||||
n.find('.rtype').text(r[i].rtype);
|
||||
n.find('.value').text(r[i].value);
|
||||
$('#external_dns_settings tbody').append(n);
|
||||
|
||||
var n = $("<tr class='explanation'><td colspan='3'/></tr>");
|
||||
n.find('td').text(r[i].explanation);
|
||||
$('#external_dns_settings tbody').append(n);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -17,13 +17,43 @@
|
|||
@import url(https://fonts.googleapis.com/css?family=Raleway:400,700);
|
||||
@import url(https://fonts.googleapis.com/css?family=Ubuntu:300);
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
html {
|
||||
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 {
|
||||
padding-top: 50px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
footer {
|
||||
padding-top: 10px;
|
||||
font: bold italic medium;
|
||||
border-top: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
p {
|
||||
|
@ -61,9 +91,9 @@
|
|||
margin: 1.5em 0;
|
||||
}
|
||||
|
||||
ol li {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
ol li {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
|
||||
<style>
|
||||
|
@ -73,99 +103,111 @@
|
|||
<!--[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>
|
||||
<![endif]-->
|
||||
<div class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="#">{{hostname}}</a>
|
||||
<div id="wrap">
|
||||
<div class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="#">{{hostname}}</a>
|
||||
</div>
|
||||
<div class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">System <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#system_status" onclick="return show_panel(this);">Status Checks</a></li>
|
||||
<li><a href="#ssl" onclick="return show_panel(this);">SSL Certificates</a></li>
|
||||
<li><a href="#system_backup" onclick="return show_panel(this);">Backup Status</a></li>
|
||||
<li class="divider"></li>
|
||||
<li class="dropdown-header">Super Advanced Options</li>
|
||||
<li><a href="#custom_dns" onclick="return show_panel(this);">Custom DNS</a></li>
|
||||
<li><a href="#external_dns" onclick="return show_panel(this);">External DNS</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Mail <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#mail-guide" onclick="return show_panel(this);">Instructions</a></li>
|
||||
<li><a href="#users" onclick="return show_panel(this);">Users</a></li>
|
||||
<li><a href="#aliases" onclick="return show_panel(this);">Aliases</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#sync_guide" onclick="return show_panel(this);">Contacts/Calendar</a></li>
|
||||
<li><a href="#web" onclick="return show_panel(this);">Web</a></li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li><a href="#" onclick="do_logout(); return false;" style="color: white">Log out?</a></li>
|
||||
</ul>
|
||||
</div><!--/.navbar-collapse -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div id="panel_system_status" class="panel">
|
||||
{% include "system-status.html" %}
|
||||
</div>
|
||||
|
||||
<div id="panel_system_backup" class="panel">
|
||||
{% include "system-backup.html" %}
|
||||
</div>
|
||||
|
||||
<div id="panel_external_dns" class="panel">
|
||||
{% include "external-dns.html" %}
|
||||
</div>
|
||||
|
||||
<div id="panel_custom_dns" class="panel">
|
||||
{% include "custom-dns.html" %}
|
||||
</div>
|
||||
|
||||
<div id="panel_login" class="panel">
|
||||
{% include "login.html" %}
|
||||
</div>
|
||||
|
||||
<div id="panel_mail-guide" class="panel">
|
||||
{% include "mail-guide.html" %}
|
||||
</div>
|
||||
|
||||
<div id="panel_users" class="panel">
|
||||
{% include "users.html" %}
|
||||
</div>
|
||||
|
||||
<div id="panel_aliases" class="panel">
|
||||
{% include "aliases.html" %}
|
||||
</div>
|
||||
|
||||
<div id="panel_sync_guide" class="panel">
|
||||
{% include "sync-guide.html" %}
|
||||
</div>
|
||||
|
||||
<div id="panel_web" class="panel">
|
||||
{% include "web.html" %}
|
||||
</div>
|
||||
|
||||
<div id="panel_ssl" class="panel">
|
||||
{% include "ssl.html" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="push"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<footer>
|
||||
<p>This is a <a href="https://mailinabox.email">Mail-in-a-Box</a>.</p>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">System <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#system_status" onclick="return show_panel(this);">Status Checks</a></li>
|
||||
<li><a href="#ssl" onclick="return show_panel(this);">SSL Certificates</a></li>
|
||||
<li><a href="#system_backup" onclick="return show_panel(this);">Backup Status</a></li>
|
||||
<li class="divider"></li>
|
||||
<li class="dropdown-header">Super Advanced Options</li>
|
||||
<li><a href="#custom_dns" onclick="return show_panel(this);">Custom DNS</a></li>
|
||||
<li><a href="#external_dns" onclick="return show_panel(this);">External DNS</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Mail <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#mail-guide" onclick="return show_panel(this);">Instructions</a></li>
|
||||
<li><a href="#users" onclick="return show_panel(this);">Users</a></li>
|
||||
<li><a href="#aliases" onclick="return show_panel(this);">Aliases</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#sync_guide" onclick="return show_panel(this);">Contacts/Calendar</a></li>
|
||||
<li><a href="#web" onclick="return show_panel(this);">Web</a></li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li><a href="#" onclick="do_logout(); return false;" style="color: white">Log out?</a></li>
|
||||
</ul>
|
||||
</div><!--/.navbar-collapse -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div id="panel_system_status" class="container panel">
|
||||
{% include "system-status.html" %}
|
||||
</div>
|
||||
|
||||
<div id="panel_system_backup" class="container panel">
|
||||
{% include "system-backup.html" %}
|
||||
</div>
|
||||
|
||||
<div id="panel_external_dns" class="container panel">
|
||||
{% include "external-dns.html" %}
|
||||
</div>
|
||||
|
||||
<div id="panel_custom_dns" class="container panel">
|
||||
{% include "custom-dns.html" %}
|
||||
</div>
|
||||
|
||||
<div id="panel_login" class="panel">
|
||||
{% include "login.html" %}
|
||||
</div>
|
||||
|
||||
<div id="panel_mail-guide" class="container panel">
|
||||
{% include "mail-guide.html" %}
|
||||
</div>
|
||||
|
||||
<div id="panel_users" class="container panel">
|
||||
{% include "users.html" %}
|
||||
</div>
|
||||
|
||||
<div id="panel_aliases" class="container panel">
|
||||
{% include "aliases.html" %}
|
||||
</div>
|
||||
|
||||
<div id="panel_sync_guide" class="container panel">
|
||||
{% include "sync-guide.html" %}
|
||||
</div>
|
||||
|
||||
<div id="panel_web" class="container panel">
|
||||
{% include "web.html" %}
|
||||
</div>
|
||||
|
||||
<div id="panel_ssl" class="container panel">
|
||||
{% include "ssl.html" %}
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<footer>
|
||||
<p>This is a <a href="https://mailinabox.email">Mail-in-a-Box</a>.</p>
|
||||
</footer>
|
||||
</div> <!-- /container -->
|
||||
|
||||
<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">
|
||||
|
|
|
@ -1,118 +1,128 @@
|
|||
<h1 style="margin: 1em; text-align: center">{{hostname}}</h1>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
|
||||
{% if no_admins_exist %}
|
||||
<div class="container">
|
||||
<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,
|
||||
log into this machine using SSH (like when you first set it up) and run:</p>
|
||||
<pre>cd mailinabox
|
||||
sudo tools/mail.py user make-admin your@emailaddress.com</pre>
|
||||
<hr>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<h1 style="margin: 1em; text-align: center">{{hostname}}</h1>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-offset-2 col-sm-8 col-md-offset-3 col-md-6 col-lg-offset-4 col-lg-4">
|
||||
<center>
|
||||
<p style="margin: 2em">Log in here for your Mail-in-a-Box control panel.</p>
|
||||
</center>
|
||||
|
||||
<form class="form-horizontal" role="form" onsubmit="do_login(); return false;">
|
||||
<div class="form-group">
|
||||
<label for="inputEmail3" class="col-sm-2 control-label">Email</label>
|
||||
<div class="col-sm-10">
|
||||
<input name="email" type="email" class="form-control" id="loginEmail" placeholder="Email">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputPassword3" class="col-sm-2 control-label">Password</label>
|
||||
<div class="col-sm-10">
|
||||
<input name="password" type="password" class="form-control" id="loginPassword" placeholder="Password">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input name='remember' type="checkbox" id="loginRemember"> Remember me
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" class="btn btn-default">Sign in</button>
|
||||
</div>
|
||||
{% if no_admins_exist %}
|
||||
<div class="row">
|
||||
<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,
|
||||
log into this machine using SSH (like when you first set it up) and run:</p>
|
||||
<pre>cd mailinabox
|
||||
sudo tools/mail.py user make-admin your@emailaddress.com</pre>
|
||||
<hr>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</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>
|
||||
<p style="margin: 2em">Log in here for your Mail-in-a-Box control panel.</p>
|
||||
</center>
|
||||
|
||||
<script>
|
||||
function do_login() {
|
||||
if ($('#loginEmail').val() == "") {
|
||||
show_modal_error("Login Failed", "Enter your email address.")
|
||||
return false;
|
||||
}
|
||||
if ($('#loginPassword').val() == "") {
|
||||
show_modal_error("Login Failed", "Enter your email password.")
|
||||
return false;
|
||||
}
|
||||
<form class="form-horizontal" role="form" onsubmit="do_login(); return false;">
|
||||
<div class="form-group">
|
||||
<label for="inputEmail3" class="col-sm-2 control-label">Email</label>
|
||||
<div class="col-sm-10">
|
||||
<input name="email" type="email" class="form-control" id="loginEmail" placeholder="Email">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputPassword3" class="col-sm-2 control-label">Password</label>
|
||||
<div class="col-sm-10">
|
||||
<input name="password" type="password" class="form-control" id="loginPassword" placeholder="Password">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input name='remember' type="checkbox" id="loginRemember"> Remember me
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" class="btn btn-default">Sign in</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
// Exchange the email address & password for an API key.
|
||||
api_credentials = [$('#loginEmail').val(), $('#loginPassword').val()]
|
||||
</div>
|
||||
</div>
|
||||
|
||||
api(
|
||||
"/me",
|
||||
"GET",
|
||||
{ },
|
||||
function(response){
|
||||
// This API call always succeeds. It returns a JSON object indicating
|
||||
// whether the request was authenticated or not.
|
||||
if (response.status != "authorized") {
|
||||
// Show why the login failed.
|
||||
show_modal_error("Login Failed", response.reason)
|
||||
|
||||
// Reset any saved credentials.
|
||||
do_logout();
|
||||
|
||||
} else {
|
||||
// Login succeeded.
|
||||
|
||||
// Save the new credentials.
|
||||
api_credentials = [response.api_key, ""];
|
||||
|
||||
// Try to wipe the username/password information.
|
||||
$('#loginEmail').val('');
|
||||
$('#loginPassword').val('');
|
||||
|
||||
// Remember the credentials.
|
||||
if (typeof localStorage != 'undefined' && typeof sessionStorage != 'undefined') {
|
||||
if ($('#loginRemember').val()) {
|
||||
localStorage.setItem("miab-cp-credentials", api_credentials.join(":"));
|
||||
sessionStorage.removeItem("miab-cp-credentials");
|
||||
} else {
|
||||
localStorage.removeItem("miab-cp-credentials");
|
||||
sessionStorage.setItem("miab-cp-credentials", api_credentials.join(":"));
|
||||
}
|
||||
<script>
|
||||
function do_login() {
|
||||
if ($('#loginEmail').val() == "") {
|
||||
show_modal_error("Login Failed", "Enter your email address.")
|
||||
return false;
|
||||
}
|
||||
if ($('#loginPassword').val() == "") {
|
||||
show_modal_error("Login Failed", "Enter your email password.")
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open the next panel the user wants to go to. Do this after the XHR response
|
||||
// is over so that we don't start a new XHR request while this one is finishing,
|
||||
// which confuses the loading indicator.
|
||||
setTimeout(function() { show_panel(!switch_back_to_panel ? 'system_status' : switch_back_to_panel) }, 300);
|
||||
}
|
||||
})
|
||||
}
|
||||
// Exchange the email address & password for an API key.
|
||||
api_credentials = [$('#loginEmail').val(), $('#loginPassword').val()]
|
||||
|
||||
function do_logout() {
|
||||
api_credentials = ["", ""];
|
||||
if (typeof localStorage != 'undefined')
|
||||
localStorage.removeItem("miab-cp-credentials");
|
||||
if (typeof sessionStorage != 'undefined')
|
||||
sessionStorage.removeItem("miab-cp-credentials");
|
||||
show_panel('login');
|
||||
}
|
||||
</script>
|
||||
api(
|
||||
"/me",
|
||||
"GET",
|
||||
{ },
|
||||
function(response){
|
||||
// This API call always succeeds. It returns a JSON object indicating
|
||||
// whether the request was authenticated or not.
|
||||
if (response.status != "authorized") {
|
||||
// Show why the login failed.
|
||||
show_modal_error("Login Failed", response.reason)
|
||||
|
||||
// Reset any saved credentials.
|
||||
do_logout();
|
||||
|
||||
} else {
|
||||
// Login succeeded.
|
||||
|
||||
// Save the new credentials.
|
||||
api_credentials = [response.api_key, ""];
|
||||
|
||||
// Try to wipe the username/password information.
|
||||
$('#loginEmail').val('');
|
||||
$('#loginPassword').val('');
|
||||
|
||||
// Remember the credentials.
|
||||
if (typeof localStorage != 'undefined' && typeof sessionStorage != 'undefined') {
|
||||
if ($('#loginRemember').val()) {
|
||||
localStorage.setItem("miab-cp-credentials", api_credentials.join(":"));
|
||||
sessionStorage.removeItem("miab-cp-credentials");
|
||||
} else {
|
||||
localStorage.removeItem("miab-cp-credentials");
|
||||
sessionStorage.setItem("miab-cp-credentials", api_credentials.join(":"));
|
||||
}
|
||||
}
|
||||
|
||||
// Open the next panel the user wants to go to. Do this after the XHR response
|
||||
// is over so that we don't start a new XHR request while this one is finishing,
|
||||
// which confuses the loading indicator.
|
||||
setTimeout(function() { show_panel(!switch_back_to_panel ? 'system_status' : switch_back_to_panel) }, 300);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function do_logout() {
|
||||
api_credentials = ["", ""];
|
||||
if (typeof localStorage != 'undefined')
|
||||
localStorage.removeItem("miab-cp-credentials");
|
||||
if (typeof sessionStorage != 'undefined')
|
||||
sessionStorage.removeItem("miab-cp-credentials");
|
||||
show_panel('login');
|
||||
}
|
||||
</script>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
<style>#panel_mail-guide table.table { width: auto; margin-left: .5em; }</style>
|
||||
<div class="container-fluid">
|
||||
|
||||
<div class="container">
|
||||
<h2 style="margin-bottom: 0">Checking and Sending Mail</h2>
|
||||
<style>#panel_mail-guide table.table { width: auto; margin-left: .5em; }</style>
|
||||
|
||||
<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="col-sm-6">
|
||||
|
||||
<h3>How to log in</h3>
|
||||
|
||||
<p>Your username and password are the same no matter how you check your mail:</p>
|
||||
|
@ -28,41 +35,45 @@
|
|||
<li>On iOS devices, look for Exchange or ActiveSync.</li>
|
||||
</ul>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<h4>IMAP/SMTP settings</h4>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
|
||||
<p>This method is preferred on Android devices but is not available on iOS devices.</p>
|
||||
<h4>IMAP/SMTP settings</h4>
|
||||
|
||||
<p>Your mail server is <strong>{{hostname}}</strong>. Use the following settings when prompted:</p>
|
||||
<p>This method is preferred on Android devices but is not available on iOS devices.</p>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr><th>Protocol</th> <th>Port</th> <th>Options</th></tr>
|
||||
</thead>
|
||||
<tr><th>IMAP</th> <td>993</td> <td>SSL</td></tr>
|
||||
<tr><th>SMTP</th> <td>587</td> <td>STARTTLS <span>(“always” or “required”, if prompted)</span></td></tr>
|
||||
</table>
|
||||
<p>Your mail server is <strong>{{hostname}}</strong>. Use the following settings when prompted:</p>
|
||||
|
||||
<p>In addition to setting up your email, you’ll also need to set up <a href="#sync_guide" onclick="return show_panel(this);">contacts and calendar synchronization</a> separately.</p>
|
||||
</div>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr><th>Protocol</th> <th>Port</th> <th>Options</th></tr>
|
||||
</thead>
|
||||
<tr><th>IMAP</th> <td>993</td> <td>SSL</td></tr>
|
||||
<tr><th>SMTP</th> <td>587</td> <td>STARTTLS <span>(“always” or “required”, if prompted)</span></td></tr>
|
||||
</table>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<h4>Exchange/ActiveSync settings</h4>
|
||||
<p>In addition to setting up your email, you’ll also need to set up <a href="#sync_guide" onclick="return show_panel(this);">contacts and calendar synchronization</a> separately.</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>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
|
||||
<table class="table">
|
||||
<tr><th>Server</th> <td>{{hostname}}</td></tr>
|
||||
<tr><th>Options</th> <td>Secure Connection</td></tr>
|
||||
</table>
|
||||
<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>
|
||||
|
||||
<table class="table">
|
||||
<tr><th>Server</th> <td>{{hostname}}</td></tr>
|
||||
<tr><th>Options</th> <td>Secure Connection</td></tr>
|
||||
</table>
|
||||
|
||||
<p>Your device should also provide a contacts list and calendar that syncs to this box when you use this method.</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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 class="col-sm-6">
|
||||
|
||||
<h3>Other information about mail on your box</h3>
|
||||
|
||||
<h4>Greylisting</h4>
|
||||
|
@ -70,6 +81,7 @@
|
|||
|
||||
<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>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,118 +1,123 @@
|
|||
<style>
|
||||
</style>
|
||||
<div class="container-fluid">
|
||||
<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;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Domain</th>
|
||||
<th>Certificate Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table id="ssl_domains" class="table" style="margin-bottom: 2em; width: auto;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Domain</th>
|
||||
<th>Certificate Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</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’s $9 certificate</a> or <a href="https://www.startssl.com/">StartSSL’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’s $9 certificate</a> or <a href="https://www.startssl.com/">StartSSL’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">
|
||||
<p>You will need to provide the SSL certificate provider this Certificate Signing Request (CSR):</p>
|
||||
|
||||
<div id="csr_info" style="display: none">
|
||||
<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>
|
||||
<p><small>The CSR is safe to share. It can only be used in combination with a secret key stored on this machine.</small></p>
|
||||
|
||||
<p><small>The CSR is safe to share. It can only be used in combination with a secret key stored on this machine.</small></p>
|
||||
<p>The SSL certificate provider will then provide you with an SSL certificate. They may also provide you with an intermediate chain. Paste each separately into the boxes below:</p>
|
||||
|
||||
<p>The SSL certificate provider will then provide you with an SSL certificate. They may also provide you with an intermediate chain. Paste each separately into the boxes below:</p>
|
||||
<p style="margin-bottom: .5em">SSL certificate:</p>
|
||||
<p><textarea id="ssl_paste_cert" class="form-control" style="max-width: 40em; height: 8em" placeholder="-----BEGIN CERTIFICATE-----
stuff here
-----END CERTIFICATE-----"></textarea></p>
|
||||
|
||||
<p style="margin-bottom: .5em">SSL certificate:</p>
|
||||
<p><textarea id="ssl_paste_cert" class="form-control" style="max-width: 40em; height: 8em" placeholder="-----BEGIN CERTIFICATE-----
stuff here
-----END CERTIFICATE-----"></textarea></p>
|
||||
<p style="margin-bottom: .5em">SSL intermediate chain (if provided):</p>
|
||||
<p><textarea id="ssl_paste_chain" class="form-control" style="max-width: 40em; height: 8em" placeholder="-----BEGIN CERTIFICATE-----
stuff here
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
more stuff here
-----END CERTIFICATE-----"></textarea></p>
|
||||
|
||||
<p style="margin-bottom: .5em">SSL intermediate chain (if provided):</p>
|
||||
<p><textarea id="ssl_paste_chain" class="form-control" style="max-width: 40em; height: 8em" placeholder="-----BEGIN CERTIFICATE-----
stuff here
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
more stuff here
-----END CERTIFICATE-----"></textarea></p>
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<button class="btn-primary" onclick="install_cert()">Install</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function show_ssl() {
|
||||
api(
|
||||
"/web/domains",
|
||||
"GET",
|
||||
{
|
||||
},
|
||||
function(domains) {
|
||||
var tb = $('#ssl_domains tbody');
|
||||
tb.text('');
|
||||
$('#ssldomain').html('<option value="">(select)</option>');
|
||||
<script>
|
||||
function show_ssl() {
|
||||
api(
|
||||
"/web/domains",
|
||||
"GET",
|
||||
{
|
||||
},
|
||||
function(domains) {
|
||||
var tb = $('#ssl_domains tbody');
|
||||
tb.text('');
|
||||
$('#ssldomain').html('<option value="">(select)</option>');
|
||||
|
||||
for (var i = 0; i < domains.length; i++) {
|
||||
var row = $("<tr><th class='domain'><a href=''></a></th><td class='status'></td> <td class='actions'><a href='#' onclick='return ssl_install(this);' class='btn btn-xs'>Install Certificate</a></td></tr>");
|
||||
tb.append(row);
|
||||
row.attr('data-domain', domains[i].domain);
|
||||
row.find('.domain a').text(domains[i].domain);
|
||||
row.find('.domain a').attr('href', 'https://' + domains[i].domain);
|
||||
row.addClass("text-" + domains[i].ssl_certificate[0]);
|
||||
row.find('.status').text(domains[i].ssl_certificate[1]);
|
||||
if (domains[i].ssl_certificate[0] == "success") {
|
||||
row.find('.actions a').addClass('btn-default').text('Replace Certificate');
|
||||
} else {
|
||||
row.find('.actions a').addClass('btn-primary').text('Install Certificate');
|
||||
}
|
||||
for (var i = 0; i < domains.length; i++) {
|
||||
var row = $("<tr><th class='domain'><a href=''></a></th><td class='status'></td> <td class='actions'><a href='#' onclick='return ssl_install(this);' class='btn btn-xs'>Install Certificate</a></td></tr>");
|
||||
tb.append(row);
|
||||
row.attr('data-domain', domains[i].domain);
|
||||
row.find('.domain a').text(domains[i].domain);
|
||||
row.find('.domain a').attr('href', 'https://' + domains[i].domain);
|
||||
row.addClass("text-" + domains[i].ssl_certificate[0]);
|
||||
row.find('.status').text(domains[i].ssl_certificate[1]);
|
||||
if (domains[i].ssl_certificate[0] == "success") {
|
||||
row.find('.actions a').addClass('btn-default').text('Replace Certificate');
|
||||
} else {
|
||||
row.find('.actions a').addClass('btn-primary').text('Install Certificate');
|
||||
}
|
||||
|
||||
$('#ssldomain').append($('<option>').text(domains[i].domain));
|
||||
}
|
||||
});
|
||||
}
|
||||
$('#ssldomain').append($('<option>').text(domains[i].domain));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function ssl_install(elem) {
|
||||
var domain = $(elem).parents('tr').attr('data-domain');
|
||||
$('#ssldomain').val(domain);
|
||||
$('#csr_info').slideDown();
|
||||
$('#ssl_csr').text('Loading...');
|
||||
show_csr();
|
||||
$('html, body').animate({ scrollTop: $('#ssl_install_header').offset().top })
|
||||
return false;
|
||||
}
|
||||
function ssl_install(elem) {
|
||||
var domain = $(elem).parents('tr').attr('data-domain');
|
||||
$('#ssldomain').val(domain);
|
||||
$('#csr_info').slideDown();
|
||||
$('#ssl_csr').text('Loading...');
|
||||
show_csr();
|
||||
$('html, body').animate({ scrollTop: $('#ssl_install_header').offset().top })
|
||||
return false;
|
||||
}
|
||||
|
||||
function show_csr() {
|
||||
api(
|
||||
"/ssl/csr/" + $('#ssldomain').val(),
|
||||
"POST",
|
||||
{
|
||||
},
|
||||
function(data) {
|
||||
$('#ssl_csr').text(data);
|
||||
});
|
||||
}
|
||||
function show_csr() {
|
||||
api(
|
||||
"/ssl/csr/" + $('#ssldomain').val(),
|
||||
"POST",
|
||||
{
|
||||
},
|
||||
function(data) {
|
||||
$('#ssl_csr').text(data);
|
||||
});
|
||||
}
|
||||
|
||||
function install_cert() {
|
||||
api(
|
||||
"/ssl/install",
|
||||
"POST",
|
||||
{
|
||||
domain: $('#ssldomain').val(),
|
||||
cert: $('#ssl_paste_cert').val(),
|
||||
chain: $('#ssl_paste_chain').val()
|
||||
},
|
||||
function(status) {
|
||||
if (status == "") {
|
||||
show_modal_error("SSL Certificate Installation", "Certificate has been installed. Check that you have no connection problems to the domain.", function() { show_ssl(); $('#csr_info').slideUp(); });
|
||||
} else {
|
||||
show_modal_error("SSL Certificate Installation", status);
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
function install_cert() {
|
||||
api(
|
||||
"/ssl/install",
|
||||
"POST",
|
||||
{
|
||||
domain: $('#ssldomain').val(),
|
||||
cert: $('#ssl_paste_cert').val(),
|
||||
chain: $('#ssl_paste_chain').val()
|
||||
},
|
||||
function(status) {
|
||||
if (status == "") {
|
||||
show_modal_error("SSL Certificate Installation", "Certificate has been installed. Check that you have no connection problems to the domain.", function() { show_ssl(); $('#csr_info').slideUp(); });
|
||||
} else {
|
||||
show_modal_error("SSL Certificate Installation", status);
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
</div>
|
|
@ -1,49 +1,55 @@
|
|||
<div class="container">
|
||||
<h2>Contacts & Calendar Synchronization</h2>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
|
||||
<p>This box can hold your contacts and calendar, just like it holds your email.</p>
|
||||
<h2>Contacts & Calendar Synchronization</h2>
|
||||
|
||||
<hr>
|
||||
<p>This box can hold your contacts and calendar, just like it holds your email.</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<h4>In your browser</h4>
|
||||
<hr>
|
||||
|
||||
<p>You can edit your contacts and calendar from your web browser.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<h4>In your browser</h4>
|
||||
|
||||
<table class="table">
|
||||
<thead><tr><th>For...</th> <th>Visit this URL</th></tr></thead>
|
||||
<tr><th>Contacts</td> <td><a href="https://{{hostname}}/cloud/contacts">https://{{hostname}}/cloud/contacts</a></td></tr>
|
||||
<tr><th>Calendar</td> <td><a href="https://{{hostname}}/cloud/calendar">https://{{hostname}}/cloud/calendar</a></td></tr>
|
||||
</table>
|
||||
<p>You can edit your contacts and calendar from your web browser.</p>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
<table class="table">
|
||||
<thead><tr><th>For...</th> <th>Visit this URL</th></tr></thead>
|
||||
<tr><th>Contacts</td> <td><a href="https://{{hostname}}/cloud/contacts">https://{{hostname}}/cloud/contacts</a></td></tr>
|
||||
<tr><th>Calendar</td> <td><a href="https://{{hostname}}/cloud/calendar">https://{{hostname}}/cloud/calendar</a></td></tr>
|
||||
</table>
|
||||
|
||||
<div class="col-sm-6">
|
||||
<h4>On your mobile device</h4>
|
||||
<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>
|
||||
|
||||
<p>If you set up your <a href="#mail-guide" onclick="return show_panel(this);">mail</a> using Exchange/ActiveSync,
|
||||
your contacts and calendar may already appear on your device.</p>
|
||||
<p>Otherwise, here are some apps that can synchronize your contacts and calendar to your Android phone.</p>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<h4>On your mobile device</h4>
|
||||
|
||||
<table class="table">
|
||||
<thead><tr><th>For...</th> <th>Use...</th></tr></thead>
|
||||
<tr><td>Contacts and Calendar</td> <td><a href="https://play.google.com/store/apps/details?id=at.bitfire.davdroid">DAVdroid</a> ($3.69; free <a href="https://f-droid.org/repository/browse/?fdfilter=dav&fdid=at.bitfire.davdroid">here</a>)</td></tr>
|
||||
<tr><td>Only Contacts</td> <td><a href="https://play.google.com/store/apps/details?id=org.dmfs.carddav.sync">CardDAV-Sync free beta</a> (free)</td></tr>
|
||||
<tr><td>Only Calendar</td> <td><a href="https://play.google.com/store/apps/details?id=org.dmfs.caldav.lib">CalDAV-Sync</a> ($2.89)</td></tr>
|
||||
</table>
|
||||
<p>If you set up your <a href="#mail-guide" onclick="return show_panel(this);">mail</a> using Exchange/ActiveSync,
|
||||
your contacts and calendar may already appear on your device.</p>
|
||||
<p>Otherwise, here are some apps that can synchronize your contacts and calendar to your Android phone.</p>
|
||||
|
||||
<p>Use the following settings:</p>
|
||||
<table class="table">
|
||||
<thead><tr><th>For...</th> <th>Use...</th></tr></thead>
|
||||
<tr><td>Contacts and Calendar</td> <td><a href="https://play.google.com/store/apps/details?id=at.bitfire.davdroid">DAVdroid</a> ($3.69; free <a href="https://f-droid.org/repository/browse/?fdfilter=dav&fdid=at.bitfire.davdroid">here</a>)</td></tr>
|
||||
<tr><td>Only Contacts</td> <td><a href="https://play.google.com/store/apps/details?id=org.dmfs.carddav.sync">CardDAV-Sync free beta</a> (free)</td></tr>
|
||||
<tr><td>Only Calendar</td> <td><a href="https://play.google.com/store/apps/details?id=org.dmfs.caldav.lib">CalDAV-Sync</a> ($2.89)</td></tr>
|
||||
</table>
|
||||
|
||||
<table class="table">
|
||||
<tr><td>Account Type</td> <td>CardDAV or CalDAV</td></tr>
|
||||
<tr><td>Server Name</td> <td>{{hostname}}</td></tr>
|
||||
<tr><td>Use SSL</td> <td>Yes</td></tr>
|
||||
<tr><td>Username</td> <td>Your complete email address.</td></tr>
|
||||
<tr><td>Password</td> <td>Your mail password.</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<p>Use the following settings:</p>
|
||||
|
||||
<table class="table">
|
||||
<tr><td>Account Type</td> <td>CardDAV or CalDAV</td></tr>
|
||||
<tr><td>Server Name</td> <td>{{hostname}}</td></tr>
|
||||
<tr><td>Use SSL</td> <td>Yes</td></tr>
|
||||
<tr><td>Username</td> <td>Your complete email address.</td></tr>
|
||||
<tr><td>Password</td> <td>Your mail password.</td></tr>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,89 +1,99 @@
|
|||
<style>
|
||||
#backup-status th { text-align: center; }
|
||||
#backup-status tr.full-backup td { font-weight: bold; }
|
||||
</style>
|
||||
<div class="container-fluid">
|
||||
|
||||
<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">
|
||||
<thead>
|
||||
<th colspan="2">When</th>
|
||||
<th>Type</th>
|
||||
<th>Size</th>
|
||||
<th>Deleted in...</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3>Current Backups</h3>
|
||||
|
||||
<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>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>
|
||||
|
||||
<script>
|
||||
function nice_size(bytes) {
|
||||
var powers = ['bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
while (true) {
|
||||
if (powers.length == 1) break;
|
||||
if (bytes < 1000) break;
|
||||
bytes /= 1024;
|
||||
powers.shift();
|
||||
}
|
||||
// round to have three significant figures but at most one decimal place
|
||||
if (bytes >= 100)
|
||||
bytes = Math.round(bytes)
|
||||
else
|
||||
bytes = Math.round(bytes*10)/10;
|
||||
return bytes + " " + powers[0];
|
||||
}
|
||||
<table id="backup-status" class="table" style="width: auto">
|
||||
<thead>
|
||||
<th colspan="2">When</th>
|
||||
<th>Type</th>
|
||||
<th>Size</th>
|
||||
<th>Deleted in...</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
function show_system_backup() {
|
||||
$('#backup-status tbody').html("<tr><td colspan='2' class='text-muted'>Loading...</td></tr>")
|
||||
api(
|
||||
"/system/backup/status",
|
||||
"GET",
|
||||
{ },
|
||||
function(r) {
|
||||
$('#backup-location').text(r.encdirectory);
|
||||
$('#backup-encpassword-file').text(r.encpwfile);
|
||||
<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>
|
||||
|
||||
$('#backup-status tbody').html("");
|
||||
var total_disk_size = 0;
|
||||
</div>
|
||||
</div>
|
||||
|
||||
if (r.backups.length == 0) {
|
||||
var tr = $('<tr><td colspan="3">No backups have been made yet.</td></tr>');
|
||||
$('#backup-status tbody').append(tr);
|
||||
<script>
|
||||
function nice_size(bytes) {
|
||||
var powers = ['bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
while (true) {
|
||||
if (powers.length == 1) break;
|
||||
if (bytes < 1000) break;
|
||||
bytes /= 1024;
|
||||
powers.shift();
|
||||
}
|
||||
// round to have three significant figures but at most one decimal place
|
||||
if (bytes >= 100)
|
||||
bytes = Math.round(bytes)
|
||||
else
|
||||
bytes = Math.round(bytes*10)/10;
|
||||
return bytes + " " + powers[0];
|
||||
}
|
||||
|
||||
for (var i = 0; i < r.backups.length; i++) {
|
||||
var b = r.backups[i];
|
||||
var tr = $('<tr/>');
|
||||
if (b.full) tr.addClass("full-backup");
|
||||
tr.append( $('<td/>').text(b.date_str + " " + r.tz) );
|
||||
tr.append( $('<td/>').text(b.date_delta + " ago") );
|
||||
tr.append( $('<td/>').text(b.full ? "full" : "increment") );
|
||||
tr.append( $('<td style="text-align: right"/>').text( nice_size(b.encsize)) );
|
||||
if (b.deleted_in)
|
||||
tr.append( $('<td/>').text(b.deleted_in) );
|
||||
else
|
||||
tr.append( $('<td class="text-muted">n/a</td>') );
|
||||
$('#backup-status tbody').append(tr);
|
||||
function show_system_backup() {
|
||||
$('#backup-status tbody').html("<tr><td colspan='2' class='text-muted'>Loading...</td></tr>")
|
||||
api(
|
||||
"/system/backup/status",
|
||||
"GET",
|
||||
{ },
|
||||
function(r) {
|
||||
$('#backup-location').text(r.encdirectory);
|
||||
$('#backup-encpassword-file').text(r.encpwfile);
|
||||
|
||||
total_disk_size += b.size;
|
||||
total_disk_size += b.encsize;
|
||||
}
|
||||
$('#backup-status tbody').html("");
|
||||
var total_disk_size = 0;
|
||||
|
||||
$('#backup-total-size').text(nice_size(total_disk_size));
|
||||
})
|
||||
}
|
||||
</script>
|
||||
if (r.backups.length == 0) {
|
||||
var tr = $('<tr><td colspan="3">No backups have been made yet.</td></tr>');
|
||||
$('#backup-status tbody').append(tr);
|
||||
}
|
||||
|
||||
for (var i = 0; i < r.backups.length; i++) {
|
||||
var b = r.backups[i];
|
||||
var tr = $('<tr/>');
|
||||
if (b.full) tr.addClass("full-backup");
|
||||
tr.append( $('<td/>').text(b.date_str + " " + r.tz) );
|
||||
tr.append( $('<td/>').text(b.date_delta + " ago") );
|
||||
tr.append( $('<td/>').text(b.full ? "full" : "increment") );
|
||||
tr.append( $('<td style="text-align: right"/>').text( nice_size(b.encsize)) );
|
||||
if (b.deleted_in)
|
||||
tr.append( $('<td/>').text(b.deleted_in) );
|
||||
else
|
||||
tr.append( $('<td class="text-muted">n/a</td>') );
|
||||
$('#backup-status tbody').append(tr);
|
||||
|
||||
total_disk_size += b.size;
|
||||
total_disk_size += b.encsize;
|
||||
}
|
||||
|
||||
$('#backup-total-size').text(nice_size(total_disk_size));
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
</div>
|
|
@ -1,86 +1,96 @@
|
|||
<h2>System Status Checks</h2>
|
||||
<div class="container-fluid">
|
||||
|
||||
<style>
|
||||
#system-checks .heading td {
|
||||
font-weight: bold;
|
||||
font-size: 120%;
|
||||
padding-top: 1.5em;
|
||||
}
|
||||
#system-checks .heading.first td {
|
||||
border-top: none;
|
||||
padding-top: 0;
|
||||
}
|
||||
#system-checks .status-error td {
|
||||
color: #733;
|
||||
}
|
||||
#system-checks .status-warning td {
|
||||
color: #770;
|
||||
}
|
||||
#system-checks .status-ok td {
|
||||
color: #040;
|
||||
}
|
||||
#system-checks div.extra {
|
||||
display: none;
|
||||
margin-top: 1em;
|
||||
max-width: 50em;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
#system-checks a.showhide {
|
||||
display: none;
|
||||
font-size: 85%;
|
||||
}
|
||||
#system-checks .pre {
|
||||
margin: 1em;
|
||||
font-family: monospace;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
<table id="system-checks" class="table" style="max-width: 60em">
|
||||
<thead>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<script>
|
||||
function show_system_status() {
|
||||
$('#system-checks tbody').html("<tr><td colspan='2' class='text-muted'>Loading...</td></tr>")
|
||||
api(
|
||||
"/system/status",
|
||||
"POST",
|
||||
{ },
|
||||
function(r) {
|
||||
$('#system-checks tbody').html("");
|
||||
for (var i = 0; i < r.length; i++) {
|
||||
var n = $("<tr><td class='status'/><td class='message'><p style='margin: 0'/><div class='extra'/><a class='showhide' href='#'/></tr>");
|
||||
if (i == 0) n.addClass('first')
|
||||
if (r[i].type == "heading")
|
||||
n.addClass(r[i].type)
|
||||
else
|
||||
n.addClass("status-" + r[i].type)
|
||||
if (r[i].type == "ok") n.find('td.status').text("✓")
|
||||
if (r[i].type == "error") n.find('td.status').text("✖")
|
||||
if (r[i].type == "warning") n.find('td.status').text("?")
|
||||
n.find('td.message p').text(r[i].text)
|
||||
$('#system-checks tbody').append(n);
|
||||
|
||||
if (r[i].extra.length > 0) {
|
||||
n.find('a.showhide').show().text("show more").click(function() {
|
||||
$(this).hide();
|
||||
$(this).parent().find('.extra').fadeIn();
|
||||
return false;
|
||||
});
|
||||
<style>
|
||||
#system-checks .heading td {
|
||||
font-weight: bold;
|
||||
font-size: 120%;
|
||||
padding-top: 1.5em;
|
||||
}
|
||||
|
||||
for (var j = 0; j < r[i].extra.length; j++) {
|
||||
|
||||
var m = $("<div/>").text(r[i].extra[j].text)
|
||||
if (r[i].extra[j].monospace)
|
||||
m.addClass("pre");
|
||||
n.find('> td.message > div').append(m);
|
||||
#system-checks .heading.first td {
|
||||
border-top: none;
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
#system-checks .status-error td {
|
||||
color: #733;
|
||||
}
|
||||
#system-checks .status-warning td {
|
||||
color: #770;
|
||||
}
|
||||
#system-checks .status-ok td {
|
||||
color: #040;
|
||||
}
|
||||
#system-checks div.extra {
|
||||
display: none;
|
||||
margin-top: 1em;
|
||||
max-width: 50em;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
#system-checks a.showhide {
|
||||
display: none;
|
||||
font-size: 85%;
|
||||
}
|
||||
#system-checks .pre {
|
||||
margin: 1em;
|
||||
font-family: monospace;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
<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>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function show_system_status() {
|
||||
$('#system-checks tbody').html("<tr><td colspan='2' class='text-muted'>Loading...</td></tr>")
|
||||
api(
|
||||
"/system/status",
|
||||
"POST",
|
||||
{ },
|
||||
function(r) {
|
||||
$('#system-checks tbody').html("");
|
||||
for (var i = 0; i < r.length; i++) {
|
||||
var n = $("<tr><td class='status'/><td class='message'><p style='margin: 0'/><div class='extra'/><a class='showhide' href='#'/></tr>");
|
||||
if (i == 0) n.addClass('first')
|
||||
if (r[i].type == "heading")
|
||||
n.addClass(r[i].type)
|
||||
else
|
||||
n.addClass("status-" + r[i].type)
|
||||
if (r[i].type == "ok") n.find('td.status').text("✓")
|
||||
if (r[i].type == "error") n.find('td.status').text("✖")
|
||||
if (r[i].type == "warning") n.find('td.status').text("?")
|
||||
n.find('td.message p').text(r[i].text)
|
||||
$('#system-checks tbody').append(n);
|
||||
|
||||
if (r[i].extra.length > 0) {
|
||||
n.find('a.showhide').show().text("show more").click(function() {
|
||||
$(this).hide();
|
||||
$(this).parent().find('.extra').fadeIn();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
for (var j = 0; j < r[i].extra.length; j++) {
|
||||
|
||||
var m = $("<div/>").text(r[i].extra[j].text)
|
||||
if (r[i].extra[j].monospace)
|
||||
m.addClass("pre");
|
||||
n.find('> td.message > div').append(m);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
</div>
|
|
@ -1,252 +1,261 @@
|
|||
<h2>Users</h2>
|
||||
<div class="container-fluid">
|
||||
|
||||
<style>
|
||||
#user_table tr.account_inactive td.address { color: #888; text-decoration: line-through; }
|
||||
#user_table .aliases { font-size: 90%; }
|
||||
#user_table .aliases div:before { content: "⇖ "; }
|
||||
#user_table .aliases div { }
|
||||
#user_table .actions { margin-top: .33em; font-size: 95%; }
|
||||
#user_table .account_inactive .if_active { display: none; }
|
||||
#user_table .account_active .if_inactive { display: none; }
|
||||
</style>
|
||||
<style>
|
||||
#user_table tr.account_inactive td.address { color: #888; text-decoration: line-through; }
|
||||
#user_table .aliases { font-size: 90%; }
|
||||
#user_table .aliases div:before { content: "⇖ "; }
|
||||
#user_table .aliases div { }
|
||||
#user_table .actions { margin-top: .33em; font-size: 95%; }
|
||||
#user_table .account_inactive .if_active { display: none; }
|
||||
#user_table .account_active .if_inactive { display: none; }
|
||||
</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;">
|
||||
<div class="form-group">
|
||||
<label class="sr-only" for="adduserEmail">Email address</label>
|
||||
<input type="email" class="form-control" id="adduserEmail" placeholder="Email Address">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="sr-only" for="adduserPassword">Password</label>
|
||||
<input type="password" class="form-control" id="adduserPassword" placeholder="Password">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<select class="form-control" id="adduserPrivs">
|
||||
<option value="">Normal User</option>
|
||||
<option value="admin">Administrator</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Add User</button>
|
||||
</form>
|
||||
<p style="margin-top: .5em"><small>
|
||||
Passwords must be at least four characters and may not contain spaces.
|
||||
Administrators get access to this control panel.
|
||||
</small></p>
|
||||
<h3>Add a mail user</h3>
|
||||
|
||||
<h3>Existing mail users</h3>
|
||||
<table id="user_table" class="table" style="width: auto">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="50%">Email Address</th>
|
||||
<th>Actions</th>
|
||||
<th>Mailbox Size</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
<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>
|
||||
|
||||
<div style="display: none">
|
||||
<table>
|
||||
<tr id="user-template">
|
||||
<td class='address'>
|
||||
</td>
|
||||
<td class='actions'>
|
||||
<span class='privs'>
|
||||
</span>
|
||||
<form class="form-inline" role="form" onsubmit="return do_add_user(); return false;">
|
||||
<div class="form-group">
|
||||
<label class="sr-only" for="adduserEmail">Email address</label>
|
||||
<input type="email" class="form-control" id="adduserEmail" placeholder="Email Address">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="sr-only" for="adduserPassword">Password</label>
|
||||
<input type="password" class="form-control" id="adduserPassword" placeholder="Password">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<select class="form-control" id="adduserPrivs">
|
||||
<option value="">Normal User</option>
|
||||
<option value="admin">Administrator</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Add User</button>
|
||||
</form>
|
||||
<p style="margin-top: .5em"><small>
|
||||
Passwords must be at least four characters and may not contain spaces.
|
||||
Administrators get access to this control panel.
|
||||
</small></p>
|
||||
|
||||
<span class="if_active">
|
||||
<a href="#" onclick="users_set_password(this); return false;" class='setpw' title="Set Password">
|
||||
set password
|
||||
</a>
|
||||
|
|
||||
</span>
|
||||
<h3>Existing mail users</h3>
|
||||
<table id="user_table" class="table" style="width: auto">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="50%">Email Address</th>
|
||||
<th>Actions</th>
|
||||
<th>Mailbox Size</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<span class='add-privs'>
|
||||
</span>
|
||||
<div style="display: none">
|
||||
<table>
|
||||
<tr id="user-template">
|
||||
<td class='address'>
|
||||
</td>
|
||||
<td class='actions'>
|
||||
<span class='privs'>
|
||||
</span>
|
||||
|
||||
<a href="#" onclick="users_remove(this); return false;" class='if_active' title="Archive Account">
|
||||
archive account
|
||||
</a>
|
||||
</td>
|
||||
<td class='mailboxsize'>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="user-extra-template">
|
||||
<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>
|
||||
<span class="if_active">
|
||||
<a href="#" onclick="users_set_password(this); return false;" class='setpw' title="Set Password">
|
||||
set password
|
||||
</a>
|
||||
|
|
||||
</span>
|
||||
|
||||
<div class='aliases' style='display: none'> </div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<span class='add-privs'>
|
||||
</span>
|
||||
|
||||
<a href="#" onclick="users_remove(this); return false;" class='if_active' title="Archive Account">
|
||||
archive account
|
||||
</a>
|
||||
</td>
|
||||
<td class='mailboxsize'>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="user-extra-template">
|
||||
<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>
|
||||
|
||||
<script>
|
||||
function show_users() {
|
||||
$('#user_table tbody').html("<tr><td colspan='2' class='text-muted'>Loading...</td></tr>")
|
||||
api(
|
||||
"/mail/users",
|
||||
"GET",
|
||||
{ format: 'json' },
|
||||
function(r) {
|
||||
$('#user_table tbody').html("");
|
||||
for (var i = 0; i < r.length; i++) {
|
||||
var hdr = $("<tr><td><h4/></td></tr>");
|
||||
hdr.find('h4').text(r[i].domain);
|
||||
$('#user_table tbody').append(hdr);
|
||||
<div class='aliases' style='display: none'></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
for (var k = 0; k < r[i].users.length; k++) {
|
||||
var user = r[i].users[k];
|
||||
</div>
|
||||
</div>
|
||||
|
||||
var n = $("#user-template").clone();
|
||||
var n2 = $("#user-extra-template").clone();
|
||||
n.attr('id', '');
|
||||
n2.attr('id', '');
|
||||
$('#user_table tbody').append(n);
|
||||
$('#user_table tbody').append(n2);
|
||||
<script>
|
||||
function show_users() {
|
||||
$('#user_table tbody').html("<tr><td colspan='2' class='text-muted'>Loading...</td></tr>")
|
||||
api(
|
||||
"/mail/users",
|
||||
"GET",
|
||||
{ format: 'json' },
|
||||
function(r) {
|
||||
$('#user_table tbody').html("");
|
||||
for (var i = 0; i < r.length; i++) {
|
||||
var hdr = $("<tr><td><h4/></td></tr>");
|
||||
hdr.find('h4').text(r[i].domain);
|
||||
$('#user_table tbody').append(hdr);
|
||||
|
||||
n.addClass("account_" + user.status);
|
||||
n2.addClass("account_" + user.status);
|
||||
for (var k = 0; k < r[i].users.length; k++) {
|
||||
var user = r[i].users[k];
|
||||
|
||||
n.attr('data-email', user.email);
|
||||
n.find('.address').text(user.email)
|
||||
n.find('.mailboxsize').text(nice_size(user.mailbox_size))
|
||||
n2.find('.restore_info tt').text(user.mailbox);
|
||||
var n = $("#user-template").clone();
|
||||
var n2 = $("#user-extra-template").clone();
|
||||
n.attr('id', '');
|
||||
n2.attr('id', '');
|
||||
$('#user_table tbody').append(n);
|
||||
$('#user_table tbody').append(n2);
|
||||
|
||||
if (user.status == 'inactive') continue;
|
||||
n.addClass("account_" + user.status);
|
||||
n2.addClass("account_" + user.status);
|
||||
|
||||
var add_privs = ["admin"];
|
||||
n.attr('data-email', user.email);
|
||||
n.find('.address').text(user.email)
|
||||
n.find('.mailboxsize').text(nice_size(user.mailbox_size))
|
||||
n2.find('.restore_info tt').text(user.mailbox);
|
||||
|
||||
for (var j = 0; j < user.privileges.length; j++) {
|
||||
var p = $("<span><b><span class='name'></span></b> (<a href='#' onclick='mod_priv(this, \"remove\"); return false;' title='Remove Privilege'>remove privilege</a>) |</span>");
|
||||
p.find('span.name').text(user.privileges[j]);
|
||||
n.find('.privs').append(p);
|
||||
if (add_privs.indexOf(user.privileges[j]) >= 0)
|
||||
add_privs.splice(add_privs.indexOf(user.privileges[j]), 1);
|
||||
}
|
||||
if (user.status == 'inactive') continue;
|
||||
|
||||
for (var j = 0; j < add_privs.length; j++) {
|
||||
var p = $("<span><a href='#' onclick='mod_priv(this, \"add\"); return false;' title='Add Privilege'>make <span class='name'></span></a> | </span>");
|
||||
p.find('span.name').text(add_privs[j]);
|
||||
n.find('.add-privs').append(p);
|
||||
}
|
||||
var add_privs = ["admin"];
|
||||
|
||||
if (user.aliases && user.aliases.length > 0) {
|
||||
n2.find('.aliases').show();
|
||||
for (var j = 0; j < user.aliases.length; j++) {
|
||||
n2.find('.aliases').append($("<div/>").text(
|
||||
user.aliases[j][0]
|
||||
+ (user.aliases[j][1].length > 0 ? " ⇐ " + user.aliases[j][1].join(", ") : "")
|
||||
))
|
||||
for (var j = 0; j < user.privileges.length; j++) {
|
||||
var p = $("<span><b><span class='name'></span></b> (<a href='#' onclick='mod_priv(this, \"remove\"); return false;' title='Remove Privilege'>remove privilege</a>) |</span>");
|
||||
p.find('span.name').text(user.privileges[j]);
|
||||
n.find('.privs').append(p);
|
||||
if (add_privs.indexOf(user.privileges[j]) >= 0)
|
||||
add_privs.splice(add_privs.indexOf(user.privileges[j]), 1);
|
||||
}
|
||||
|
||||
for (var j = 0; j < add_privs.length; j++) {
|
||||
var p = $("<span><a href='#' onclick='mod_priv(this, \"add\"); return false;' title='Add Privilege'>make <span class='name'></span></a> | </span>");
|
||||
p.find('span.name').text(add_privs[j]);
|
||||
n.find('.add-privs').append(p);
|
||||
}
|
||||
|
||||
if (user.aliases && user.aliases.length > 0) {
|
||||
n2.find('.aliases').show();
|
||||
for (var j = 0; j < user.aliases.length; j++) {
|
||||
n2.find('.aliases').append($("<div/>").text(
|
||||
user.aliases[j][0]
|
||||
+ (user.aliases[j][1].length > 0 ? " ⇐ " + user.aliases[j][1].join(", ") : "")
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function do_add_user() {
|
||||
var email = $("#adduserEmail").val();
|
||||
var pw = $("#adduserPassword").val();
|
||||
var privs = $("#adduserPrivs").val();
|
||||
api(
|
||||
"/mail/users/add",
|
||||
"POST",
|
||||
{
|
||||
email: email,
|
||||
password: pw,
|
||||
privileges: privs
|
||||
},
|
||||
function(r) {
|
||||
// Responses are multiple lines of pre-formatted text.
|
||||
show_modal_error("Add User", $("<pre/>").text(r));
|
||||
show_users()
|
||||
},
|
||||
function(r) {
|
||||
show_modal_error("Add User", r);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
function users_set_password(elem) {
|
||||
var email = $(elem).parents('tr').attr('data-email');
|
||||
show_modal_confirm(
|
||||
"Archive User",
|
||||
$("<p>Set a new password for <b>" + email + "</b>?</p> <p><label for='users_set_password_pw' style='display: block; font-weight: normal'>New Password:</label><input type='password' id='users_set_password_pw'></p><p><small>Passwords must be at least four characters and may not contain spaces.</small></p>"),
|
||||
"Set Password",
|
||||
function() {
|
||||
api(
|
||||
"/mail/users/password",
|
||||
"POST",
|
||||
{
|
||||
email: email,
|
||||
password: $('#users_set_password_pw').val()
|
||||
},
|
||||
function(r) {
|
||||
// Responses are multiple lines of pre-formatted text.
|
||||
show_modal_error("Set Password", $("<pre/>").text(r));
|
||||
},
|
||||
function(r) {
|
||||
show_modal_error("Set Password", r);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function users_remove(elem) {
|
||||
var email = $(elem).parents('tr').attr('data-email');
|
||||
show_modal_confirm(
|
||||
"Archive User",
|
||||
$("<p>Are you sure you want to archive <b>" + email + "</b>?</p> <p>The user's mailboxes will not be deleted (you can do that later), but the user will no longer be able to log into any services on this machine.</p>"),
|
||||
"Archive",
|
||||
function() {
|
||||
api(
|
||||
"/mail/users/remove",
|
||||
"POST",
|
||||
{
|
||||
email: email
|
||||
},
|
||||
function(r) {
|
||||
// Responses are multiple lines of pre-formatted text.
|
||||
show_modal_error("Remove User", $("<pre/>").text(r));
|
||||
show_users();
|
||||
},
|
||||
function(r) {
|
||||
show_modal_error("Remove User", r);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function mod_priv(elem, add_remove) {
|
||||
var email = $(elem).parents('tr').attr('data-email');
|
||||
var priv = $(elem).parents('td').find('.name').text();
|
||||
|
||||
// can't remove your own admin access
|
||||
if (priv == "admin" && add_remove == "remove" && api_credentials != null && email == api_credentials[0]) {
|
||||
show_modal_error("Modify Privileges", "You cannot remove the admin privilege from yourself.");
|
||||
return;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function do_add_user() {
|
||||
var email = $("#adduserEmail").val();
|
||||
var pw = $("#adduserPassword").val();
|
||||
var privs = $("#adduserPrivs").val();
|
||||
api(
|
||||
"/mail/users/add",
|
||||
"POST",
|
||||
{
|
||||
email: email,
|
||||
password: pw,
|
||||
privileges: privs
|
||||
},
|
||||
function(r) {
|
||||
// Responses are multiple lines of pre-formatted text.
|
||||
show_modal_error("Add User", $("<pre/>").text(r));
|
||||
show_users()
|
||||
},
|
||||
function(r) {
|
||||
show_modal_error("Add User", r);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
function users_set_password(elem) {
|
||||
var email = $(elem).parents('tr').attr('data-email');
|
||||
show_modal_confirm(
|
||||
"Archive User",
|
||||
$("<p>Set a new password for <b>" + email + "</b>?</p> <p><label for='users_set_password_pw' style='display: block; font-weight: normal'>New Password:</label><input type='password' id='users_set_password_pw'></p><p><small>Passwords must be at least four characters and may not contain spaces.</small></p>"),
|
||||
"Set Password",
|
||||
function() {
|
||||
api(
|
||||
"/mail/users/password",
|
||||
"POST",
|
||||
{
|
||||
email: email,
|
||||
password: $('#users_set_password_pw').val()
|
||||
},
|
||||
function(r) {
|
||||
// Responses are multiple lines of pre-formatted text.
|
||||
show_modal_error("Set Password", $("<pre/>").text(r));
|
||||
},
|
||||
function(r) {
|
||||
show_modal_error("Set Password", r);
|
||||
var add_remove1 = add_remove.charAt(0).toUpperCase() + add_remove.substring(1);
|
||||
show_modal_confirm(
|
||||
"Modify Privileges",
|
||||
"Are you sure you want to " + add_remove + " the " + priv + " privilege for <b>" + email + "</b>?",
|
||||
add_remove1,
|
||||
function() {
|
||||
api(
|
||||
"/mail/users/privileges/" + add_remove,
|
||||
"POST",
|
||||
{
|
||||
email: email,
|
||||
privilege: priv
|
||||
},
|
||||
function(r) {
|
||||
show_users();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
function users_remove(elem) {
|
||||
var email = $(elem).parents('tr').attr('data-email');
|
||||
show_modal_confirm(
|
||||
"Archive User",
|
||||
$("<p>Are you sure you want to archive <b>" + email + "</b>?</p> <p>The user's mailboxes will not be deleted (you can do that later), but the user will no longer be able to log into any services on this machine.</p>"),
|
||||
"Archive",
|
||||
function() {
|
||||
api(
|
||||
"/mail/users/remove",
|
||||
"POST",
|
||||
{
|
||||
email: email
|
||||
},
|
||||
function(r) {
|
||||
// Responses are multiple lines of pre-formatted text.
|
||||
show_modal_error("Remove User", $("<pre/>").text(r));
|
||||
show_users();
|
||||
},
|
||||
function(r) {
|
||||
show_modal_error("Remove User", r);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function mod_priv(elem, add_remove) {
|
||||
var email = $(elem).parents('tr').attr('data-email');
|
||||
var priv = $(elem).parents('td').find('.name').text();
|
||||
|
||||
// can't remove your own admin access
|
||||
if (priv == "admin" && add_remove == "remove" && api_credentials != null && email == api_credentials[0]) {
|
||||
show_modal_error("Modify Privileges", "You cannot remove the admin privilege from yourself.");
|
||||
return;
|
||||
}
|
||||
|
||||
var add_remove1 = add_remove.charAt(0).toUpperCase() + add_remove.substring(1);
|
||||
show_modal_confirm(
|
||||
"Modify Privileges",
|
||||
"Are you sure you want to " + add_remove + " the " + priv + " privilege for <b>" + email + "</b>?",
|
||||
add_remove1,
|
||||
function() {
|
||||
api(
|
||||
"/mail/users/privileges/" + add_remove,
|
||||
"POST",
|
||||
{
|
||||
email: email,
|
||||
privilege: priv
|
||||
},
|
||||
function(r) {
|
||||
show_users();
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</div>
|
|
@ -1,102 +1,107 @@
|
|||
<style>
|
||||
</style>
|
||||
<div class="container-fluid">
|
||||
<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’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’t help you design a website, but once you have <tt>.html</tt> files you can upload it following these instructions:</p>
|
||||
|
||||
<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>
|
||||
<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>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;">
|
||||
<thead>
|
||||
<table id="web_domains_existing" class="table" style="margin-bottom: 2em; width: auto;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Site</th>
|
||||
<th>Directory for Files</th>
|
||||
<th>Site</th>
|
||||
<th>Directory for Files</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</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’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’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;">
|
||||
<thead>
|
||||
<tr>
|
||||
<table id="web_domains_custom" class="table" style="margin-bottom: 2em; width: auto;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Site</th>
|
||||
<th>Create Directory</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function show_web() {
|
||||
api(
|
||||
"/web/domains",
|
||||
"GET",
|
||||
{
|
||||
},
|
||||
function(domains) {
|
||||
var tb = $('#web_domains_existing tbody');
|
||||
tb.text('');
|
||||
for (var i = 0; i < domains.length; i++) {
|
||||
var row = $("<tr><th class='domain'><a href=''></a></th><td class='directory'><tt/></td></tr>");
|
||||
tb.append(row);
|
||||
row.find('.domain a').text('https://' + domains[i].domain);
|
||||
row.find('.domain a').attr('href', 'https://' + domains[i].domain);
|
||||
row.find('.directory tt').text(domains[i].root);
|
||||
}
|
||||
<script>
|
||||
function show_web() {
|
||||
api(
|
||||
"/web/domains",
|
||||
"GET",
|
||||
{
|
||||
},
|
||||
function(domains) {
|
||||
var tb = $('#web_domains_existing tbody');
|
||||
tb.text('');
|
||||
for (var i = 0; i < domains.length; i++) {
|
||||
var row = $("<tr><th class='domain'><a href=''></a></th><td class='directory'><tt/></td></tr>");
|
||||
tb.append(row);
|
||||
row.find('.domain a').text('https://' + domains[i].domain);
|
||||
row.find('.domain a').attr('href', 'https://' + domains[i].domain);
|
||||
row.find('.directory tt').text(domains[i].root);
|
||||
}
|
||||
|
||||
tb = $('#web_domains_custom tbody');
|
||||
tb.text('');
|
||||
for (var i = 0; i < domains.length; i++) {
|
||||
if (domains[i].root != domains[i].custom_root) {
|
||||
var row = $("<tr><th class='domain'><a href=''></a></th><td class='directory'><tt></td></tr>");
|
||||
tb.append(row);
|
||||
row.find('.domain a').text('https://' + domains[i].domain);
|
||||
row.find('.domain a').attr('href', 'https://' + domains[i].domain);
|
||||
row.find('.directory tt').text(domains[i].custom_root);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
tb = $('#web_domains_custom tbody');
|
||||
tb.text('');
|
||||
for (var i = 0; i < domains.length; i++) {
|
||||
if (domains[i].root != domains[i].custom_root) {
|
||||
var row = $("<tr><th class='domain'><a href=''></a></th><td class='directory'><tt></td></tr>");
|
||||
tb.append(row);
|
||||
row.find('.domain a').text('https://' + domains[i].domain);
|
||||
row.find('.domain a').attr('href', 'https://' + domains[i].domain);
|
||||
row.find('.directory tt').text(domains[i].custom_root);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function do_web_update() {
|
||||
api(
|
||||
"/web/update",
|
||||
"POST",
|
||||
{
|
||||
},
|
||||
function(data) {
|
||||
if (data == "")
|
||||
data = "Nothing changed.";
|
||||
else
|
||||
data = $("<pre/>").text(data);
|
||||
show_modal_error("Web Update", data, function() { show_web() });
|
||||
});
|
||||
}
|
||||
</script>
|
||||
function do_web_update() {
|
||||
api(
|
||||
"/web/update",
|
||||
"POST",
|
||||
{
|
||||
},
|
||||
function(data) {
|
||||
if (data == "")
|
||||
data = "Nothing changed.";
|
||||
else
|
||||
data = $("<pre/>").text(data);
|
||||
show_modal_error("Web Update", data, function() { show_web() });
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
</div>
|
Loading…
Reference in New Issue