Merge pull request #980 from amortsell/recurrence

Recurrence
This commit is contained in:
Laura Kokkarinen 2019-09-08 12:24:10 +03:00 committed by GitHub
commit 015a3699e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 325 additions and 8 deletions

View File

@ -18718,6 +18718,11 @@
"integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=", "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=",
"dev": true "dev": true
}, },
"string-format": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz",
"integrity": "sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA=="
},
"string-hash": { "string-hash": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz", "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz",

View File

@ -43,6 +43,7 @@
"react-dom": "16.7.0", "react-dom": "16.7.0",
"react-draft-wysiwyg": "^1.13.2", "react-draft-wysiwyg": "^1.13.2",
"spfx-uifabric-themes": "^0.6.0", "spfx-uifabric-themes": "^0.6.0",
"string-format": "^2.0.0",
"typescript": "^3.2.4", "typescript": "^3.2.4",
"xml2js": "^0.4.19" "xml2js": "^0.4.19"
}, },

View File

@ -29,4 +29,5 @@ export interface IEventState {
showRecurrenceSeriesInfo:boolean; showRecurrenceSeriesInfo:boolean;
newRecurrenceEvent:boolean; newRecurrenceEvent:boolean;
recurrenceAction:string; recurrenceAction:string;
recurrenceDescription?:string;
} }

View File

@ -4,6 +4,7 @@ import * as strings from 'CalendarWebPartStrings';
import { IEventProps } from './IEventProps'; import { IEventProps } from './IEventProps';
import { IEventState } from './IEventState'; import { IEventState } from './IEventState';
import * as moment from 'moment'; import * as moment from 'moment';
import { parseString } from 'xml2js';
import 'react-big-calendar/lib/css/react-big-calendar.css'; import 'react-big-calendar/lib/css/react-big-calendar.css';
import { PeoplePicker, PrincipalType } from "@pnp/spfx-controls-react/lib/PeoplePicker"; import { PeoplePicker, PrincipalType } from "@pnp/spfx-controls-react/lib/PeoplePicker";
import { import {
@ -44,6 +45,7 @@ import { Map, ICoordinates, MapType } from "@pnp/spfx-controls-react/lib/Map";
import { EventRecurrenceInfo } from '../../controls/EventRecurrenceInfo/EventRecurrenceInfo'; import { EventRecurrenceInfo } from '../../controls/EventRecurrenceInfo/EventRecurrenceInfo';
import { getGUID } from '@pnp/common'; import { getGUID } from '@pnp/common';
import { toLocaleShortDateString } from '../../utils/dateUtils'; import { toLocaleShortDateString } from '../../utils/dateUtils';
const format = require('string-format');
const DayPickerStrings: IDatePickerStrings = { const DayPickerStrings: IDatePickerStrings = {
months: [strings.January, strings.February, strings.March, strings.April, strings.May, strings.June, strings.July, strings.August, strings.September, strings.October, strings.November, strings.December], months: [strings.January, strings.February, strings.March, strings.April, strings.May, strings.June, strings.July, strings.August, strings.September, strings.October, strings.November, strings.December],
@ -177,6 +179,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
eventData.fRecurrence = true; eventData.fRecurrence = true;
eventData.UID = getGUID(); eventData.UID = getGUID();
panelMode = IPanelModelEnum.add; panelMode = IPanelModelEnum.add;
eventData.RecurrenceData = await this.returnExceptionRecurrenceInfo(eventData.RecurrenceData);
} }
startDate = `${moment(this.state.startDate).format('YYYY/MM/DD')}`; startDate = `${moment(this.state.startDate).format('YYYY/MM/DD')}`;
endDate = `${moment(this.state.endDate).format('YYYY/MM/DD')}`; endDate = `${moment(this.state.endDate).format('YYYY/MM/DD')}`;
@ -299,6 +302,8 @@ export class Event extends React.Component<IEventProps, IEventState> {
event.geolocation.Latitude = this.latitude; event.geolocation.Latitude = this.latitude;
event.geolocation.Longitude = this.longitude; event.geolocation.Longitude = this.longitude;
const recurrenceInfo = event.EventType === "4" && event.MasterSeriesItemID !== "" ? event.RecurrenceData : await this.returnExceptionRecurrenceInfo(event.RecurrenceData);
// Update Component Data // Update Component Data
this.setState({ this.setState({
eventData: event, eventData: event,
@ -315,6 +320,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
siteRegionalSettings: siteRegionalSettigns, siteRegionalSettings: siteRegionalSettigns,
locationLatitude: this.latitude, locationLatitude: this.latitude,
locationLongitude: this.longitude, locationLongitude: this.longitude,
recurrenceDescription: recurrenceInfo
}); });
} else { } else {
editorState = EditorState.createEmpty(); editorState = EditorState.createEmpty();
@ -337,8 +343,6 @@ export class Event extends React.Component<IEventProps, IEventState> {
* @memberof Event * @memberof Event
*/ */
public async componentDidMount() { public async componentDidMount() {
await this.renderEventData(); await this.renderEventData();
} }
@ -566,6 +570,280 @@ export class Event extends React.Component<IEventProps, IEventState> {
this.setState({ showRecurrenceSeriesInfo: true, recurrenceSeriesEdited: true }); this.setState({ showRecurrenceSeriesInfo: true, recurrenceSeriesEdited: true });
} }
/**
*
*
* @private
* @param {string} rule
* @memberof Event
*/
private parseDailyRule(rule): string {
const keys = Object.keys(rule);
if (keys.indexOf("weekday") !== -1 && rule["weekday"] === "TRUE")
return format("{} {}", format(strings.everyFormat, 1), strings.weekDayLabel);
if (keys.indexOf("dayFrequency") !== -1) {
const dayFrequency: number = parseInt(rule["dayFrequency"]);
const frequencyFormat = dayFrequency === 1 ? strings.everyFormat : dayFrequency === 2 ? strings.everySecondFormat : strings.everyNthFormat;
return format("{} {}", format(frequencyFormat, dayFrequency), strings.dayLable);
}
return "Invalid recurrence format";
}
/**
*
*
* @private
* @param { string } rule
* @memberof Event
*/
private parseWeeklyRule(rule): string {
const frequency: number = parseInt(rule["weekFrequency"]);
const keys = Object.keys(rule);
const dayMap: any = {
"mo": strings.Monday,
"tu": strings.Tuesday,
"we": strings.Wednesday,
"th": strings.Thursday,
"fr": strings.Friday,
"sa": strings.Saturday,
"su": strings.Sunday
};
let days: string[] = [];
for (let key of keys) {
days.push(dayMap[key]);
}
return format("{}{} {} {}",
frequency === 1 ? format(strings.everyFormat, frequency) : frequency === 2 ? format(strings.everySecondFormat, frequency): format(strings.everyNthFormat, frequency),
strings.weekLabel,
strings.onLabel,
days.join(", "));
}
/**
*
*
* @private
* @param { string } rule
* @memberof Event
*/
private parseMonthlyRule(rule): string {
const frequency: number = parseInt(rule["monthFrequency"]);
const day: number = parseInt(rule["day"]);
return format("{}{} {}",
frequency === 1 ? format(strings.everyFormat, frequency) : frequency === 2 ? format(strings.everySecondFormat, frequency): format(strings.everyNthFormat, frequency),
strings.monthLabel,
format(strings.onTheDayFormat, day)
);
}
/**
*
* @private
* @param { string } rule
* @memberof Event
*/
private parseMonthlyByDayRule(rule): string {
let keys: string[] = Object.keys(rule);
const dayTypeMap: any = {
"day": strings.weekDayLabel,
"weekend_day": strings.weekEndDay,
"mo": strings.Monday,
"tu": strings.Tuesday,
"we": strings.Wednesday,
"th": strings.Thursday,
"fr": strings.Friday,
"sa": strings.Saturday,
"su": strings.Sunday
};
const orderType: any = {
"first": strings.firstLabel,
"second": strings.secondLabel,
"third": strings.thirdLabel,
"fourth": strings.fourthLabel,
"last": strings.lastLabel
};
let order: string;
let dayType: string;
let frequencyFormat: string;
for (let key of keys) {
switch (key) {
case "monthFrequency":
const frequency = parseInt(rule[key]);
switch(frequency) {
case 1:
frequencyFormat = format(strings.everyFormat, frequency);
break;
case 2:
frequencyFormat = format(strings.everySecondFormat, frequency);
break;
default:
frequencyFormat = format(strings.everyNthFormat, frequency);
break;
}
break;
case "weekDayOfMonth":
order = orderType[rule[key]];
break;
default:
dayType = dayTypeMap[rule[key]];
break;
}
}
return format("{} {} {} {} {}{}",
frequencyFormat,
strings.monthLabel.toLowerCase(),
strings.onTheLabel,
order,
dayType,
strings.theSuffix);
}
/**
*
* @private
* @param rule
* @memberof Event
*/
private parseYearlyRule(rule): string {
const keys: string[] = Object.keys(rule);
const months: string[] = DayPickerStrings.months;
let frequencyString: string;
let month: string;
let day: string;
for (let key of keys) {
switch(key) {
case "yearFrequency":
const frequency = parseInt(rule[key]);
const frequencyFormat = frequency == 1 ? strings.everyFormat : frequency == 2 ? strings.everySecondFormat : strings.everyNthFormat;
frequencyString = format(frequencyFormat, frequency);
break;
case "month":
month = months[parseInt(rule[key]) - 1];
break;
case "day":
day = rule[key];
break;
}
}
return format("{} {} {}", frequencyString, strings.yearLabel, format(strings.theNthOfMonthFormat, month, day));
}
/**
*
*
* @private
* @param rule
* @memberof Event
*/
private parseYearlyByDayRule(rule): string {
const keys: string[] = Object.keys(rule);
const months: string[] = DayPickerStrings.months;
const orderMap: any = {
"first": strings.firstLabel,
"second": strings.secondLabel,
"third": strings.thirdLabel,
"fourth": strings.fourthLabel,
"last": strings.lastLabel
};
const dayTypeMap: any = {
"day": strings.weekDayLabel,
"weekend_day": strings.weekEndDay,
"mo": strings.Monday,
"tu": strings.Tuesday,
"we": strings.Wednesday,
"th": strings.Thursday,
"fr": strings.Friday,
"sa": strings.Saturday,
"su": strings.Sunday
};
let frequencyString: string;
let month: string;
let order: string;
let dayTypeString: string;
for (let key of keys) {
switch(key) {
case "yearFrequency":
const frequency = parseInt(rule[key]);
const frequencyFormat = frequency === 1 ? strings.everyFormat : frequency === 2 ? strings.everySecondFormat : strings.everyNthFormat;
frequencyString = format(frequencyFormat, frequency);
break;
case "weekDayOfMonth":
order = orderMap[rule[key]];
break;
case "month":
month = months[parseInt(rule[key]) - 1];
break;
default:
dayTypeString = dayTypeMap[rule[key]];
break;
}
return format("{} {} {}",
frequencyString,
strings.yearLabel,
format(strings.onTheDayTypeFormat, order, dayTypeString.toLowerCase(), strings.theSuffix)
);
}
}
/**
*
*
* @private
* @param {string} recurrenceData
* @memberof Event
*/
private async returnExceptionRecurrenceInfo(recurrenceData: string) {
const promise = new Promise<object>((resolve, reject) => {
parseString(recurrenceData, (err, result) => {
if (err) {
reject(err);
}
resolve(result);
});
});
const recurrenceInfo: any = await promise;
let keys = Object.keys(recurrenceInfo.recurrence.rule[0].repeat[0]);
const recurrenceTypes = ["daily", "weekly", "monthly", "monthlyByDay", "yearly", "yearlyByDay"];
for (var key of keys) {
const rule = recurrenceInfo.recurrence.rule[0].repeat[0][key][0]['$'];
switch(recurrenceTypes.indexOf(key)) {
case 0:
return this.parseDailyRule(rule);
break;
case 1:
return this.parseWeeklyRule(rule);
break;
case 2:
return this.parseMonthlyRule(rule);
break;
case 3:
return this.parseMonthlyByDayRule(rule);
break;
case 4:
return this.parseYearlyRule(rule);
break;
case 5:
return this.parseYearlyByDayRule(rule);
break;
default:
continue;
}
}
}
/** /**
* *
* *
@ -579,6 +857,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
//console.log(this.returnedRecurrenceInfo); //console.log(this.returnedRecurrenceInfo);
} }
/** /**
* *
* *
@ -588,6 +867,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
public render(): React.ReactElement<IEventProps> { public render(): React.ReactElement<IEventProps> {
const { editorState } = this.state; const { editorState } = this.state;
return ( return (
<div> <div>
<Panel <Panel
@ -617,6 +897,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
(this.state.eventData && (this.state.eventData.EventType !== "0" && this.state.showRecurrenceSeriesInfo !== true)) ? (this.state.eventData && (this.state.eventData.EventType !== "0" && this.state.showRecurrenceSeriesInfo !== true)) ?
<div> <div>
<h2 style={{ display: 'inline-block', verticalAlign: 'top' }}>{ strings.recurrenceEventLabel }</h2> <h2 style={{ display: 'inline-block', verticalAlign: 'top' }}>{ strings.recurrenceEventLabel }</h2>
{ this.state.recurrenceDescription ? <span style={{ display: 'block' }} >{ this.state.recurrenceDescription }</span> : null }
<DefaultButton <DefaultButton
style={{ display: 'inline-block', marginLeft: '330px', verticalAlign: 'top', width: 'auto' }} style={{ display: 'inline-block', marginLeft: '330px', verticalAlign: 'top', width: 'auto' }}
iconProps={{ iconName: 'RecurringEvent' }} iconProps={{ iconName: 'RecurringEvent' }}

View File

@ -368,6 +368,10 @@ export default class parseRecurrentEvent {
if ((new Date(init) > end) || (rTotal > 0 && rTotal <= total)) loop = false; if ((new Date(init) > end) || (rTotal > 0 && rTotal <= total)) loop = false;
} }
} }
if (e.fRecurrence === "1" && e.MasterSeriesItemID !== "") {
const ni = this.cloneObj(e);
er.push(ni);
}
return er; return er;
} //end recurrence check } //end recurrence check
} }

View File

@ -126,6 +126,15 @@ define([], function () {
patternLabel: "Pattern", patternLabel: "Pattern",
dateRangeLabel: "Date Range", dateRangeLabel: "Date Range",
occurrencesLabel: "occurrences", occurrencesLabel: "occurrences",
ofMonthLabel: "of" ofMonthLabel: "of",
everyFormat: "Every {0} ",
everySecondFormat: "Every {0} ",
everyNthFormat: "Every {0} ",
onTheDayFormat: "on the {0}th day",
onTheLabel: "on the",
theSuffix: "",
yearLabel: "year",
theNthOfMonthFormat: "on {0} {1}",
onTheDayTypeFormat: "on the {0} {1}"
} }
}); });

View File

@ -127,7 +127,15 @@ declare interface ICalendarWebPartStrings {
dateRangeLabel: string; dateRangeLabel: string;
occurrencesLabel: string; occurrencesLabel: string;
ofMonthLabel:string; ofMonthLabel:string;
everyFormat: string;
everySecondFormat: string;
everyNthFormat: string;
onTheDayFormat: string;
onTheLabel: string;
theSuffix: string;
yearLabel: string;
theNthOfMonthFormat: string;
onTheDayTypeFormat: string;
} }
declare module 'CalendarWebPartStrings' { declare module 'CalendarWebPartStrings' {

View File

@ -5,7 +5,7 @@ define([], function () {
OcurrencesLabel: "Tillfällen", OcurrencesLabel: "Tillfällen",
dateRangeLabel: "Datumintervall", dateRangeLabel: "Datumintervall",
weekEndDay: "Helgdag", weekEndDay: "Helgdag",
weekDayLabel: "Veckodag", weekDayLabel: "Arbetsdag",
lastLabel: "sista", lastLabel: "sista",
fourthLabel: "fjärde", fourthLabel: "fjärde",
thirdLabel: "tredje", thirdLabel: "tredje",
@ -16,7 +16,7 @@ define([], function () {
ofEveryLabel: "för varje ", ofEveryLabel: "för varje ",
AllowedValues1to12Label: "Tillåtna värder 1 till 12", AllowedValues1to12Label: "Tillåtna värder 1 till 12",
noEndDate: "inget slutdatum", noEndDate: "inget slutdatum",
everyweekdays: "alla veckodagar", everyweekdays: "alla arbetsdagar",
days: "dag", days: "dag",
every: "var", every: "var",
EndByLabel: "slutar den", EndByLabel: "slutar den",
@ -113,7 +113,7 @@ define([], function () {
nextLabel: "Nästa", nextLabel: "Nästa",
showMore: "mer", showMore: "mer",
recurrenceEventLabel: "Återkommande händelse", recurrenceEventLabel: "Återkommande händelse",
editRecurrenceSeries: "Redigera återkommande händelse", editRecurrenceSeries: "Redigera serie",
ifRecurrenceLabel: "Återkommande ?", ifRecurrenceLabel: "Återkommande ?",
onLabel: "På", onLabel: "På",
offLabel: "Av", offLabel: "Av",
@ -126,7 +126,15 @@ define([], function () {
patternLabel: "Schema", patternLabel: "Schema",
dateRangeLabel: "Datumintervall", dateRangeLabel: "Datumintervall",
occurrencesLabel: "tillfällen", occurrencesLabel: "tillfällen",
ofMonthLabel: "i" ofMonthLabel: "i",
everyFormat: "Varje ",
everySecondFormat: "Varannan ",
everyNthFormat: "Var {0}:e ",
onTheDayFormat: "den {0}:e dagen",
onTheLabel: "på den",
theSuffix: "en",
theNthOfMonthFormat: "den {1} {0}",
onTheDayTypeFormat: "på den {0} {1}{2}"
} }
}); });