parent
fec263a3d6
commit
99e5c1b93c
5
NEWS
5
NEWS
|
@ -1,6 +1,10 @@
|
|||
3.0.2 (2016-02-DD)
|
||||
------------------
|
||||
|
||||
New features
|
||||
- [web] show all/only this calendar
|
||||
- [web] convert a message to an appointment or a task (#1722)
|
||||
|
||||
Enhancements
|
||||
- [web] added Junk handling feature from v2
|
||||
- [web] updated Material Icons font to version 2.1.3
|
||||
|
@ -8,7 +12,6 @@ Enhancements
|
|||
- [web] mail filters are now sortable
|
||||
- [web] now supports RFC6154 and NoInferiors IMAP flag
|
||||
- [web] improved confirm dialogs for deletions
|
||||
- [web] show all/only this calendar
|
||||
- [web] allow resources to prevent invitations (#3410)
|
||||
|
||||
Bug fixes
|
||||
|
|
|
@ -167,8 +167,7 @@
|
|||
NSMutableArray *keys;
|
||||
NSArray *acceptedTypes;
|
||||
|
||||
acceptedTypes
|
||||
= [NSArray arrayWithObjects: @"text/plain", @"text/html", nil];
|
||||
acceptedTypes = [NSArray arrayWithObjects: @"text/plain", @"text/html", nil];
|
||||
keys = [NSMutableArray array];
|
||||
[self addRequiredKeysOfStructure: [self bodyStructure]
|
||||
path: @"" toArray: keys acceptedTypes: acceptedTypes
|
||||
|
|
|
@ -187,6 +187,13 @@
|
|||
"Save As..." = "Save As...";
|
||||
"Print Preview" = "Print Preview";
|
||||
"View Message Source" = "View Message Source";
|
||||
|
||||
/* Message view "more" menu: create an event from message */
|
||||
"Convert To Event" = "Convert To Event";
|
||||
|
||||
/* Message view "more" menu: create a task from message */
|
||||
"Convert To Task" = "Convert To Task";
|
||||
|
||||
"Print..." = "Print...";
|
||||
"Delete Message" = "Delete Message";
|
||||
"Delete Selected Messages" = "Delete Selected Messages";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* UIxMailActions.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2007-2014 Inverse inc.
|
||||
* Copyright (C) 2007-2016 Inverse inc.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -29,10 +29,13 @@
|
|||
#import <NGObjWeb/WOResponse.h>
|
||||
#import <NGObjWeb/NSException+HTTP.h>
|
||||
|
||||
#import <SoObjects/Mailer/NSString+Mail.h>
|
||||
#import <SoObjects/Mailer/SOGoDraftObject.h>
|
||||
#import <SoObjects/Mailer/SOGoDraftsFolder.h>
|
||||
#import <SoObjects/Mailer/SOGoMailAccount.h>
|
||||
#import <SoObjects/Mailer/SOGoMailObject.h>
|
||||
#import <SoObjects/Mailer/SOGoMailObject+Draft.h>
|
||||
#import <SoObjects/SOGo/NSArray+Utilities.h>
|
||||
#import <SoObjects/SOGo/NSDictionary+Utilities.h>
|
||||
#import <SoObjects/SOGo/NSString+Utilities.h>
|
||||
#import <SoObjects/SOGo/SOGoUser.h>
|
||||
|
@ -121,6 +124,61 @@
|
|||
andString: [data jsonRepresentation]];
|
||||
}
|
||||
|
||||
- (WOResponse *) viewPlainAction
|
||||
{
|
||||
BOOL htmlContent;
|
||||
NSArray *acceptedTypes, *types;
|
||||
NSDictionary *parts;
|
||||
NSMutableArray *keys;
|
||||
NSMutableDictionary *data;
|
||||
NSString *rawPart, *contentKey, *subject, *content;
|
||||
NSUInteger index;
|
||||
SOGoMailObject *co;
|
||||
|
||||
co = [self clientObject];
|
||||
subject = [co decodedSubject];
|
||||
htmlContent = NO;
|
||||
data = [NSMutableDictionary dictionary];
|
||||
|
||||
if (subject)
|
||||
[data setObject: subject
|
||||
forKey: @"subject"];
|
||||
|
||||
// Fetch the text parts of the message body structure
|
||||
acceptedTypes = [NSArray arrayWithObjects: @"text/plain", @"text/html", nil];
|
||||
keys = [NSMutableArray array];
|
||||
[co addRequiredKeysOfStructure: [co bodyStructure]
|
||||
path: @"" toArray: keys acceptedTypes: acceptedTypes
|
||||
withPeek: NO];
|
||||
|
||||
// Use plain part if available, otherwise use the HTML part
|
||||
types = [keys objectsForKey: @"mimeType" notFoundMarker: @""];
|
||||
index = [types indexOfObject: @"text/plain"];
|
||||
if (index == NSNotFound)
|
||||
{
|
||||
index = [types indexOfObject: @"text/html"];
|
||||
htmlContent = YES;
|
||||
}
|
||||
|
||||
// Fetch part and convert HTML if necessary
|
||||
contentKey = [keys objectAtIndex: index];
|
||||
parts = [co fetchPlainTextStrings: [NSArray arrayWithObject: contentKey]];
|
||||
if ([parts count] > 0)
|
||||
{
|
||||
rawPart = [[parts allValues] objectAtIndex: 0];
|
||||
if (htmlContent)
|
||||
content = [rawPart htmlToText];
|
||||
else
|
||||
content = rawPart;
|
||||
if (content)
|
||||
[data setObject: [content stringByTrimmingSpaces]
|
||||
forKey: @"content"];
|
||||
}
|
||||
|
||||
return [self responseWithStatus: 201
|
||||
andString: [data jsonRepresentation]];
|
||||
}
|
||||
|
||||
/* active message */
|
||||
|
||||
- (id) markMessageUnflaggedAction
|
||||
|
|
|
@ -278,6 +278,11 @@
|
|||
actionClass = "UIxMailActions";
|
||||
actionName = "forward";
|
||||
};
|
||||
viewplain = {
|
||||
protectedBy = "View";
|
||||
actionClass = "UIxMailActions";
|
||||
actionName = "viewPlain";
|
||||
};
|
||||
markMessageUncollapse = {
|
||||
protectedBy = "View";
|
||||
actionClass = "UIxMailActions";
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
xmlns:label="OGo:label"
|
||||
className="UIxPageFrame"
|
||||
title="title"
|
||||
const:jsFiles="Common.js, Preferences.services.js, Contacts.services.js, Mailer.js, Mailer.services.js, vendor/ckeditor/ckeditor.js, vendor/ckeditor/ck.js, vendor/angular-file-upload.min.js">
|
||||
const:jsFiles="Common.js, Preferences.services.js, Contacts.services.js, Scheduler.services.js, Mailer.js, Mailer.services.js, vendor/ckeditor/ckeditor.js, vendor/ckeditor/ck.js, vendor/angular-file-upload.min.js">
|
||||
<script type="text/javascript">
|
||||
var mailAccounts = <var:string value="mailAccounts" const:escapeHTML="NO" />;
|
||||
var userNames = <var:string value="userNames" const:escapeHTML="NO" />;
|
||||
|
@ -116,7 +116,8 @@
|
|||
md-menu-origin="md-menu-origin">more_vert</md-icon>
|
||||
<md-menu-content width="3">
|
||||
<md-menu-item>
|
||||
<md-button type="button" md-menu-align-target="md-menu-align-target" ng-click="app.markFolderRead(folder)">
|
||||
<md-button type="button" md-menu-align-target="md-menu-align-target"
|
||||
ng-click="app.markFolderRead(folder)">
|
||||
<var:string label:value="Mark Folder Read"/>
|
||||
</md-button>
|
||||
</md-menu-item>
|
||||
|
|
|
@ -87,6 +87,19 @@
|
|||
<var:string label:value="View Message Source"/>
|
||||
</md-button>
|
||||
</md-menu-item>
|
||||
<md-menu-divider><!-- divider --></md-menu-divider>
|
||||
<md-menu-item>
|
||||
<md-button label:aria-label="Convert To Event"
|
||||
ng-click="viewer.convertToEvent($event)">
|
||||
<var:string label:value="Convert To Event"/>
|
||||
</md-button>
|
||||
</md-menu-item>
|
||||
<md-menu-item>
|
||||
<md-button label:aria-label="Convert To Task"
|
||||
ng-click="viewer.convertToTask($event)">
|
||||
<var:string label:value="Convert To Task"/>
|
||||
</md-button>
|
||||
</md-menu-item>
|
||||
</md-menu-content>
|
||||
</md-menu>
|
||||
</md-card-actions>
|
||||
|
|
|
@ -2,8 +2,8 @@ module.exports = function(grunt) {
|
|||
var js_files = {
|
||||
'js/Common.js': ['js/Common/*.app.js', 'js/Common/*.filter.js', 'js/Common/*Controller.js', 'js/Common/*.service.js', 'js/Common/*.directive.js', 'js/Common/utils.js'],
|
||||
'js/Main.js': ['js/Main/Main.app.js'],
|
||||
'js/Scheduler.services.js': ['js/Scheduler/*.service.js'],
|
||||
'js/Scheduler.js': ['js/Scheduler/Scheduler.app.js', 'js/Scheduler/*Controller.js', 'js/Scheduler/*.directive.js'],
|
||||
'js/Scheduler.services.js': ['js/Scheduler/*.service.js', 'js/Scheduler/*Controller.js', 'js/Scheduler/*.directive.js'],
|
||||
'js/Scheduler.js': ['js/Scheduler/Scheduler.app.js'],
|
||||
'js/Contacts.services.js': ['js/Contacts/*.service.js'],
|
||||
'js/Contacts.js': ['js/Contacts/Contacts.app.js', 'js/Contacts/*Controller.js', 'js/Contacts/*.directive.js'],
|
||||
'js/Mailer.services.js': ['js/Mailer/*.service.js', 'js/Mailer/*Controller.js', 'js/Mailer/*.directive.js'],
|
||||
|
|
|
@ -328,7 +328,7 @@ Date.prototype.beginOfDay = function() {
|
|||
return beginOfDay;
|
||||
};
|
||||
|
||||
Date.prototype.beginOfWeek = function() {
|
||||
Date.prototype.beginOfWeek = function(firstDayOfWeek) {
|
||||
var offset = firstDayOfWeek - this.getDay();
|
||||
if (offset > 0)
|
||||
offset -= 7;
|
||||
|
@ -340,8 +340,8 @@ Date.prototype.beginOfWeek = function() {
|
|||
return beginOfWeek;
|
||||
};
|
||||
|
||||
Date.prototype.endOfWeek = function() {
|
||||
var endOfWeek = this.beginOfWeek();
|
||||
Date.prototype.endOfWeek = function(firstDayOfWeek) {
|
||||
var endOfWeek = this.beginOfWeek(firstDayOfWeek);
|
||||
endOfWeek.addDays(6);
|
||||
|
||||
endOfWeek.setHours(23);
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular.module('SOGo.MailerUI', ['ui.router', 'ck', 'angularFileUpload', 'SOGo.Common', 'SOGo.ContactsUI', 'ngAnimate', 'SOGo.PreferencesUI'])
|
||||
angular.module('SOGo.MailerUI', ['ui.router', 'ck', 'angularFileUpload', 'SOGo.Common', 'SOGo.ContactsUI', 'SOGo.SchedulerUI', 'ngAnimate', 'SOGo.PreferencesUI'])
|
||||
.config(configure)
|
||||
.run(runBlock);
|
||||
|
||||
|
|
|
@ -362,6 +362,15 @@
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @function $plainContent
|
||||
* @memberof Message.prototype
|
||||
* @returns the a plain text representation of the subject and body
|
||||
*/
|
||||
Message.prototype.$plainContent = function() {
|
||||
return Message.$$resource.fetch(this.$absolutePath(), 'viewplain');
|
||||
};
|
||||
|
||||
/**
|
||||
* @function addTag
|
||||
* @memberof Message.prototype
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
/**
|
||||
* @ngInject
|
||||
*/
|
||||
MessageController.$inject = ['$window', '$scope', '$state', '$mdDialog', 'stateAccounts', 'stateAccount', 'stateMailbox', 'stateMessage', 'encodeUriFilter', 'sgSettings', 'sgFocus', 'Dialog', 'Account', 'Mailbox', 'Message'];
|
||||
function MessageController($window, $scope, $state, $mdDialog, stateAccounts, stateAccount, stateMailbox, stateMessage, encodeUriFilter, sgSettings, focus, Dialog, Account, Mailbox, Message) {
|
||||
MessageController.$inject = ['$window', '$scope', '$state', '$mdDialog', 'stateAccounts', 'stateAccount', 'stateMailbox', 'stateMessage', 'encodeUriFilter', 'sgSettings', 'sgFocus', 'Dialog', 'Calendar', 'Component', 'Account', 'Mailbox', 'Message'];
|
||||
function MessageController($window, $scope, $state, $mdDialog, stateAccounts, stateAccount, stateMailbox, stateMessage, encodeUriFilter, sgSettings, focus, Dialog, Calendar, Component, Account, Mailbox, Message) {
|
||||
var vm = this, messageDialog = null, popupWindow = null;
|
||||
|
||||
// Expose controller
|
||||
|
@ -35,6 +35,8 @@
|
|||
vm.saveMessage = saveMessage;
|
||||
vm.toggleRawSource = toggleRawSource;
|
||||
vm.showRawSource = false;
|
||||
vm.convertToEvent = convertToEvent;
|
||||
vm.convertToTask = convertToTask;
|
||||
|
||||
// One-way refresh of the parent window when modifying the message from a popup window.
|
||||
if ($window.opener) {
|
||||
|
@ -254,6 +256,45 @@
|
|||
vm.showRawSource = !vm.showRawSource;
|
||||
}
|
||||
}
|
||||
|
||||
function convertToEvent($event) {
|
||||
return convertToComponent($event, 'appointment');
|
||||
}
|
||||
|
||||
function convertToTask($event) {
|
||||
return convertToComponent($event, 'task');
|
||||
}
|
||||
|
||||
function convertToComponent($event, type) {
|
||||
vm.message.$plainContent().then(function(data) {
|
||||
var componentData = {
|
||||
pid: Calendar.$defaultCalendar(),
|
||||
type: type,
|
||||
summary: data.subject,
|
||||
comment: data.content
|
||||
};
|
||||
var component = new Component(componentData);
|
||||
// UI/Templates/SchedulerUI/UIxAppointmentEditorTemplate.wox or
|
||||
// UI/Templates/SchedulerUI/UIxTaskEditorTemplate.wox
|
||||
var templateUrl = [
|
||||
sgSettings.activeUser('folderURL'),
|
||||
'Calendar',
|
||||
'UIx' + type.capitalize() + 'EditorTemplate'
|
||||
].join('/');
|
||||
return $mdDialog.show({
|
||||
parent: angular.element(document.body),
|
||||
targetEvent: $event,
|
||||
clickOutsideToClose: true,
|
||||
escapeToClose: true,
|
||||
templateUrl: templateUrl,
|
||||
controller: 'ComponentEditorController',
|
||||
controllerAs: 'editor',
|
||||
locals: {
|
||||
stateComponent: component
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
angular
|
||||
|
|
|
@ -124,6 +124,14 @@
|
|||
_this.$calendars.push(calendar);
|
||||
});
|
||||
}
|
||||
else if (angular.isUndefined(this.$calendars)) {
|
||||
this.$calendars = [];
|
||||
this.$subscriptions = [];
|
||||
this.$webcalendars = [];
|
||||
Calendar.$$resource.fetch('calendarslist').then(function(data) {
|
||||
Calendar.$findAll(data.calendars, writable);
|
||||
});
|
||||
}
|
||||
|
||||
if (writable) {
|
||||
return _.union(this.$calendars, _.filter(this.$subscriptions, function(calendar) { return calendar.acls.objectCreator; }));
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
$Preferences: Preferences,
|
||||
$Card: Card,
|
||||
$gravatar: Gravatar,
|
||||
$$resource: new Resource(Settings.baseURL(), Settings.activeUser()),
|
||||
$$resource: new Resource(Settings.activeUser('folderURL') + 'Calendar', Settings.activeUser()),
|
||||
timeFormat: "%H:%M",
|
||||
// Filter parameters common to events and tasks
|
||||
$query: { value: '', search: 'title_Category_Location' },
|
||||
|
@ -236,35 +236,40 @@
|
|||
* @returns a promise of a collection of objects describing the events blocks
|
||||
*/
|
||||
Component.$eventsBlocksForView = function(view, date) {
|
||||
var viewAction, startDate, endDate, params;
|
||||
var _this = this;
|
||||
|
||||
if (view == 'day') {
|
||||
viewAction = 'dayView';
|
||||
startDate = endDate = date;
|
||||
}
|
||||
else if (view == 'multicolumnday') {
|
||||
viewAction = 'multicolumndayView';
|
||||
startDate = endDate = date;
|
||||
}
|
||||
else if (view == 'week') {
|
||||
viewAction = 'weekView';
|
||||
startDate = date.beginOfWeek();
|
||||
endDate = new Date();
|
||||
endDate.setTime(startDate.getTime());
|
||||
endDate.addDays(6);
|
||||
}
|
||||
else if (view == 'month') {
|
||||
viewAction = 'monthView';
|
||||
startDate = date;
|
||||
startDate.setDate(1);
|
||||
startDate = startDate.beginOfWeek();
|
||||
endDate = new Date();
|
||||
endDate.setTime(startDate.getTime());
|
||||
endDate.setMonth(endDate.getMonth() + 1);
|
||||
endDate.addDays(-1);
|
||||
endDate = endDate.endOfWeek();
|
||||
}
|
||||
return this.$eventsBlocks(viewAction, startDate, endDate);
|
||||
return Component.$Preferences.ready().then(function(data) {
|
||||
var firstDayOfWeek, viewAction, startDate, endDate, params;
|
||||
firstDayOfWeek = Component.$Preferences.defaults.SOGoFirstDayOfWeek;
|
||||
|
||||
if (view == 'day') {
|
||||
viewAction = 'dayView';
|
||||
startDate = endDate = date;
|
||||
}
|
||||
else if (view == 'multicolumnday') {
|
||||
viewAction = 'multicolumndayView';
|
||||
startDate = endDate = date;
|
||||
}
|
||||
else if (view == 'week') {
|
||||
viewAction = 'weekView';
|
||||
startDate = date.beginOfWeek(firstDayOfWeek);
|
||||
endDate = new Date();
|
||||
endDate.setTime(startDate.getTime());
|
||||
endDate.addDays(6);
|
||||
}
|
||||
else if (view == 'month') {
|
||||
viewAction = 'monthView';
|
||||
startDate = date;
|
||||
startDate.setDate(1);
|
||||
startDate = startDate.beginOfWeek(firstDayOfWeek);
|
||||
endDate = new Date();
|
||||
endDate.setTime(startDate.getTime());
|
||||
endDate.setMonth(endDate.getMonth() + 1);
|
||||
endDate.addDays(-1);
|
||||
endDate = endDate.endOfWeek(firstDayOfWeek);
|
||||
}
|
||||
return _this.$eventsBlocks(viewAction, startDate, endDate);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue