mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-18 05:51:26 +02:00
Layout and design improvements :
* New way to list messages * Infinite scrolling for the webmail see #585
This commit is contained in:
@@ -89,7 +89,7 @@ def parse_date(value, **kwargs):
|
||||
ndate = datetime.datetime(*(tmp)[:7])
|
||||
now = datetime.datetime.now()
|
||||
if now - ndate > datetime.timedelta(7):
|
||||
return ndate.strftime("%d.%m.%Y %H:%M")
|
||||
return ndate.strftime("%d/%m/%Y %H:%M")
|
||||
return ndate.strftime("%a %H:%M")
|
||||
except ValueError:
|
||||
return value
|
||||
|
||||
@@ -649,15 +649,12 @@ class IMAPconnector(object):
|
||||
if not r'\Seen' in data[int(uid)]['FLAGS']:
|
||||
msg['style'] = 'unseen'
|
||||
if r'\Answered' in data[int(uid)]['FLAGS']:
|
||||
msg['img_flags'] = [static_url('pics/answered.png')]
|
||||
msg['answered'] = True
|
||||
if r'$Forwarded' in data[int(uid)]['FLAGS']:
|
||||
if 'img_flags' in msg:
|
||||
msg['img_flags'] += [static_url('pics/forwarded.png')]
|
||||
else:
|
||||
msg['img_flags'] = [static_url('pics/forwarded.png')]
|
||||
msg['forwarded'] = True
|
||||
bs = BodyStructure(data[int(uid)]['BODYSTRUCTURE'])
|
||||
if bs.has_attachments():
|
||||
msg['img_withatts'] = static_url('pics/attachment.png')
|
||||
msg['attachments'] = True
|
||||
result += [msg]
|
||||
return result
|
||||
|
||||
|
||||
@@ -14,25 +14,25 @@ class WMtable(tables.Table):
|
||||
styles = "table-hover"
|
||||
idkey = "imapid"
|
||||
drag = tables.ImgColumn(
|
||||
"drag", cssclass="draggable left", width="1%",
|
||||
"drag", cssclass="draggable left",
|
||||
defvalue="%spics/grippy.png" % settings.STATIC_URL,
|
||||
header="<input type='checkbox' name='toggleselect' id='toggleselect' />"
|
||||
)
|
||||
selection = tables.SelectionColumn(
|
||||
"selection", safe=True, width='1%', header=None, sortable=False
|
||||
"selection", safe=True, header=None, sortable=False
|
||||
)
|
||||
flags = tables.ImgColumn("flags", width="3%")
|
||||
withatts = tables.ImgColumn("withatts", width="2%")
|
||||
flags = tables.ImgColumn("flags")
|
||||
withatts = tables.ImgColumn("withatts")
|
||||
subject = tables.Column(
|
||||
"subject", label=ugettext_lazy("Subject"), limit=60,
|
||||
cssclass="openable"
|
||||
)
|
||||
from_ = tables.Column(
|
||||
"from", width="20%", label=ugettext_lazy("From"), limit=30,
|
||||
"from", label=ugettext_lazy("From"), limit=30,
|
||||
cssclass="openable"
|
||||
)
|
||||
date = tables.Column(
|
||||
"date", width="15%", label=ugettext_lazy("Date"), cssclass="openable"
|
||||
"date", label=ugettext_lazy("Date"), cssclass="openable"
|
||||
)
|
||||
|
||||
cols_order = [
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
/* MEDIA */
|
||||
#listing {
|
||||
position: absolute;
|
||||
overflow: auto;
|
||||
top: 60px;
|
||||
padding: 0;
|
||||
top: 75px;
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
overflow: auto;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
td[name=drag], td[name=selection] {
|
||||
width: 1%;
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
@@ -21,7 +27,7 @@
|
||||
@media (max-width: 500px) {
|
||||
th#date,
|
||||
td[name="date"],
|
||||
#menubar .Actions{
|
||||
#menubar .actions {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -38,7 +44,7 @@
|
||||
|
||||
@media (min-width: 768px) {
|
||||
td[name=from] {
|
||||
width: 20%;
|
||||
width: 25%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +54,7 @@
|
||||
}
|
||||
th#date,
|
||||
td[name="date"]{
|
||||
max-width: 60px;
|
||||
max-width: 100px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
@@ -71,9 +77,6 @@
|
||||
|
||||
@media (min-width: 1198px) {
|
||||
|
||||
#listing {
|
||||
padding-left: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
/* GENERAL */
|
||||
@@ -82,14 +85,18 @@ body, html {
|
||||
}
|
||||
|
||||
#rightcol {
|
||||
position: relative;
|
||||
position: fixed;
|
||||
top: 51px;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
#folders {
|
||||
/*position: fixed;
|
||||
top: 73px;*/
|
||||
position: absolute;
|
||||
top: 73px;
|
||||
left: 15px;
|
||||
right: 15px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#folders .ui-resizable-handle {
|
||||
@@ -149,9 +156,19 @@ td[name="date"] {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#left-toolbar {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
#quotabox {
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
#quotabar {
|
||||
margin: 15px 0 0;
|
||||
width: 190px;
|
||||
height: 5px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#quotaraw {
|
||||
@@ -160,7 +177,7 @@ td[name="date"] {
|
||||
|
||||
|
||||
/*
|
||||
*TOP NAVBAR
|
||||
* TOP NAVBAR
|
||||
*/
|
||||
|
||||
.navbar-header{
|
||||
@@ -170,9 +187,26 @@ td[name="date"] {
|
||||
/*
|
||||
* Mailboxes list
|
||||
*/
|
||||
.email {
|
||||
border-top: 1px solid #ddd;
|
||||
padding-top: 10px;
|
||||
|
||||
}
|
||||
|
||||
div.email:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.openable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.leftcol-heading {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
#mboxactions {
|
||||
position: fixed;
|
||||
padding-left: 30px;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
div.clickbox {
|
||||
@@ -216,9 +250,9 @@ li.droppable li{
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
#mailheader label {
|
||||
text-align: left;
|
||||
}
|
||||
/* #mailheader label { */
|
||||
/* text-align: left; */
|
||||
/* } */
|
||||
|
||||
#mailheader .options {
|
||||
margin-bottom: 8px;
|
||||
@@ -240,7 +274,7 @@ li.droppable li{
|
||||
float: left;
|
||||
}
|
||||
|
||||
#list {
|
||||
list {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
left: 180px;
|
||||
@@ -308,16 +342,13 @@ li.droppable li{
|
||||
* Table
|
||||
*/
|
||||
|
||||
td[class*=openable] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*Action buttons
|
||||
*/
|
||||
|
||||
#menubar {
|
||||
/*background-color: #fff;*/
|
||||
position: absolute;
|
||||
top: 0;
|
||||
padding-top: 20px;
|
||||
|
||||
@@ -20,8 +20,6 @@ Webmail.prototype = {
|
||||
mboxes_col_width: 200
|
||||
},
|
||||
|
||||
|
||||
|
||||
initialize: function(options) {
|
||||
this.options = $.extend({}, this.defaults, options);
|
||||
this.rtimer = null;
|
||||
@@ -38,88 +36,116 @@ Webmail.prototype = {
|
||||
});
|
||||
this.record_unseen_messages();
|
||||
|
||||
$("#mboxactions").on("shown.bs.dropdown", function() {
|
||||
var $menu = $("ul", this);
|
||||
var offset = $menu.offset();
|
||||
|
||||
$("#mboxactions").css({
|
||||
bottom: $("#bottom-bar").outerHeight(true) + "px"
|
||||
$("body").append($menu);
|
||||
$menu.show();
|
||||
$menu.css("position", "absolute");
|
||||
$menu.css("top", offset.top + "px");
|
||||
$menu.css("left", offset.left + "px");
|
||||
$(this).data("original_menu", $menu);
|
||||
}).on("hide.bs.dropdown", function() {
|
||||
var $this = $(this);
|
||||
$this.append($this.data("original_menu"));
|
||||
$this.data("original_menu").removeAttr("style");
|
||||
});
|
||||
|
||||
/*$("#mboxactions").css({
|
||||
bottom: $("#bottom-bar").outerHeight(true) + "px"
|
||||
});*/
|
||||
|
||||
/* Responsive -> on resizing folders (leftcol) */
|
||||
$("#folders").resizable({
|
||||
start: function(event, ui) {
|
||||
$('#listing iframe').css('pointer-events','none');
|
||||
},
|
||||
handles: "e",
|
||||
minWidth: parseInt($("#folders").css("width").replace("px", "")),
|
||||
maxWidth: 400,
|
||||
stop: function(event, ui) {
|
||||
$('#listing iframe').css('pointer-events','auto');
|
||||
}
|
||||
});
|
||||
// $("#folders").resizable({
|
||||
// start: function(event, ui) {
|
||||
// $('#listing iframe').css('pointer-events','none');
|
||||
// },
|
||||
// handles: "e",
|
||||
// minWidth: $("#folders").width(),
|
||||
// maxWidth: 400,
|
||||
// stop: function(event, ui) {
|
||||
// $('#listing iframe').css('pointer-events','auto');
|
||||
// }
|
||||
// });
|
||||
|
||||
$("#folders").resizable({
|
||||
resize: function(event, ui) {
|
||||
if ($(window).width() > 1198) {
|
||||
var width = ui.size.width;
|
||||
/*$("#rightcol").css({
|
||||
marginLeft: width + 'px'
|
||||
});*/
|
||||
$("#folders").css({
|
||||
width : width + 'px'
|
||||
});
|
||||
/*$("#rightcol").css({
|
||||
paddingRight: width - 110 + 'px'
|
||||
});
|
||||
if ($(window).width() < 1800) {
|
||||
$("#rightcol").css({
|
||||
paddingRight: width - 110 + 'px'
|
||||
});
|
||||
}*/
|
||||
}
|
||||
}
|
||||
});
|
||||
// $("#folders").resizable({
|
||||
// resize: function(event, ui) {
|
||||
// if ($(window).width() > 1198) {
|
||||
// var width = ui.size.width;
|
||||
// /*$("#rightcol").css({
|
||||
// marginLeft: width + 'px'
|
||||
// });*/
|
||||
// $("#folders").css({
|
||||
// width : width + 'px'
|
||||
// });
|
||||
// /*$("#rightcol").css({
|
||||
// paddingRight: width - 110 + 'px'
|
||||
// });
|
||||
// if ($(window).width() < 1800) {
|
||||
// $("#rightcol").css({
|
||||
// paddingRight: width - 110 + 'px'
|
||||
// });
|
||||
// }*/
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
/* end Responsive */
|
||||
|
||||
this.resize();
|
||||
|
||||
this.setup_infinite_scroll();
|
||||
this.init_droppables();
|
||||
this.register_navcallbacks();
|
||||
this.listen();
|
||||
},
|
||||
|
||||
register_navcallbacks: function() {
|
||||
this.navobject.register_callback("listmailbox", $.proxy(this.listmailbox_callback, this));
|
||||
this.navobject.register_callback("compose", $.proxy(this.compose_callback, this));
|
||||
this.navobject.register_callback("viewmail", $.proxy(this.viewmail_callback, this));
|
||||
this.navobject.register_callback(
|
||||
"listmailbox", $.proxy(this.listmailbox_callback, this));
|
||||
this.navobject.register_callback(
|
||||
"compose", $.proxy(this.compose_callback, this));
|
||||
this.navobject.register_callback(
|
||||
"viewmail", $.proxy(this.viewmail_callback, this));
|
||||
|
||||
this.navobject.register_callback("reply", $.proxy(this.compose_callback, this));
|
||||
this.navobject.register_callback("replyall", $.proxy(this.compose_callback, this));
|
||||
this.navobject.register_callback("forward", $.proxy(this.compose_callback, this));
|
||||
this.navobject.register_callback(
|
||||
"reply", $.proxy(this.compose_callback, this));
|
||||
this.navobject.register_callback(
|
||||
"replyall", $.proxy(this.compose_callback, this));
|
||||
this.navobject.register_callback(
|
||||
"forward", $.proxy(this.compose_callback, this));
|
||||
},
|
||||
|
||||
listen: function() {
|
||||
$(window).resize($.proxy(this.resize, this));
|
||||
|
||||
$(document).on("click", "a[name=compose]", $.proxy(this.compose_loader, this));
|
||||
$(document).on("click", "a[name=totrash]", $.proxy(this.delete_messages, this));
|
||||
$(document).on("click", "a[name*=mark-]", $.proxy(this.send_mark_request, this));
|
||||
$(document).on(
|
||||
"click", "a[name=compose]", $.proxy(this.compose_loader, this));
|
||||
$(document).on(
|
||||
"click", "a[name=totrash]", $.proxy(this.delete_messages, this));
|
||||
$(document).on(
|
||||
"click", "a[name*=mark-]", $.proxy(this.send_mark_request, this));
|
||||
$(document).on("click", "a[name=compress]", $.proxy(this.compress, this));
|
||||
$(document).on("click", "a[name=empty]", $.proxy(this.empty, this));
|
||||
$(document).on("click", "#bottom-bar a", $.proxy(this.getpage_loader, this));
|
||||
|
||||
$(document).on("click", "a[name=loadfolder]", $.proxy(this.listmailbox_loader, this));
|
||||
$(document).on(
|
||||
"click", "a[name=loadfolder]", $.proxy(this.listmailbox_loader, this));
|
||||
$(document).on("click", "a[name=selectfolder]", this.select_parent_mailbox);
|
||||
$(document).on("click", "div[class*=clickbox]", $.proxy(this.mbox_state_callback, this));
|
||||
$(document).on(
|
||||
"click", "div[class*=clickbox]", $.proxy(this.mbox_state_callback, this));
|
||||
$(document).on("click", "a[name=newmbox]", $.proxy(this.new_mailbox, this));
|
||||
$(document).on("click", "a[name=editmbox]", $.proxy(this.edit_mbox, this));
|
||||
$(document).on("click", "a[name=removembox]", $.proxy(this.remove_mbox, this));
|
||||
|
||||
$(document).on("click", "td[class*=openable]", $.proxy(this.viewmail_loader, this));
|
||||
$(document).on("click", "div.openable", $.proxy(this.viewmail_loader, this));
|
||||
|
||||
$(document).on("click", "a[name=reply]", $.proxy(this.reply_loader, this));
|
||||
$(document).on("click", "a[name=replyall]", $.proxy(this.reply_loader, this));
|
||||
$(document).on("click", "a[name=forward]", $.proxy(this.reply_loader, this));
|
||||
$(document).on("click", "a[name=delete]", $.proxy(this.delete_message, this));
|
||||
$(document).on("click", "a[name=activate_links]", $.proxy(function(e) { this.display_mode(e, "1"); }, this));
|
||||
$(document).on(
|
||||
"click", "a[name=activate_links]", $.proxy(function(e) { this.display_mode(e, "1"); }, this));
|
||||
$(document).on("click", "a[name=disable_links]", $.proxy(function(e) { this.display_mode(e, "0"); }, this));
|
||||
|
||||
$(document).on("click", "a[name=sendmail]", $.proxy(this.sendmail, this));
|
||||
@@ -130,6 +156,28 @@ Webmail.prototype = {
|
||||
}, this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup an "infinite scroll" behaviour for the email list.
|
||||
*/
|
||||
setup_infinite_scroll: function() {
|
||||
var that = this;
|
||||
|
||||
$("#listing").infinite_scroll({
|
||||
url: this.options.listing_url,
|
||||
calculate_bottom: function($element) {
|
||||
return $("#emails").height() - $element.height();
|
||||
},
|
||||
get_args: function() {
|
||||
return that.navparams;
|
||||
},
|
||||
process_results: function(data) {
|
||||
var content = $("#emails").html() + data.listing;
|
||||
|
||||
$("#emails").html(content);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
record_unseen_messages: function() {
|
||||
var unseen_counters = {};
|
||||
|
||||
@@ -144,37 +192,23 @@ Webmail.prototype = {
|
||||
* Global resize event callback.
|
||||
*/
|
||||
resize: function() {
|
||||
/*$("#mboxes_container").height(
|
||||
$("#folders").height() - $("#mboxactions").height());*/
|
||||
var $window = $(window);
|
||||
var current_action = this.navobject.getparam("action");
|
||||
|
||||
$("#folders").height(
|
||||
$("#left-toolbar").offset().top - $("#folders").offset().top
|
||||
);
|
||||
|
||||
if ($window.width() > 1198) {
|
||||
$("#folders").css({
|
||||
/*bottom: $("#bottom-bar").outerHeight(true) + 10,*/
|
||||
width: this.options.mboxes_col_width + 'px'
|
||||
});
|
||||
// $("#folders").css({
|
||||
// width: this.options.mboxes_col_width + 'px'
|
||||
// });
|
||||
}
|
||||
if (current_action == "listmailbox") {
|
||||
this.resize_listing("auto");
|
||||
} else if (current_action == "viewmail") {
|
||||
this.resize_listing("hidden");
|
||||
} else if (current_action == "compose") {
|
||||
if (current_action == "compose") {
|
||||
this.resize_compose_body();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Resize the "listing" div.
|
||||
*/
|
||||
resize_listing: function(overflow) {
|
||||
$("#listing").css("overflow", overflow);
|
||||
$("#listing").height(
|
||||
$("#bottom-bar").offset().top -
|
||||
($("#menubar").offset().top + $("#menubar").outerHeight())
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Resize the textarea used to write plain/text messages.
|
||||
*/
|
||||
@@ -183,13 +217,12 @@ Webmail.prototype = {
|
||||
|
||||
if ($container.length) {
|
||||
var top = $container.offset().top;
|
||||
var bottom = $("#bottom-bar").offset().top;
|
||||
|
||||
$("#body_container").innerHeight(bottom - top - 10);
|
||||
$("#body_container").innerHeight($(window).height() - top - 10);
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
/**
|
||||
* Callback of the 'compose' action.
|
||||
*
|
||||
* It is also shared with other similar actions : reply, forward.
|
||||
@@ -199,14 +232,14 @@ Webmail.prototype = {
|
||||
$("#body_container").outerHeight(true));
|
||||
},
|
||||
|
||||
/*
|
||||
/**
|
||||
* Simple helper to retrieve the currently selected mailbox.
|
||||
*/
|
||||
get_current_mailbox: function() {
|
||||
return this.navobject.getparam("mbox", "INBOX");
|
||||
},
|
||||
|
||||
/*
|
||||
/**
|
||||
* Keep track of interesting navigation parameters in order to
|
||||
* restore them later.
|
||||
*/
|
||||
@@ -276,10 +309,10 @@ Webmail.prototype = {
|
||||
$("#menubar").html(response.menu);
|
||||
$("#searchfield").searchbar({navobj: this.navobject});
|
||||
}
|
||||
if (response.navbar) {
|
||||
$("#bottom-bar-right").html(response.navbar);
|
||||
/*if (response.navbar) {
|
||||
$("#pagination").html(response.navbar);
|
||||
$("#pagination-responsive").html(response.navbar);
|
||||
}
|
||||
}*/
|
||||
$("#listing").html(response.listing);
|
||||
},
|
||||
|
||||
@@ -832,22 +865,22 @@ Webmail.prototype = {
|
||||
* Loader of the 'viewmail' action
|
||||
*/
|
||||
viewmail_loader: function(e) {
|
||||
var $tr = $(e.target).parent();
|
||||
var $div = $(e.target).parents(".email");
|
||||
var curmb = this.get_current_mailbox();
|
||||
|
||||
e.preventDefault();
|
||||
if ($tr.hasClass("disabled")) {
|
||||
if ($div.hasClass("disabled")) {
|
||||
return;
|
||||
}
|
||||
$tr.addClass("disabled");
|
||||
if ($tr.hasClass("unseen")) {
|
||||
$div.addClass("disabled");
|
||||
if ($div.hasClass("unseen")) {
|
||||
var mb = this.get_current_mailbox();
|
||||
this.change_unseen_messages(mb, -1);
|
||||
}
|
||||
this.navobject.reset().setparams({
|
||||
action: "viewmail",
|
||||
mbox: curmb,
|
||||
mailid: $tr.attr("id")
|
||||
mailid: $div.attr("id")
|
||||
}).update();
|
||||
},
|
||||
|
||||
@@ -861,14 +894,14 @@ Webmail.prototype = {
|
||||
listmailbox_callback: function(resp) {
|
||||
this.store_nav_params();
|
||||
this.page_update(resp);
|
||||
$("#emails").htmltable();
|
||||
$("#emails").htmltable({
|
||||
row_selector: "div.email"
|
||||
});
|
||||
this.htmltable = $("#emails").data("htmltable");
|
||||
this.init_draggables();
|
||||
this.resize_listing("auto");
|
||||
$("#listing").css("overflow", "auto");
|
||||
},
|
||||
|
||||
|
||||
|
||||
add_field: function(e, name) {
|
||||
e.preventDefault();
|
||||
$("label[for=id_" + name + "]").parent().show();
|
||||
@@ -912,7 +945,7 @@ Webmail.prototype = {
|
||||
*/
|
||||
viewmail_callback: function(resp) {
|
||||
this.page_update(resp);
|
||||
this.resize_listing("hidden");
|
||||
$("#listing").css("overflow", "hidden");
|
||||
$("a[name=back]").click($.proxy(function(e) {
|
||||
e.preventDefault();
|
||||
this.go_back_to_listing();
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
{% load i18n form_tags %}{% load url from future %}
|
||||
<form class="form-horizontal" method="POST" action="{{ posturl }}" id="composemail">{% csrf_token %}
|
||||
<div id="compose-container" class="col-lg-12">
|
||||
<div id="mailheader" class="well">
|
||||
{% render_field form.to %}
|
||||
<div id="mailheader">
|
||||
<div class="form-group">
|
||||
{% render_field form.to %}
|
||||
</div>
|
||||
<div class="options col-lg-offset-4 col-lg-7 col-md-offset-4 col-md-7 col-sm-offset-4 col-sm-7">
|
||||
{% if not form.cc.value %}<a id="add_cc" href="#" class="glyphicon glyphicon-plus">{% trans "Cc" %}</a>{% endif %} <a id="add_cci" href="#" class="glyphicon glyphicon-plus">{% trans "Cci" %}</a>
|
||||
</div>
|
||||
{% if form.cc.value %}{% render_field form.cc %}{% else %}{% render_and_hide_field form.cc %}{% endif %}
|
||||
{% render_and_hide_field form.cci %}
|
||||
{% render_field form.subject %}
|
||||
<div id="attachments" name="{% url 'modoboa.extensions.webmail.views.attachments' %}">
|
||||
<div>
|
||||
<img src="{{ STATIC_URL }}pics/attachment.png" />
|
||||
<div id="attachments" class="row" name="{% url 'modoboa.extensions.webmail.views.attachments' %}">
|
||||
<div class="col-lg-3">
|
||||
<span class="glyphicon glyphicon-paperclip"></span>
|
||||
{% trans "Attachments" %}
|
||||
</div>
|
||||
<div id="list">
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
{% load i18n %}
|
||||
{% load webmail_tags %}
|
||||
|
||||
<div id="emails">
|
||||
{% for email in emails %}
|
||||
<div id="{{ email.imapid }}" class="row email{% if email.style %} {{ email.style }}{% endif %}">
|
||||
<div class="col-xs-1 col-lg-1 text-center">
|
||||
<input type="checkbox" />
|
||||
</div>
|
||||
<div class="col-xs-9 col-lg-9 openable">
|
||||
{{ email.from|parse_imap_header:"from" }}
|
||||
<p class="text-muted">
|
||||
{{ email.subject|parse_imap_header:"subject" }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-xs-2 col-lg-2 text-right">
|
||||
{{ email.date|parse_imap_header:"date" }}
|
||||
<p>{% if email.answered %}<span class="glyphicon glyphicon-share-alt"></span>{% endif %}{% if email.attachments %}<span class="glyphicon glyphicon-paperclip"></span>{% endif %}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
@@ -1,7 +1,4 @@
|
||||
{% load i18n webmail_tags %}
|
||||
<!--<div id="mboxes_container">-->
|
||||
<ul id="folders" class="nav nav-sidebar">
|
||||
<ul class="nav nav-sidebar">
|
||||
{% print_mailboxes mboxes selected withunseen %}
|
||||
</ul>
|
||||
<!--</div>-->
|
||||
<div id="mboxactions">{% mboxes_menu %}</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "fixed.html" %}{% load i18n lib_tags %}{% load url from future %}
|
||||
{% extends "fixed.html" %}{% load i18n lib_tags webmail_tags %}{% load url from future %}
|
||||
{% block pagetitle %}Webmail{% endblock %}
|
||||
{% block html_head %}
|
||||
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/jquery-ui.css" />
|
||||
@@ -8,6 +8,7 @@
|
||||
<script type="text/javascript" src="{{ STATIC_URL }}js/history.js"></script>
|
||||
<script type="text/javascript" src="{{ STATIC_URL }}js/jquery.htmltable.js"></script>
|
||||
<script type="text/javascript" src="{{ STATIC_URL }}js/searchbar.js"></script>
|
||||
<script type="text/javascript" src="{{ STATIC_URL }}js/jquery.infinitescroll.js"></script>
|
||||
<script type="text/javascript" src="{{ STATIC_URL }}webmail/js/webmail.js"></script>
|
||||
<script type="text/javascript">
|
||||
var webmail = undefined;
|
||||
@@ -16,6 +17,7 @@ $(document).ready(function() {
|
||||
webmail = new Webmail({
|
||||
poller_interval: {{ refreshrate }},
|
||||
poller_url: "{% url 'modoboa.extensions.webmail.views.check_unseen_messages' %}",
|
||||
listing_url: "{% url 'modoboa.extensions.webmail.views.index' %}?action=listmailbox",
|
||||
move_url: "{% url 'modoboa.extensions.webmail.views.move' %}",
|
||||
submboxes_url: "{% url 'modoboa.extensions.webmail.views.submailboxes' %}",
|
||||
delattachment_url: "{% url 'modoboa.extensions.webmail.views.delattachment' %}",
|
||||
@@ -33,9 +35,40 @@ $(document).ready(function() {
|
||||
{% block fixed_content %}
|
||||
<div class="row">
|
||||
<div id="leftcol" class="col-lg-2 col-md-3 col-sm-3 col-xs-0 sidebar">
|
||||
<!--<div class="row">-->
|
||||
{{ mboxes }}
|
||||
<!--</div>-->
|
||||
<div class="leftcol-heading text-center">
|
||||
<a name="compose" class="btn btn-success btn-lg" href="compose">
|
||||
<span class="glyphicon glyphicon-edit"></span>
|
||||
{% trans "Compose" %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="folders" class="row">
|
||||
<div class="col-sm-12 col-md-12 col-lg-12">
|
||||
{{ mboxes }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="left-toolbar" class="row">
|
||||
<div class="col-sm-2 col-md-2 col-lg-2">
|
||||
<div id="mboxactions" class="btn-group dropup">
|
||||
<button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown">
|
||||
<span class="glyphicon glyphicon-cog"></span>
|
||||
</button>
|
||||
{% mboxes_menu %}
|
||||
</div>
|
||||
</div>
|
||||
{% if quota != -1 %}
|
||||
<div id="quotabox" class="col-sm-10 col-md-10 col-lg-10">
|
||||
<!--<label class="pull-left navbar-text">{% trans "Mailbox quota" %}</label>-->
|
||||
<div id="quotabar" class="progress">
|
||||
<div class="progress-bar {% progress_color quota %}" role="progressbar" style="width: {{ quota }}%" aria-valuenow="{{ quota }}" aria-valuemin="0" aria-valuemax="100">{{ quota }}%</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div id="rightcol" class="col-lg-offset-2 col-lg-10 col-md-offset-3 col-md-9 col-sm-offset-3 col-sm-9 col-xs-12">
|
||||
<div class="row">
|
||||
@@ -47,13 +80,4 @@ $(document).ready(function() {
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block footerleft %}
|
||||
{% if quota != -1 %}
|
||||
<div id="quotabox">
|
||||
<label class="pull-left navbar-text">{% trans "Mailbox quota" %}</label>
|
||||
<div id="quotabar" class="pull-left progress">
|
||||
<div class="{% progress_color quota %}" role="progressbar" style="width: {{ quota }}%" aria-valuenow="{{ quota }}" aria-valuemin="0" aria-valuemax="100">{{ quota }}%</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
{% extends "common/buttons_list.html" %}{% load i18n %}
|
||||
{% block extracontent %}{% include "common/email_searchbar.html" with extraopts=extraopts %}
|
||||
|
||||
{% block before_menubar %}
|
||||
<div class="col-xs-5 col-sm-5 col-md-5 col-lg-5">
|
||||
{% include "common/email_searchbar.html" with extraopts=extraopts %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block menubar_classes %}col-lg-2{% endblock %}
|
||||
|
||||
{% block after_menubar %}
|
||||
<div id="pagination" class="col-lg-2"></div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -4,8 +4,11 @@ from django.template.loader import render_to_string
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
|
||||
from modoboa.extensions import webmail
|
||||
from modoboa.extensions.webmail.lib import separate_mailbox
|
||||
from modoboa.extensions.webmail.lib import (
|
||||
imapheader, separate_mailbox
|
||||
)
|
||||
from modoboa.lib import parameters
|
||||
|
||||
register = template.Library()
|
||||
@@ -15,10 +18,10 @@ register = template.Library()
|
||||
def viewmail_menu(selection, folder, user, mail_id=None):
|
||||
entries = [
|
||||
{"name": "back",
|
||||
"url": "javascript:history.go(-2);",
|
||||
"img": "glyphicon glyphicon-arrow-left",
|
||||
"class": "btn-primary sm-margin-left-back",
|
||||
"label": _("Back")},
|
||||
"url": "javascript:history.go(-1);",
|
||||
"img": "glyphicon glyphicon-arrow-left",
|
||||
"class": "btn-primary sm-margin-left-back",
|
||||
"label": _("Back")},
|
||||
{"name": "reply",
|
||||
"url": "action=reply&mbox=%s&mailid=%s" % (folder, mail_id),
|
||||
"img": "glyphicon glyphicon-share",
|
||||
@@ -78,33 +81,23 @@ def compose_menu(selection, backurl, user):
|
||||
@register.simple_tag
|
||||
def listmailbox_menu(selection, folder, user):
|
||||
entries = [
|
||||
{"name": "compose",
|
||||
"url": "compose",
|
||||
"img": "glyphicon glyphicon-edit",
|
||||
"label": _("New message"),
|
||||
"class": "btn btn-default"},
|
||||
{"name": "totrash",
|
||||
"label": "",
|
||||
"class": "btn btn-default",
|
||||
"img": "glyphicon glyphicon-trash",
|
||||
"url": reverse("modoboa.extensions.webmail.views.delete"),
|
||||
},
|
||||
{"name": "mark",
|
||||
"label": _("Mark messages"),
|
||||
"class": "btn btn-default",
|
||||
"menu": [
|
||||
{"name": "mark-read",
|
||||
"label": _("As read"),
|
||||
"url": reverse(webmail.views.mark, args=[folder]) + "?status=read"},
|
||||
{"name": "mark-unread",
|
||||
"label": _("As unread"),
|
||||
"url": reverse(webmail.views.mark, args=[folder]) + "?status=unread"}
|
||||
]
|
||||
},
|
||||
{"name": "actions",
|
||||
"label": _("Actions"),
|
||||
"class": "btn btn-default",
|
||||
"menu": [
|
||||
{"name": "mark-read",
|
||||
"label": _("Mark as read"),
|
||||
"url": reverse(webmail.views.mark, args=[folder]) + "?status=read"},
|
||||
{"name": "mark-unread",
|
||||
"label": _("Mark as unread"),
|
||||
"url": reverse(webmail.views.mark, args=[folder]) + "?status=unread"},
|
||||
{"divider": True},
|
||||
{"name": "compress",
|
||||
"label": _("Compress folder"),
|
||||
"url": "compact/%s/" % folder}
|
||||
@@ -179,7 +172,7 @@ def mboxes_menu():
|
||||
{"name": "newmbox",
|
||||
"url": reverse(webmail.views.newfolder),
|
||||
"img": "glyphicon glyphicon-plus",
|
||||
"title": _("Create a new mailbox"),
|
||||
"label": _("Create a new mailbox"),
|
||||
"modal": True,
|
||||
"modalcb": "webmail.mboxform_cb",
|
||||
"closecb": "webmail.mboxform_close",
|
||||
@@ -187,15 +180,25 @@ def mboxes_menu():
|
||||
{"name": "editmbox",
|
||||
"url": reverse(webmail.views.editfolder),
|
||||
"img": "glyphicon glyphicon-edit",
|
||||
"title": _("Edit the selected mailbox"),
|
||||
"label": _("Edit the selected mailbox"),
|
||||
"class": "btn-default btn-xs"},
|
||||
{"name": "removembox",
|
||||
"url": reverse(webmail.views.delfolder),
|
||||
"img": "glyphicon glyphicon-remove",
|
||||
"title": _("Remove the selected mailbox"),
|
||||
"label": _("Remove the selected mailbox"),
|
||||
"class": "btn-default btn-xs"}
|
||||
]
|
||||
|
||||
return render_to_string('common/buttons_list.html', dict(
|
||||
entries=entries, css="nav"
|
||||
return render_to_string('common/menu.html', dict(
|
||||
entries=entries, css="dropdown-menu"
|
||||
))
|
||||
|
||||
|
||||
@register.filter
|
||||
def parse_imap_header(value, header):
|
||||
"""Simple template tag to display a IMAP header."""
|
||||
try:
|
||||
value = getattr(imapheader, "parse_%s" % header)(value)
|
||||
except AttributeError:
|
||||
pass
|
||||
return value
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
# coding: utf-8
|
||||
"""
|
||||
Webmail extension views.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from rfc6266 import build_header
|
||||
|
||||
from django.conf import settings
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import render
|
||||
@@ -10,6 +16,7 @@ from django.core.urlresolvers import reverse
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views.decorators.gzip import gzip_page
|
||||
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.exceptions import ModoboaException, BadRequest
|
||||
from modoboa.lib.webutils import (
|
||||
@@ -27,7 +34,7 @@ from .lib import (
|
||||
ImapEmail, WebmailNavigationParameters, ReplyModifier, ForwardModifier,
|
||||
get_imapconnector, IMAPconnector, separate_mailbox
|
||||
)
|
||||
from templatetags import webmail_tags
|
||||
from .templatetags import webmail_tags
|
||||
|
||||
|
||||
@login_required
|
||||
@@ -338,7 +345,15 @@ def listmailbox(request, defmailbox="INBOX", update_session=True):
|
||||
elems_per_page=int(parameters.get_user(request.user, "MESSAGES_PER_PAGE")),
|
||||
**request.session["webmail_navparams"]
|
||||
)
|
||||
return lst.render(request, navparams.get('page'))
|
||||
page = lst.paginator.getpage(navparams.get('page'))
|
||||
if page is not None:
|
||||
content = _render_to_string(request, "webmail/email_list.html", {
|
||||
"emails": lst.mbc.fetch(page.id_start, page.id_stop, mbox, nbelems=40)
|
||||
})
|
||||
else:
|
||||
content = ""
|
||||
return {"listing": content, "length": len(content)}
|
||||
#return lst.render(request, navparams.get('page'))
|
||||
|
||||
|
||||
def render_compose(request, form, posturl, email=None, insert_signature=False):
|
||||
|
||||
@@ -54,11 +54,11 @@
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 51px;
|
||||
bottom: 51px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
display: block;
|
||||
padding: 20px;
|
||||
padding: 20px 15px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
|
||||
background-color: #f5f5f5;
|
||||
|
||||
@@ -26,35 +26,37 @@
|
||||
},
|
||||
|
||||
listen: function() {
|
||||
$(document)
|
||||
.off("change", "tbody input[type=checkbox]")
|
||||
.on("change", "tbody input[type=checkbox]",
|
||||
this.$element
|
||||
.off("change", "input[type=checkbox]")
|
||||
.on("change", "input[type=checkbox]",
|
||||
$.proxy(this.toggle_select, this));
|
||||
$(document).on("keydown", "body", $.proxy(this.keydown, this));
|
||||
$(document).on("keyup", "body", $.proxy(this.keyup, this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Effective change of a table row's state.
|
||||
* Effective change of a row's state.
|
||||
*/
|
||||
_toggle_select: function($tr) {
|
||||
if ($tr.hasClass(this.options.tr_selected_class)) {
|
||||
$tr.children('td[name=selection]').children('input').prop('checked', false);
|
||||
$tr.removeClass(this.options.tr_selected_class);
|
||||
if (this.options.tr_unselected_event != undefined) {
|
||||
this.options.tr_unselected_event($tr);
|
||||
_toggle_select: function($row) {
|
||||
var $input = $row.find(this.options.input_selector);
|
||||
|
||||
if ($row.hasClass(this.options.row_selected_class)) {
|
||||
$input.prop('checked', false);
|
||||
$row.removeClass(this.options.row_selected_class);
|
||||
if (this.options.row_unselected_event !== undefined) {
|
||||
this.options.row_unselected_event($row);
|
||||
}
|
||||
} else {
|
||||
$tr.children('td[name=selection]').children('input').prop('checked', true);
|
||||
$tr.addClass(this.options.tr_selected_class);
|
||||
if (this.options.tr_selected_event != undefined) {
|
||||
this.options.tr_selected_event($tr);
|
||||
$input.prop('checked', true);
|
||||
$row.addClass(this.options.row_selected_class);
|
||||
if (this.options.row_selected_event !== undefined) {
|
||||
this.options.row_selected_event($row);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Change the selection state of a given table row.
|
||||
* Change the selection state of a given row.
|
||||
*
|
||||
* Holding the shift key between two selections let users
|
||||
* select all rows between those two locations.
|
||||
@@ -63,49 +65,50 @@
|
||||
* an additive selection.
|
||||
*/
|
||||
toggle_select: function(e) {
|
||||
var $tr = $(e.target).parents('tr');
|
||||
var $row = $(e.target).parents(this.options.row_selector);
|
||||
|
||||
if (this.shift_pressed && this.last_selection) {
|
||||
var start = this.last_selection;
|
||||
|
||||
this.clear_selection();
|
||||
this.last_selection = start;
|
||||
var itfunc = ($tr.index() >= this.last_selection.index())
|
||||
? "next" : "prev";
|
||||
var $curtr = null;
|
||||
|
||||
var itfunc = ($row.index() >= this.last_selection.index()) ?
|
||||
"next" : "prev";
|
||||
var $cur_row = null;
|
||||
|
||||
if (this.prev_dir && itfunc != this.prev_dir) {
|
||||
$curtr = this.last_selection;
|
||||
$cur_row = this.last_selection;
|
||||
} else {
|
||||
$curtr = this.last_selection;
|
||||
$cur_row = this.last_selection;
|
||||
}
|
||||
for (; $curtr.length; $curtr = $curtr[itfunc]("tr")) {
|
||||
this._toggle_select($curtr);
|
||||
if ($tr.attr("id") == $curtr.attr("id")) {
|
||||
for (; $cur_row.length; $cur_row = $cur_row[itfunc](this.options.row_selector)) {
|
||||
this._toggle_select($cur_row);
|
||||
if ($row.attr("id") == $cur_row.attr("id")) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.prev_dir = itfunc;
|
||||
return;
|
||||
} else if (!this.ctrl_pressed && (!this.last_selection || !this.last_selection.is($tr))) {
|
||||
} else if (!this.ctrl_pressed && (!this.last_selection || !this.last_selection.is($row))) {
|
||||
this.clear_selection();
|
||||
}
|
||||
this.last_selection = $tr;
|
||||
this._toggle_select($tr);
|
||||
this.last_selection = $row;
|
||||
this._toggle_select($row);
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if $row is selected or not.
|
||||
*/
|
||||
is_selected: function($row) {
|
||||
return $row.hasClass(this.options.tr_selected_class);
|
||||
return $row.hasClass(this.options.row_selected_class);
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the current selection. (jQuery object)
|
||||
*/
|
||||
current_selection: function() {
|
||||
return $("tr[class*=" + this.options.tr_selected_class + "]");
|
||||
return this.$element.find("." + this.options.row_selected_class);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -113,23 +116,23 @@
|
||||
*/
|
||||
clear_selection: function() {
|
||||
this.last_selection = null;
|
||||
this.current_selection().removeClass(this.options.tr_selected_class);
|
||||
$("tbody input[type=checkbox]").prop('checked', false);
|
||||
if (this.options.tr_unselected_event != undefined) {
|
||||
this.options.tr_unselected_event();
|
||||
this.current_selection().removeClass(this.options.row_selected_class);
|
||||
this.$element.find(this.options.input_selector).prop('checked', false);
|
||||
if (this.options.row_unselected_event !== undefined) {
|
||||
this.options.row_unselected_event();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Manually select a table row.
|
||||
* Manually select a row.
|
||||
*/
|
||||
select_row: function($row) {
|
||||
if (!this.last_selection) {
|
||||
this.last_selection = $row;
|
||||
}
|
||||
$row.addClass(this.options.tr_selected_class);
|
||||
if (this.options.tr_selected_event != undefined) {
|
||||
this.options.tr_selected_event($row);
|
||||
$row.addClass(this.options.row_selected_class);
|
||||
if (this.options.row_selected_event !== undefined) {
|
||||
this.options.row_selected_event($row);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -177,6 +180,8 @@
|
||||
};
|
||||
|
||||
$.fn.htmltable.defaults = {
|
||||
tr_selected_class: 'tr-selected'
|
||||
row_selector: 'tr',
|
||||
input_selector: "input[type=checkbox]",
|
||||
row_selected_class: 'tr-selected'
|
||||
};
|
||||
})(jQuery);
|
||||
})(jQuery);
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Infinite scroll plugin for jQuery.
|
||||
*
|
||||
*/
|
||||
!function($) {
|
||||
'use strict';
|
||||
|
||||
var InfiniteScroll = function(element, options) {
|
||||
this.$element = $(element);
|
||||
this.$data = $(element).data();
|
||||
this.$options = options;
|
||||
this.executing = false;
|
||||
this.end_of_results = false;
|
||||
this.current_page = 1;
|
||||
var that = this;
|
||||
|
||||
this.$element.scroll(function() {
|
||||
if (that.$element.scrollTop() >= that.$options.calculate_bottom(that.$element)) {
|
||||
that.load_more();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
InfiniteScroll.prototype = {
|
||||
constructor: InfiniteScroll,
|
||||
|
||||
load_more: function() {
|
||||
var $this = this;
|
||||
|
||||
if ($this.executing || $this.end_of_results) {
|
||||
return;
|
||||
}
|
||||
/*$this.$element.find('.spinner').removeClass('hide');*/
|
||||
$this.executing = true;
|
||||
$this.current_page += 1;
|
||||
var args = $this.$options.get_args();
|
||||
args.page = $this.current_page;
|
||||
$.ajax({
|
||||
contentType: 'application/json; charset=UTF-8',
|
||||
data: args,
|
||||
url: $this.$options.url,
|
||||
type: 'GET',
|
||||
success: function(data) {
|
||||
$this.$options.process_results(data);
|
||||
if (data.length === 0) {
|
||||
$this.end_of_results = true;
|
||||
/*$this.$element.find('#end-of-results').removeClass('hide');*/
|
||||
}
|
||||
//$this.$element.find('.spinner').addClass('hide');
|
||||
$this.executing = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$.fn.infinite_scroll = function (option) {
|
||||
return this.each(function () {
|
||||
var $this = $(this),
|
||||
data = $this.data('infinite-scroll'),
|
||||
options = $.extend({}, $.fn.infinite_scroll.defaults, typeof option == 'object' && option);
|
||||
|
||||
if (!data) {
|
||||
$this.data('infinite-scroll', (data = new InfiniteScroll(this, options)));
|
||||
}
|
||||
if (typeof options == 'string') {
|
||||
data[options]();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$.fn.infinite_scroll.defaults = {
|
||||
calculate_bottom: null,
|
||||
get_data: null,
|
||||
process_results: null,
|
||||
url: ''
|
||||
};
|
||||
|
||||
$.fn.infinite_scroll.Constructor = InfiniteScroll;
|
||||
}(window.jQuery);
|
||||
@@ -1,18 +1,28 @@
|
||||
{% load lib_tags %}
|
||||
|
||||
<!-- Brand and toggle get grouped for better mobile display -->
|
||||
<div class="btn-toolbar {{ extraclasses }}">
|
||||
{% for entry in entries %}
|
||||
<ul class="nav navbar-nav action-buttons btn-group">{% if entry.menu %}
|
||||
<a class="btn btn-default dropdown-toggle {{ entry.label }}" data-toggle="dropdown">{% if entry.img %}<i class="{{ entry.img}}"></i>{% endif %}{{ entry.label }} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">{% for sentry in entry.menu %}
|
||||
<li>{% render_link sentry %}</li>
|
||||
{% endfor %}</ul>
|
||||
{% else %}{% render_link entry "btn" %}{% endif %}
|
||||
</ul>
|
||||
{% endfor %}
|
||||
<ul class="nav navbar-nav action-buttons navbar-right">
|
||||
{% block extracontent %}{% endblock %}
|
||||
</ul>
|
||||
<div class="row">
|
||||
{% block before_menubar %}{% endblock %}
|
||||
<div class="btn-toolbar {{ extraclasses }}{% block menubar_classes %}col-xs-12 col-sm-12 col-md-12 col-lg-12{% endblock %}">
|
||||
{% for entry in entries %}
|
||||
<ul class="nav navbar-nav action-buttons btn-group">
|
||||
{% if entry.menu %}
|
||||
<a class="btn btn-default dropdown-toggle {{ entry.label }}" data-toggle="dropdown">{% if entry.img %}<i class="{{ entry.img}}"></i>{% endif %}{{ entry.label }} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
{% for sentry in entry.menu %}
|
||||
{% if sentry.divider %}
|
||||
<li class="divider"></li>
|
||||
{% else %}
|
||||
<li>{% render_link sentry %}</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
{% render_link entry "btn" %}
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% block after_menubar %}{% endblock %}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{% load i18n %}
|
||||
<div id="searchbox" class="navbar-form navbar-left">
|
||||
<div class="input-group">
|
||||
<div id="searchbox" class="">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" name="searchfield" id="searchfield" placeholder="{% trans "Search..." %}" />
|
||||
<ul class="dropdown-menu pull-right" id="searchparams">
|
||||
<li><label class="radio"><input type="radio" id="crit_from_addr" name="scriteria" value="from_addr" checked>{% trans "From address" %}</label></li>
|
||||
<li><label class="radio"><input type="radio" id="crit_subject" name="scriteria" value="subject">{% trans "Subject" %}</label></li>
|
||||
{% for opt in extraopts %}
|
||||
<li><label class="radio"><input type="radio" id="crit_{{ opt.name }}" name="scriteria" value="{{ opt.name }}">{{ opt.label }}</label></li>
|
||||
{% endfor %}
|
||||
<li><label class="radio"><input type="radio" id="crit_both" name="scriteria" value="both">{% trans "Both" %}</label></li>
|
||||
</ul>
|
||||
<a href="#" class="input-group-addon" data-toggle="dropdown"><i class="glyphicon glyphicon-search" id="searchopts"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="dropdown-menu pull-right" id="searchparams">
|
||||
<li><label class="radio"><input type="radio" id="crit_from_addr" name="scriteria" value="from_addr" checked>{% trans "From address" %}</label></li>
|
||||
<li><label class="radio"><input type="radio" id="crit_subject" name="scriteria" value="subject">{% trans "Subject" %}</label></li>
|
||||
{% for opt in extraopts %}
|
||||
<li><label class="radio"><input type="radio" id="crit_{{ opt.name }}" name="scriteria" value="{{ opt.name }}">{{ opt.label }}</label></li>
|
||||
{% endfor %}
|
||||
<li><label class="radio"><input type="radio" id="crit_both" name="scriteria" value="both">{% trans "Both" %}</label></li>
|
||||
</ul>
|
||||
<a href="#" class="input-group-addon" data-toggle="dropdown"><i class="glyphicon glyphicon-search" id="searchopts"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1 +1 @@
|
||||
<ul class="{{ css }}">{% include "common/menulist.html" %}</ul>
|
||||
<ul class="{{ css }}" role="menu">{% include "common/menulist.html" %}</ul>
|
||||
|
||||
@@ -97,21 +97,20 @@
|
||||
</div>
|
||||
</div>
|
||||
{% block content %}{% endblock %}
|
||||
<!--<div class="display-up-1198 nav-bottom">-->
|
||||
<div id="bottom-bar" class="navbar navbar-inverse navbar-fixed-bottom">
|
||||
<div class="container-fluid">
|
||||
<div id="bottom-bar-left" class="col-lg-4 col-md-4 col-sm-4 col-xs-0">
|
||||
{% block footerleft %}{% endblock %}
|
||||
</div>
|
||||
<div id="bottom-bar-middle" class="col-lg-4 col-md-4 col-sm-4 col-xs-0 col-xs-offset-0">
|
||||
<p class="navbar-text"><i>{% trans "Powered by Modoboa" %}</i></p>
|
||||
{% block footermiddle %}{% endblock %}
|
||||
</div>
|
||||
<div id="bottom-bar-right" class="col-lg-4 col-md-4 col-sm-4 col-xs-12">
|
||||
{% block footerright %}{% endblock %}
|
||||
</div>
|
||||
<!--<div id="bottom-bar" class="navbar navbar-inverse navbar-fixed-bottom">
|
||||
<div class="container-fluid">
|
||||
<div id="bottom-bar-left" class="col-lg-4 col-md-4 col-sm-4 col-xs-0">
|
||||
{% block footerleft %}{% endblock %}
|
||||
</div>
|
||||
<div id="bottom-bar-middle" class="col-lg-4 col-md-4 col-sm-4 col-xs-0 col-xs-offset-0">
|
||||
<p class="navbar-text"><i>{% trans "Powered by Modoboa" %}</i></p>
|
||||
{% block footermiddle %}{% endblock %}
|
||||
</div>
|
||||
<div id="bottom-bar-right" class="col-lg-4 col-md-4 col-sm-4 col-xs-12">
|
||||
{% block footerright %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
<!--</div>-->
|
||||
</div>-->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user