Commit 66c1e9ed authored by Carsten Schoenert's avatar Carsten Schoenert

New upstream version 3.5.1~rc1+dfsg1

parent 6320c46a
......@@ -18,7 +18,7 @@ JSDEPLOY = $(DESTDIR)/client
JSCOMPILER ?= $(JAVA) -jar tools/lib/compiler.jar
JSOPTIONS = --externs client/externs.js \
--compilation_level SIMPLE --warning_level VERBOSE --jscomp_off=es5Strict \
--compilation_level SIMPLE --warning_level VERBOSE --jscomp_off=es5Strict --strict_mode_input=false \
--jscomp_off=globalThis --jscomp_off=misplacedTypeAnnotation --jscomp_off=nonStandardJsDocs \
--jscomp_off=missingProperties --jscomp_off=invalidCasts --jscomp_off=checkTypes \
--jscomp_warning=visibility --jscomp_warning=unknownDefines --jscomp_warning=undefinedVars \
......@@ -152,13 +152,15 @@ $(JSDEPLOY)/resize.js: client/resize.js
cat client/resize.js > $(JSDEPLOY)/resize-debug.js
$(JSCOMPILER) --js $(@:.js=-debug.js) --js_output_file $@
$(JSDEPLOY)/third-party/ux-thirdparty.js: client/third-party/tinymce/TinyMceTextArea.js
$(JSDEPLOY)/third-party/ux-thirdparty.js: $(JSDEPLOY)/third-party/TinyMceTextArea-debug.js client/third-party/tokenizr/tokenizr.min.js
mkdir -p $(JSDEPLOY)/third-party
cat client/third-party/tinymce/TinyMceTextArea.js > $(JSDEPLOY)/third-party/ux-thirdparty-debug.js
$(JSCOMPILER) --js $(@:.js=-debug.js) --js_output_file $@ \
--source_map_location_mapping=$(JSDEPLOY)/third-party/\| \
--output_wrapper="%output%//# sourceMappingURL=$(shell basename $@.map)" \
--create_source_map $@.map $(JSOPTIONS)
# concatenate using cat
cat $^ > $@
$(JSDEPLOY)/third-party/TinyMceTextArea-debug.js: client/third-party/tinymce/TinyMceTextArea.js
mkdir -p $(JSDEPLOY)/third-party
cp $< $@
$(JSCOMPILER) --js $< --js_output_file $@
config:
cp $(DESTDIR)/config.php.dist $(DESTDIR)/config.php
......
......@@ -279,15 +279,21 @@
</zCompile>
<echo message="Compiling: ${thirdparty-file}" />
<zCompile inputFolder="${target-folder}/${thirdparty-folder}" inputFile="${thirdparty-debugfile}" outputFolder="${target-folder}/${thirdparty-folder}" outputFile="${thirdparty-file}" warningLevel="default">
<zCompile inputFolder="${target-folder}/${thirdparty-folder}" inputFile="${thirdparty-debugfile}" outputFolder="${target-folder}/${thirdparty-folder}" outputFile="${thirdparty-file}" warningLevel="QUIET">
<externs>
var Ext = {};
var console = {};
var exports = {};
var module = {};
var define = {};
var global = {};
var require = {};
var Proxy = {};
</externs>
</zCompile>
<echo message="Compiling: ${kopano-file}" />
<zCompile inputFolder="${target-folder}/${kopano-folder}" inputFile="${kopano-debugfile}" outputFolder="${target-folder}/${kopano-folder}" outputFile="${kopano-file}">
<zCompile inputFolder="${target-folder}/${kopano-folder}" inputFile="${kopano-debugfile}" outputFolder="${target-folder}/${kopano-folder}" outputFile="${kopano-file}" checkRegExp="off">
<externs>
var FormData = {};
var Ext = {};
......@@ -310,6 +316,7 @@
var pgettext = function(msgctxt, msgid) {};
var resizeLoginBox = function() {};
var tinymce = {};
var Tokenizr = {};
</externs>
</zCompile>
</target>
......
......@@ -11,6 +11,12 @@ var user = {};
var version = {};
var urlActionData = {};
var console = {};
var Tokenizr = {};
var module;
var define;
var global;
var require;
var proxy;
var _ = function(key, domain) {};
var dgettext = function(domain, msgid) {};
var dngettext = function(domain, msgid, msgid_plural, count) {};
......
......@@ -2,7 +2,7 @@
var checkPerc = /^(100?|\d?\d)?%?$/;
var checkNaturalInteger = /^[1-9]\d*$/i;
// Override regex to allow TLD with max length of 10 characters
var email = /^(\w+)([\-+.\'][\w]+)*@(\w[\-\w]*\.){1,5}([A-Za-z]){2,10}$/;
var email = /^(\w+)([\-+.\']+[\w]+)*@(\w[\-\w]*\.){1,5}([A-Za-z]){2,10}$/;
/**
* @class Ext.form.VTypes
......
This diff is collapsed.
......@@ -2,5 +2,7 @@
"id": "breeze",
"display-name": "Breeze",
"stylesheet": "breeze-icons.css",
"primary-color": "#6d6d70",
"secondary-color": "#19b5f1",
"about": "The Breeze iconset was created by <a target=\"_blank\" href=\"https://www.patreon.com/user?u=10071325\">Andreas Kainz</a>"
}
\ No newline at end of file
......@@ -81,7 +81,6 @@
div {
padding: $padding-medium;
background: $light-blue;
border: 1px solid $grey5;
}
}
......@@ -233,7 +232,8 @@
.k-datepanel,
.k-workpanel,
.k-companypanel {
.k-companypanel,
.k-updatelistpanel {
padding: $padding-medium;
}
......@@ -242,9 +242,11 @@
padding-left: $padding-extra-extra-large;
}
}
.k-updatelistpanel .x-form-item {
padding: $padding-medium;
.k-updatelistpanel {
.x-btn {
padding-top: $padding-medium;
}
}
}
......@@ -391,4 +393,4 @@
border-left: none;
}
}
}
\ No newline at end of file
}
......@@ -110,8 +110,7 @@ $preview-header-collapse-icon-height : 5;
}
.preview-header-recipients .preview-recipient-title,
.preview-header-attachments .preview-attachment-title
{
.preview-header-attachments .preview-attachment-title {
float:left;
}
......@@ -138,14 +137,12 @@ $preview-header-collapse-icon-height : 5;
/* Overflow for recipients / attachments */
.preview-header-recipients .preview-recipient-data,
.preview-header-attachments .preview-attachment-data
{
.preview-header-attachments .preview-attachment-data {
overflow-y: auto;
}
/* Attachments: */
.preview-header-attachmentbox
{
.preview-header-attachmentbox {
line-height:18px;
padding-top:2px;
}
......@@ -158,23 +155,14 @@ $preview-header-collapse-icon-height : 5;
/* Top header infobox */
.preview-header-extrainfobox {
// FIXME: SASS Color
background-color: #D2E0F0;
color: #000000;
padding: 2px 0px;
background-color: $light-blue;
margin-bottom: $padding-small;
padding: $padding-medium $padding-large $padding-medium $padding-large;
}
/* Corrupt message case */
.preview-header-extrainfobox-item-over {
// FIXME: SASS Color
background-color: #FFBD69;
cursor : pointer;
}
.preview-header-extrainfobox-item {
padding: 0px 8px;
// FIXME: SASS Color
color: #15428B;
cursor: pointer;
}
/* Preview header email address */
......@@ -189,15 +177,14 @@ span.zarafa-emailaddress-link:hover {
}
.preview-title-hr {
margin : 2px 0;
margin: 2px 0;
}
.preview-recipient-data {
word-wrap: break-word;
}
.preview-header-attachments .zarafa-attachment-link
{
.preview-header-attachments .zarafa-attachment-link {
// FIXME: sassify
border-radius: 4px;
border: 1px solid #d3d2d2;
......@@ -209,8 +196,7 @@ span.zarafa-emailaddress-link:hover {
cursor: pointer;
}
.preview-header-attachments .zarafa-attachment-link-over
{
.preview-header-attachments .zarafa-attachment-link-over {
// FIXME: sassify
background-color: #ddd;
}
......
Tokenizr in Kopano WebApp
=========================
The tokenizr library can be found at https://github.com/rse/tokenizr
However we made a small adjustment to support unicode matching rules
Until this changed has been merged into the original master branch,
the following branch should be used:
https://github.com/rotous/tokenizr/tree/unicode-support
This diff is collapsed.
......@@ -797,6 +797,9 @@ Ext.apply(Zarafa, {
Zarafa.initializeGlobals();
Zarafa.initializeEnvironment();
// Recolor the icons if necessary
this.recolorIcons();
// Start loading all plugins
Zarafa.fireReady();
......@@ -945,6 +948,67 @@ Ext.apply(Zarafa, {
container.switchContext(plugin);
},
/**
* When an iconset has SVG icons and it has defined the primary-color and/or
* secondary-color properties and the active theme has defined the
* icons-primary-color and/or icons-secondary-color property this function
* will rewrite the css rules of the iconset to update the colors of the icons.
*/
recolorIcons : function()
{
// Get the properties defined by the active iconset
var serverConfig = container.getServerConfig();
var iconsets = serverConfig.getIconsets();
var activeIconsetName = serverConfig.getActiveIconset();
var activeIconset = iconsets[activeIconsetName];
// If the iconset did not define a primary or secondary color, we cannot
// redefine the colors, so return without doing anything
if ( !activeIconset['primary-color'] && !activeIconset['secondary-color'] ) {
return;
}
// Get the primary and secondary icon color defined in the active theme
// (themes can override the colors of iconsets that allow recoloring)
var themeIconsPrimaryColor = serverConfig.getPrimaryIconColor();
var themeIconsSecondaryColor = serverConfig.getSecondaryIconColor();
// If the active theme did not define a new primary or secondary color
// for the icons there is nothing to do, so return
if ( !themeIconsPrimaryColor && !themeIconsSecondaryColor ) {
return;
}
// Get the stylesheet element that contains the icons as background images
// and check all rules in it for SVG icons we can recolor
var sheet = document.getElementById('kopano-iconset-stylesheet');
for ( var i=0; i<sheet.sheet.cssRules.length; i++ ) {
var rule = sheet.sheet.cssRules[i];
// Check the rule to see if it contains an SVG background image
// (we can only recolor SVG icons)
var matches = (/url\("data:image\/svg\+xml;base64,(.+?)"\)/).exec(rule.cssText);
if ( matches !== null ) {
// base64 decode the SVG image
var svg = atob(matches[1]);
// Simply replace the color codes
var svgRecolored = svg.replace(activeIconset['primary-color'], themeIconsPrimaryColor).replace(activeIconset['secondary-color'], themeIconsSecondaryColor);
// If we changed anything, replace the CSS rule to use the base64 encoded SVG
// with the new color(s)
if ( svg !== svgRecolored ) {
var cssText = rule.cssText.replace(
/url\("data:image\/svg\+xml;base64,(.+)"\)/,
'url("data:image/svg+xml;base64,' + btoa(svgRecolored) + '")'
);
sheet.sheet.deleteRule(i);
sheet.sheet.insertRule(cssText, i);
}
}
}
},
/**
* Check if user is out of office
* If so, ask them if they want to switch OOF off
......
This diff is collapsed.
......@@ -93,7 +93,7 @@ Zarafa.advancesearch.dialogs.SearchToolBoxPanel = Ext.extend(Ext.Panel, {
autoLoad: true,
data: Zarafa.advancesearch.data.DateRangeFields
};
Ext.applyIf(config, {
xtype : 'zarafa.searchtoolboxpanel',
title: _('Search tools'),
......@@ -121,9 +121,13 @@ Zarafa.advancesearch.dialogs.SearchToolBoxPanel = Ext.extend(Ext.Panel, {
this.createCategoryFilterFieldset(dateRangeStore),
this.createFavoritesContainer(config)
]
}]
}],
listeners: {
afterrender: this.onAfterRender,
scope: this
}
});
this.addEvents(
/**
* @event afterupdaterestriction fired after the {@link #searchCriteria} gets updated by the
......@@ -136,6 +140,47 @@ Zarafa.advancesearch.dialogs.SearchToolBoxPanel = Ext.extend(Ext.Panel, {
Zarafa.advancesearch.dialogs.SearchToolBoxPanel.superclass.constructor.call(this, config);
},
/**
* Event handler for the render event of the SearchToolBoxPanel. Will add an event listener to the
* input element of the {@link Zarafa.common.searchfield.ui.SearchTextField}
*/
onAfterRender : function()
{
var searchTextField = this.ownerCt.searchToolbar.contextMainPanelToolbar.searchFieldContainer.searchTextField;
// Because the input event is not relayed by the Ext.form.TextField (like e.g. keyup) we must listen
// to the input element itself. We can only add the listener once it has been rendered, hence the
// double mon()
this.mon(searchTextField, 'render', function() {
this.mon(searchTextField.getEl(), 'input', this.onSearchTextFieldChange, this);
// Call the listener also upon rendering of the searchTextField, since we might need to disable the
// search fields of the toolbox
this.onSearchTextFieldChange();
}, this, {single: true});
},
/**
* Event handler for the input event of the input of the {@link Zarafa.common.searchfield.ui.SearchTextField}
* Will disable the search field panels when the search query is a KQL query, or enable them otherwise
* @param {Zarafa.common.searchfield.ui.SearchTextField} searchTextField The text field where the
* search queries are entered.
*/
onSearchTextFieldChange : function()
{
var searchTextField = this.ownerCt.searchToolbar.contextMainPanelToolbar.searchFieldContainer.searchTextField;
var query = searchTextField.getValue();
var isKqlQuery = Zarafa.advancesearch.KQLParser.isKqlQuery(query);
if ( isKqlQuery ) {
this.searchInFieldset.disable();
this.categoryFilterFieldSet.disable();
} else {
this.searchInFieldset.enable();
this.categoryFilterFieldSet.enable();
}
},
/**
* Creates the folders fieldset for search tool box of form panel.
* @return {Object} config object for creating {@link Ext.form.FieldSet FieldSet}.
......@@ -330,6 +375,7 @@ Zarafa.advancesearch.dialogs.SearchToolBoxPanel = Ext.extend(Ext.Panel, {
cls: 'k-category-filter',
title: _('Filter category..'),
autoHeight: true,
ref: '../categoryFilterFieldSet',
items: [{
xtype: 'button',
iconCls: 'icon_category_add',
......@@ -385,6 +431,7 @@ Zarafa.advancesearch.dialogs.SearchToolBoxPanel = Ext.extend(Ext.Panel, {
width : 160,
border : false,
title: _('Search..'),
ref : '../searchInFieldset',
items : [{
xtype : 'checkboxgroup',
columns : 1,
......@@ -828,6 +875,7 @@ Zarafa.advancesearch.dialogs.SearchToolBoxPanel = Ext.extend(Ext.Panel, {
Zarafa.advancesearch.Actions.openCreateSearchFolderContentPanel(this.model, config);
},
/**
* Function will be used to create search restriction based on value entered in
* search textfield and {@link Zarafa.common.search.dialogs.SearchToolBoxPanel SearchToolBox}.
......@@ -844,8 +892,15 @@ Zarafa.advancesearch.dialogs.SearchToolBoxPanel = Ext.extend(Ext.Panel, {
return [];
}
var tokens = Zarafa.advancesearch.KQLParser.tokenize(textFieldValue);
if ( tokens ) {
var tokenRes = Zarafa.advancesearch.KQLParser.createTokenRestriction(tokens);
var andRes = [tokenRes];
} else {
andRes = [];
}
var finalRes = [];
var andRes = [];
var orResDate = [];
var orResSearchField = [];
var orResMessageClass = [];
......@@ -853,17 +908,19 @@ Zarafa.advancesearch.dialogs.SearchToolBoxPanel = Ext.extend(Ext.Panel, {
var orFilters = [];
Ext.iterate(this.searchCriteria, function(key, values) {
// search field restriction
if(key === 'search_fields') {
Ext.each(values, function(value){
orResSearchField.push(
Zarafa.core.data.RestrictionFactory.dataResContent(
value,
Zarafa.core.mapi.Restrictions.FL_SUBSTRING | Zarafa.core.mapi.Restrictions.FL_IGNORECASE,
textFieldValue
)
);
}, this);
if ( !tokens ) {
// search field restriction
if(key === 'search_fields') {
Ext.each(values, function(value){
orResSearchField.push(
Zarafa.core.data.RestrictionFactory.dataResContent(
value,
Zarafa.core.mapi.Restrictions.FL_SUBSTRING | Zarafa.core.mapi.Restrictions.FL_IGNORECASE,
textFieldValue
)
);
}, this);
}
}
if (key === 'extra_fields') {
......@@ -925,6 +982,7 @@ Zarafa.advancesearch.dialogs.SearchToolBoxPanel = Ext.extend(Ext.Panel, {
]);
}
}
// message class restriction
if(key === 'message_class' && !Ext.isEmpty(values)) {
Ext.each(values, function(value){
......@@ -939,7 +997,7 @@ Zarafa.advancesearch.dialogs.SearchToolBoxPanel = Ext.extend(Ext.Panel, {
}
// category restriction
if (key === 'categories' && !Ext.isEmpty(values)) {
if (!tokens && key === 'categories' && !Ext.isEmpty(values)) {
Ext.each(values, function (value) {
andResCategory.push(
Zarafa.core.data.RestrictionFactory.dataResContent(
......@@ -976,9 +1034,11 @@ Zarafa.advancesearch.dialogs.SearchToolBoxPanel = Ext.extend(Ext.Panel, {
if(!Ext.isEmpty(orResDate)) {
var andResDateSearchField = [];
andResDateSearchField.push(orResDate);
andResDateSearchField.push(Zarafa.core.data.RestrictionFactory.createResOr(orResSearchField));
if ( orResSearchField.length ) {
andResDateSearchField.push(Zarafa.core.data.RestrictionFactory.createResOr(orResSearchField));
}
andRes.push(Zarafa.core.data.RestrictionFactory.createResAnd(andResDateSearchField));
} else {
} else if ( orResSearchField.length ) {
/**
* If date information is not present in search criteria then create search restriction
* something like this.
......
......@@ -404,5 +404,45 @@ Zarafa.calendar.Actions = {
}
}, config.scope);
},
/**
* Converts record to an appointment and calls {@link Zarafa.core.data.UIFactory.openCreateRecord}
* to open the newly created appointment as editable record. The original record isn't removed.
*
* @param {Zarafa.core.data.IPMRecord} records The record which will be converted to an appointment
* @param {Zarafa.calendar.CalendarContextModel} model Used to create a new appointment record
*/
createAppointmentFromMail : function(records, model)
{
var record;
if (Array.isArray(records) && !Ext.isEmpty(records)) {
record = records[0];
} else {
return;
}
if (record.isOpened()) {
this.openHandler(record.getStore(), record, model);
} else {
// If record is not opened, then we need to reopen it to get the body. (For example when the selected records store reloads)
record.getStore().on('open', this.openHandler.createDelegate(this, [model], 2), this, {single : true});
record.open();
}
},
/**
* Handler for {@link Zarafa.core.data.IPMStore store} open event. Converts the opened record
* to an appointment and opens it as editable record.
*
* @param {Zarafa.core.data.IPMStore} store The store of the record.
* @param {Zarafa.core.data.IPMRecord} record The record which will be converted to an appointment
* @param {Zarafa.calendar.CalendarContextModel} model Used to create a new appointment record
*/
openHandler: function(store, record, model)
{
var newAppointmentRecord = record.convertToAppointment(model.getDefaultFolder());
Zarafa.core.data.UIFactory.openCreateRecord(newAppointmentRecord);
}
};
......@@ -73,6 +73,9 @@ Zarafa.calendar.CalendarContext = Ext.extend(Zarafa.core.Context, {
// The control will be shown when the user selects the calendar context from the button panel.
this.registerInsertionPoint('navigation.center', this.createCalendarNavigationPanel, this);
// Adds convert mail to appointment contextmenu item in the mail contextmenu.
this.registerInsertionPoint('context.mail.contextmenu.topoptions', this.convertToAppointment, this);
// Register the calendar category for the settings
this.registerInsertionPoint('context.settings.categories', this.createSettingCategories, this);
......@@ -675,6 +678,53 @@ Zarafa.calendar.CalendarContext = Ext.extend(Zarafa.core.Context, {
context: this.getName(),
id: 'mainmenu-button-calendar'
};
},
/**
* Adds a new contextmenu item in the mail context, which converts an email to an appointment
* @return {Zarafa.core.ui.menu.ConditionalItem} The Action context menu item
* @private
*/
convertToAppointment : function()
{
return {
xtype: 'zarafa.conditionalitem',
text : _('Create Appointment'),
iconCls : 'icon_new_appointment',
hidden: true,
handler: this.onContextItemCreateAppointment,
beforeShow: this.onBeforeShowCreateAppointment,
scope: this
};
},
/**
* Event Handler triggered when {@link #convertToAppointment convert to
* appointment} context menu item is clicked.
*
* @param {Zarafa.core.ui.menu.ConditionalItem} item context menu item
* @private
*/
onContextItemCreateAppointment : function(menuItem)
{
Zarafa.calendar.Actions.createAppointmentFromMail(menuItem.getRecords(), this.getModel());
},
/**
* onBeforeShow handler which disables showing the convert to appointment menuitem
* when a single item is selected and it's a mail item.
*
* @param {Zarafa.core.ui.menu.ConditionalItem} item context menu item
* @private
*/
onBeforeShowCreateAppointment : function(menuItem)
{
var records = menuItem.getRecords();
if (Array.isArray(records) && records.length === 1) {
menuItem.setVisible(records[0].isMessageClass('IPM.Note'));
} else {
menuItem.setVisible(false);
}
}
});
......
......@@ -34,7 +34,6 @@ Zarafa.calendar.ui.MeetingRequestButtons = Ext.extend(Ext.ButtonGroup, {
cls: 'zarafa-mr-buttons',
items: [
this.getRemoveFromCalendarButton(),
this.getNotCurrentButton(),
this.getNoResponseRequiredButton(),
this.getAcceptButton(),
this.getTentativeButton(),
......@@ -112,9 +111,6 @@ Zarafa.calendar.ui.MeetingRequestButtons = Ext.extend(Ext.ButtonGroup, {
// from the calendar.
this.removeFromCalendarButton.setVisible(isMeetingCanceled && !isSubMessage && !apptNotFound);
// When the meeting request is outdated, we can show the non-current button
this.nonCurrentButton.setVisible(isMeetingRequest && !isSubMessage && isMeetingOutOfDate);
// When this meeting is current, but the user did send this meeting request to himself,
// then the user doesn't need to respond.
this.noResponseButton.setVisible(isMeetingRequest && !isSubMessage && senderIsReceiver && !isMeetingOutOfDate);
......@@ -138,11 +134,9 @@ Zarafa.calendar.ui.MeetingRequestButtons = Ext.extend(Ext.ButtonGroup, {
this.calendarButton.setVisible((isMeetingRequest || isMeetingResponse) && !isSubMessage && !apptNotFound);
// Determine the action button
if ( this.acceptButton.isVisible() ){
this.calendarButton.getEl().removeClass('zarafa-action');
} else if ( this.nonCurrentButton.isVisible() ){
if (this.acceptButton.isVisible()) {
this.calendarButton.getEl().removeClass('zarafa-action');
} else if ( this.calendarButton.isVisible() ){
} else if (this.calendarButton.isVisible()) {
this.calendarButton.getEl().addClass('zarafa-action');
}
......@@ -180,27 +174,6 @@ Zarafa.calendar.ui.MeetingRequestButtons = Ext.extend(Ext.ButtonGroup, {
};
},
/**
* @return {Ext.Button} element config which should be
* added in the {@link Ext.Toolbar Toolbar}.
* @private
*/
getNotCurrentButton : function()
{
return {
xtype : 'button',
ref : 'nonCurrentButton',
text : _('Not Current'),
tooltip: {
title: _('Not Current'),
text: _('Meeting Request is out of date')
},
cls: 'tb-calendar-btn-not-current zarafa-action',
handler : this.onNotCurrent,
scope: this
};
},
/**
* @return {Ext.Button} element config which should be
* added in the {@link Ext.Toolbar Toolbar}.
......@@ -374,25 +347,6 @@ Zarafa.calendar.ui.MeetingRequestButtons = Ext.extend(Ext.ButtonGroup, {
};
},
/**
* Function sends request to remove outdated Meeting Request mails.
* @param {Ext.Button} button button object.
* @param {EventObject} eventObject The click event object.
* @private
*/
onNotCurrent : function(button, eventObject)
{
Ext.MessageBox.show({
title: _('Kopano WebApp'),
msg : _('This meeting request is out-of-date and will now be deleted.'),
icon: Ext.MessageBox.WARNING,
record: this.record,
fn: this.removeRecordOnOk,
scope: this,
buttons: Ext.MessageBox.OKCANCEL
});
},
/**
* Function sends request to remove Meeting Request mails which invites