Merge pull request #3076 from petkir/petkir-react-calendar-feed-Upgrade-jszip

react-calendar-feed devDependencies Version Missmatch Upgrade to SPFX 1.15.2
This commit is contained in:
Hugo Bernier 2022-11-08 13:26:15 -05:00 committed by GitHub
commit d5f95c8087
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 46241 additions and 15427 deletions

View File

@ -0,0 +1,5 @@
require('@rushstack/eslint-config/patch/modern-module-resolution');
module.exports = {
extends: ['@microsoft/eslint-config-spfx/lib/profiles/react'],
parserOptions: { tsconfigRootDir: __dirname }
};

View File

@ -30,3 +30,6 @@ obj
# Styles Generated Code # Styles Generated Code
*.scss.ts *.scss.ts
.heft
release

View File

View File

@ -4,26 +4,10 @@
* Chrome browser: https://aka.ms/spfx-debugger-extensions * Chrome browser: https://aka.ms/spfx-debugger-extensions
*/ */
"version": "0.2.0", "version": "0.2.0",
"configurations": [{ "configurations": [
"name": "Local workbench",
"type": "chrome",
"request": "launch",
"url": "https://localhost:4321/temp/workbench.html",
"webRoot": "${workspaceRoot}",
"sourceMaps": true,
"sourceMapPathOverrides": {
"webpack:///.././src/*": "${webRoot}/src/*",
"webpack:///../../../src/*": "${webRoot}/src/*",
"webpack:///../../../../src/*": "${webRoot}/src/*",
"webpack:///../../../../../src/*": "${webRoot}/src/*"
},
"runtimeArgs": [
"--remote-debugging-port=9222"
]
},
{ {
"name": "Hosted workbench", "name": "Hosted workbench",
"type": "chrome", "type": "pwa-chrome",
"request": "launch", "request": "launch",
"url": "https://enter-your-SharePoint-site/_layouts/workbench.aspx", "url": "https://enter-your-SharePoint-site/_layouts/workbench.aspx",
"webRoot": "${workspaceRoot}", "webRoot": "${workspaceRoot}",
@ -40,4 +24,4 @@
] ]
} }
] ]
} }

View File

@ -2,7 +2,7 @@
"@microsoft/generator-sharepoint": { "@microsoft/generator-sharepoint": {
"isCreatingSolution": true, "isCreatingSolution": true,
"environment": "spo", "environment": "spo",
"version": "1.10.0", "version": "1.15.0",
"libraryName": "react-calendar-feed", "libraryName": "react-calendar-feed",
"libraryId": "25653136-fc83-4abe-b9d2-a4ac041959d5", "libraryId": "25653136-fc83-4abe-b9d2-a4ac041959d5",
"packageManager": "npm", "packageManager": "npm",

View File

@ -25,25 +25,31 @@ For more information about how this solution was built, including some design de
## Compatibility ## Compatibility
![SPFx 1.10](https://img.shields.io/badge/SPFx-1.10.0-green.svg) This sample is optimally compatible with the following environment configuration:
![Node.js v10 | v8](https://img.shields.io/badge/Node.js-v10%20%7C%20v8-green.svg)
![SPFx 1.15.2](https://img.shields.io/badge/SPFx-1.15.2-green.svg)
![Node.js v16 | v14 | v12](https://img.shields.io/badge/Node.js-v16%20%7C%20v14%20%7C%20v12-green.svg)
![Compatible with SharePoint Online](https://img.shields.io/badge/SharePoint%20Online-Compatible-green.svg) ![Compatible with SharePoint Online](https://img.shields.io/badge/SharePoint%20Online-Compatible-green.svg)
![Does not work with SharePoint 2019](https://img.shields.io/badge/SharePoint%20Server%202019-Incompatible-red.svg "SharePoint Server 2019 requires SPFx 1.4.1 or lower") ![Does not work with SharePoint 2019](https://img.shields.io/badge/SharePoint%20Server%202019-Incompatible-red.svg "SharePoint Server 2019 requires SPFx 1.4.1 or lower")
![Does not work with SharePoint 2016 (Feature Pack 2)](https://img.shields.io/badge/SharePoint%20Server%202016%20(Feature%20Pack%202)-Incompatible-red.svg "SharePoint Server 2016 Feature Pack 2 requires SPFx 1.1") ![Does not work with SharePoint 2016 (Feature Pack 2)](https://img.shields.io/badge/SharePoint%20Server%202016%20(Feature%20Pack%202)-Incompatible-red.svg "SharePoint Server 2016 Feature Pack 2 requires SPFx 1.1")
![Local Workbench Partially](https://img.shields.io/badge/Local%20Workbench-Partially-yellow.svg "Some functionality may not work on local workbech") ![Local Workbench Unsupported](https://img.shields.io/badge/Local%20Workbench-Unsupported-red.svg "Local workbench is no longer available as of SPFx 1.13 and above")
![Hosted Workbench Compatible](https://img.shields.io/badge/Hosted%20Workbench-Compatible-green.svg) ![Hosted Workbench Compatible](https://img.shields.io/badge/Hosted%20Workbench-Compatible-green.svg)
![Compatible with Remote Containers](https://img.shields.io/badge/Remote%20Containers-Compatible-green.svg) ![Compatible with Remote Containers](https://img.shields.io/badge/Remote%20Containers-Compatible-green.svg)
For more information about SPFx compatibility, please refer to <https://aka.ms/spfx-matrix>
## Applies to ## Applies to
- [SharePoint Framework](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview) - [SharePoint Framework](https://learn.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
- [Microsoft 365 tenant](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-development-environment) - [Microsoft 365 tenant](https://learn.microsoft.com/sharepoint/dev/spfx/set-up-your-development-environment)
> Get your own free development tenant by subscribing to [Microsoft 365 developer program](http://aka.ms/m365devprogram)
## Prerequisites ## Prerequisites
Before you can use this web part example, you will need one of the following: Before you can use this web part example, you will need one of the following:
- A publicly-accessible iCal feed (i.e.: .ics) - A publicly-accessible iCal feed (i.e.: `.ics`)
- A publicly-accessible RSS feed of events (e.g.: Google calendar) - A publicly-accessible RSS feed of events (e.g.: Google calendar)
- A WordPress WP-FullCalendar feed - A WordPress WP-FullCalendar feed
- An Exchange Public Calendar - An Exchange Public Calendar
@ -70,6 +76,7 @@ Version|Date|Comments
5.0|August 17, 2019|Converted to SPFx 1.9.1; Refreshed carousel code; Addresses #735, #909. Also added **Convert from UTC** option to handle feeds which do not provide time zone information. 5.0|August 17, 2019|Converted to SPFx 1.9.1; Refreshed carousel code; Addresses #735, #909. Also added **Convert from UTC** option to handle feeds which do not provide time zone information.
5.1|April 16, 2020|Converted to SPFx 1.10.0; Fixed issue with UTC mode when in narrow view. Updated resizing behavior and styles to match OOB calendar view. Added support for themes and theme variants. 5.1|April 16, 2020|Converted to SPFx 1.10.0; Fixed issue with UTC mode when in narrow view. Updated resizing behavior and styles to match OOB calendar view. Added support for themes and theme variants.
5.2|July 15, 2020|Fixed issue to support IE11 5.2|July 15, 2020|Fixed issue to support IE11
5.3|October 21, 2022|Upgrade to 1.15 Security fixes Removed IE11 Support
## Minimal Path to Awesome ## Minimal Path to Awesome
@ -85,7 +92,7 @@ Version|Date|Comments
- Specify a maximum number of events to retrieve - Specify a maximum number of events to retrieve
- If necessary, specify to use a proxy. Use this option if you encounter issues where your feed provider does not accept your tenant URL as a CORS origin. - If necessary, specify to use a proxy. Use this option if you encounter issues where your feed provider does not accept your tenant URL as a CORS origin.
- If desired, specify how long (in minutes) you want to expire your users' local storage and refresh the events. - If desired, specify how long (in minutes) you want to expire your users' local storage and refresh the events.
- Exclude IE11 support with gulp parameter ```--NoIE11``` this is in-case-sensitive
> This sample can also be opened with [VS Code Remote Development](https://code.visualstudio.com/docs/remote/remote-overview). Visit https://aka.ms/spfx-devcontainer for further instructions. > This sample can also be opened with [VS Code Remote Development](https://code.visualstudio.com/docs/remote/remote-overview). Visit https://aka.ms/spfx-devcontainer for further instructions.

View File

@ -9,7 +9,7 @@
"This web part uses event feeds from various sources and renders events using a look and feel that is consistent with the SharePoint out-of-the-box Group calendar/events web part." "This web part uses event feeds from various sources and renders events using a look and feel that is consistent with the SharePoint out-of-the-box Group calendar/events web part."
], ],
"creationDateTime": "2020-07-16", "creationDateTime": "2020-07-16",
"updateDateTime": "2020-07-16", "updateDateTime": "2022-10-22",
"products": [ "products": [
"SharePoint" "SharePoint"
], ],
@ -20,7 +20,7 @@
}, },
{ {
"key": "SPFX-VERSION", "key": "SPFX-VERSION",
"value": "1.10.0" "value": "1.15.2"
}, },
{ {
"key": "SPFX-SUPPORTSTHEMEVARIANTS", "key": "SPFX-SUPPORTSTHEMEVARIANTS",
@ -42,7 +42,7 @@
"authors": [ "authors": [
{ {
"gitHubAccount": "hugoabernier", "gitHubAccount": "hugoabernier",
"company": "Tahoe Ninjas", "company": "Microsoft",
"pictureUrl": "https://github.com/hugoabernier.png", "pictureUrl": "https://github.com/hugoabernier.png",
"name": "Hugo Bernier", "name": "Hugo Bernier",
"twitter": "bernierh" "twitter": "bernierh"

View File

@ -1,4 +0,0 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
"deployCdnPath": "temp/deploy"
}

View File

@ -1,7 +1,7 @@
{ {
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json", "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
"workingDir": "./temp/deploy/", "workingDir": "./release/assets/",
"account": "<!-- STORAGE ACCOUNT NAME -->", "account": "<!-- STORAGE ACCOUNT NAME -->",
"container": "react-calendar-feed", "container": "react-calendar-feed",
"accessKey": "<!-- ACCESS KEY -->" "accessKey": "<!-- ACCESS KEY -->"
} }

View File

@ -1,10 +1,37 @@
{ {
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json", "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": { "solution": {
"metadata": {
"shortDescription": {
"default": "react-calendar-feed description"
},
"longDescription": {
"default": "react-calendar-feed description"
},
"screenshotPaths": [],
"videoUrl": "",
"categories": []
},
"name": "react-calendar-feed-client-side-solution", "name": "react-calendar-feed-client-side-solution",
"id": "25653136-fc83-4abe-b9d2-a4ac041959d5", "id": "25653136-fc83-4abe-b9d2-a4ac041959d5",
"version": "5.2.0.0", "version": "5.3.0.0",
"includeClientSideAssets": true "includeClientSideAssets": true,
"developer": {
"name": "",
"privacyUrl": "",
"termsOfUseUrl": "",
"websiteUrl": "",
"mpnId": "Undefined-1.15.0"
},
"features": [
{
"title": "react-calendar-feed Feature",
"description": "The feature that activates elements of the react-calendar-feed solution.",
"id": "57a544de-10aa-4e29-91d9-fce0ac688566",
"version": "5.2.0.0"
}
]
}, },
"paths": { "paths": {
"zippedPackage": "solution/react-calendar-feed.sppkg" "zippedPackage": "solution/react-calendar-feed.sppkg"

View File

@ -2,9 +2,6 @@
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json", "$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
"port": 4321, "port": 4321,
"https": true, "https": true,
"initialPage": "https://localhost:5432/workbench", "initialPage": "https://enter-your-SharePoint-site/_layouts/workbench.aspx",
"api": {
"port": 5432,
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
}
} }

View File

@ -3,9 +3,20 @@
const gulp = require('gulp'); const gulp = require('gulp');
const build = require('@microsoft/sp-build-web'); const build = require('@microsoft/sp-build-web');
const path = require('path'); const path = require('path');
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`); //build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
let needIESupport = true; var getTasks = build.rig.getTasks;
build.rig.getTasks = function () {
var result = getTasks.call(build.rig);
result.set('serve', result.get('serve-deprecated'));
return result;
};
/*
Only here For documentation
let needIESupport = false;
if (!!process.argv && process.argv.length > 0) { if (!!process.argv && process.argv.length > 0) {
needIESupport = process.argv.findIndex(item => '--noie11' === item.toLowerCase()) === -1; needIESupport = process.argv.findIndex(item => '--noie11' === item.toLowerCase()) === -1;
} }
@ -32,10 +43,10 @@ if (needIESupport) {
additionalConfiguration: (generatedConfiguration) => { additionalConfiguration: (generatedConfiguration) => {
generatedConfiguration.module.rules.push({ generatedConfiguration.module.rules.push({
test: /\.js$/, test: /\.js$/,
/* //
This selector increase the webpack(gulp serve) time 5 times then without //This selector increase the webpack(gulp serve) time 5 times then without
exclude: [/node_modules\/(?!(rss-parser))/], // exclude: [/node_modules\/(?!(rss-parser))/],
*/ //
include: [ include: [
path.resolve(__dirname, "node_modules/rss-parser"), path.resolve(__dirname, "node_modules/rss-parser"),
], ],
@ -49,5 +60,5 @@ if (needIESupport) {
process.stdout.write(`No IE11 Support is set \n`); process.stdout.write(`No IE11 Support is set \n`);
} }
*/
build.initialize(gulp); build.initialize(gulp);

File diff suppressed because it is too large Load Diff

View File

@ -1,60 +1,50 @@
{ {
"name": "react-calendar-feed", "name": "react-calendar-feed",
"version": "5.2.0", "version": "5.3.0",
"private": true, "private": true,
"main": "lib/index.js", "main": "lib/index.js",
"engines": {
"node": ">=0.10.0"
},
"scripts": { "scripts": {
"build": "gulp bundle", "build": "gulp bundle",
"clean": "gulp clean", "clean": "gulp clean",
"test": "gulp test" "test": "gulp test"
}, },
"dependencies": { "dependencies": {
"@microsoft/sp-core-library": "1.10.0", "@microsoft/sp-core-library": "1.15.2",
"@microsoft/sp-lodash-subset": "1.10.0", "@microsoft/sp-lodash-subset": "1.15.2",
"@microsoft/sp-office-ui-fabric-core": "1.10.0", "@microsoft/sp-office-ui-fabric-core": "1.15.2",
"@microsoft/sp-property-pane": "1.10.0", "@microsoft/sp-property-pane": "1.15.2",
"@microsoft/sp-webpart-base": "1.10.0", "@microsoft/sp-webpart-base": "1.15.2",
"@pnp/common": "^1.2.8", "@pnp/common": "^1.2.8",
"@pnp/logging": "^1.2.8", "@pnp/logging": "^1.2.8",
"@pnp/odata": "^1.2.8", "@pnp/odata": "^1.2.8",
"@pnp/sp": "^1.2.8", "@pnp/sp": "^1.2.8",
"@pnp/spfx-controls-react": "^1.11.0", "@pnp/spfx-controls-react": "3.11.0",
"@pnp/spfx-property-controls": "^1.13.1", "@pnp/spfx-property-controls": "3.10.0",
"@types/es6-promise": "0.0.33",
"@types/react": "16.8.8",
"@types/react-dom": "16.8.3",
"@types/webpack-env": "1.13.1",
"feedparser": "^2.2.9", "feedparser": "^2.2.9",
"ical.js": "^1.3.0", "ical.js": "^1.3.0",
"ics-js": "^0.10.2", "ics-js": "^0.10.2",
"moment": "^2.29.2", "moment": "^2.29.2",
"office-ui-fabric-react": "6.189.2", "office-ui-fabric-react": "7.185.7",
"react": "16.8.5", "react": "16.13.1",
"react-dom": "16.8.5", "react-dom": "16.13.1",
"react-slick": "^0.23.2", "react-slick": "^0.23.2",
"rss-parser": "^3.6.2", "rss-parser": "^3.12.0",
"slick-carousel": "^1.8.1" "slick-carousel": "^1.8.1",
}, "tslib": "2.3.1"
"resolutions": {
"@types/react": "16.8.8"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.10.4", "@microsoft/eslint-config-spfx": "1.15.2",
"@babel/preset-env": "^7.10.4", "@microsoft/eslint-plugin-spfx": "1.15.2",
"@microsoft/rush-stack-compiler-2.9": "0.7.16", "@microsoft/rush-stack-compiler-4.5": "0.2.2",
"@microsoft/rush-stack-compiler-3.3": "0.3.5",
"@microsoft/sp-build-web": "1.15.2", "@microsoft/sp-build-web": "1.15.2",
"@microsoft/sp-module-interfaces": "1.10.0", "@microsoft/sp-module-interfaces": "1.15.2",
"@microsoft/sp-tslint-rules": "1.10.0", "@rushstack/eslint-config": "2.5.1",
"@microsoft/sp-webpart-workbench": "1.12.1", "@types/react": "16.9.51",
"@types/chai": "3.4.34", "@types/react-dom": "16.9.8",
"@types/mocha": "2.2.38", "@types/webpack-env": "~1.15.2",
"ajv": "~5.2.2", "ajv": "^6.12.5",
"babel-loader": "^8.1.0", "eslint-plugin-react-hooks": "4.3.0",
"gulp": "~3.9.1", "gulp": "4.0.2",
"webpack": "^4.43.0" "typescript": "4.5.5"
} }
} }

View File

@ -1,5 +1,3 @@
import { IReadonlyTheme } from '@microsoft/sp-component-base';
export interface IPaginationProps { export interface IPaginationProps {
currentPage: number; currentPage: number;
totalItems: number; totalItems: number;

View File

@ -1,4 +1,4 @@
import { ActionButton, IButtonProps } from "office-ui-fabric-react/lib/Button"; import { ActionButton } from "office-ui-fabric-react/lib/Button";
import { Icon } from "office-ui-fabric-react/lib/Icon"; import { Icon } from "office-ui-fabric-react/lib/Icon";
import { css } from "office-ui-fabric-react/lib/Utilities"; import { css } from "office-ui-fabric-react/lib/Utilities";
import * as React from "react"; import * as React from "react";
@ -44,7 +44,7 @@ export const Pagination = (props: IPaginationProps) => {
return ( return (
<div className={css(styles.Pagination, props.showPageNum ? null : styles.noPageNum)}> <div className={css(styles.Pagination, props.showPageNum ? null : styles.noPageNum)}>
<ActionButton className={css(styles.prev, prevDisabled && styles.nogo)} <ActionButton className={css(styles.prev, prevDisabled && styles.nogo)}
onRenderIcon={(_props: IButtonProps) => { onRenderIcon={() => {
// we use the render custom icon method to render the icon consistently with the right icon // we use the render custom icon method to render the icon consistently with the right icon
return ( return (
<Icon iconName="ChevronLeft" /> <Icon iconName="ChevronLeft" />
@ -60,7 +60,7 @@ export const Pagination = (props: IPaginationProps) => {
<ActionButton className={css(styles.next, nextDisabled && styles.nogo)} <ActionButton className={css(styles.next, nextDisabled && styles.nogo)}
data-automation-id="nextPage" data-automation-id="nextPage"
disabled={nextDisabled} disabled={nextDisabled}
onRenderMenuIcon={(_props: IButtonProps) => { onRenderMenuIcon={() => {
// we use the render custom menu icon method to render the icon to the right of the text // we use the render custom menu icon method to render the icon to the right of the text
return ( return (
<Icon iconName="ChevronRight" /> <Icon iconName="ChevronRight" />

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { css } from '@uifabric/utilities/lib/css'; import { css } from '@uifabric/utilities/lib/css';
import { IconButton } from 'office-ui-fabric-react/lib/Button'; import { IconButton } from 'office-ui-fabric-react/lib/Button';
import * as React from 'react'; import * as React from 'react';
@ -19,12 +20,12 @@ export const FilmstripLayout = (props: { children: any; clientWidth: number; the
SPComponentLoader.loadCss('https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.css'); SPComponentLoader.loadCss('https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.css');
SPComponentLoader.loadCss('https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick-theme.min.css'); SPComponentLoader.loadCss('https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick-theme.min.css');
let topElem: React.MutableRefObject<HTMLDivElement> = useRef<HTMLDivElement>(null); const topElem: React.MutableRefObject<HTMLDivElement> = useRef<HTMLDivElement>(null);
let _slider: React.MutableRefObject<Slider> = useRef<Slider>(null); const _slider: React.MutableRefObject<Slider> = useRef<Slider>(null);
const [isSmall, isMedium] = useBreakpoints(props.clientWidth, [696, 928]); const [isSmall, isMedium] = useBreakpoints(props.clientWidth, [696, 928]);
let numSlides: number = 3; let numSlides = 3;
if (isSmall) { if (isSmall) {
numSlides = 2; numSlides = 2;
} else if (isMedium) { } else if (isMedium) {
@ -33,8 +34,9 @@ export const FilmstripLayout = (props: { children: any; clientWidth: number; the
numSlides = 4; numSlides = 4;
} }
var isInfinite: boolean = React.Children.count(props.children) > numSlides; const isInfinite: boolean = React.Children.count(props.children) > numSlides;
var settings: any = { // eslint-disable-next-line @typescript-eslint/no-explicit-any
const settings: any = {
accessibility: true, accessibility: true,
arrows: false, arrows: false,
autoplaySpeed: 5000, autoplaySpeed: 5000,
@ -43,7 +45,7 @@ export const FilmstripLayout = (props: { children: any; clientWidth: number; the
return ( return (
<a> <a>
<div role="button" className={styles.carouselDotsContainer} aria-label={`Carousel Dot ${i}`} data-is-focusable={true} tabIndex={0}> <div role="button" className={styles.carouselDotsContainer} aria-label={`Carousel Dot ${i}`} data-is-focusable={true} tabIndex={0}>
<span className={styles.carouselDot} tabIndex={-1}></span> <span className={styles.carouselDot} tabIndex={-1} />
</div> </div>
</a> </a>
); );

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { HttpClient, HttpClientResponse } from "@microsoft/sp-http"; import { HttpClient, HttpClientResponse } from "@microsoft/sp-http";
import { IWebPartContext } from "@microsoft/sp-webpart-base"; import { IWebPartContext } from "@microsoft/sp-webpart-base";
import * as moment from "moment"; import * as moment from "moment";
@ -111,13 +112,8 @@ export abstract class BaseCalendarService implements ICalendarService {
* @param feedUrl The URL where to retrieve the events * @param feedUrl The URL where to retrieve the events
*/ */
protected async fetchResponseAsJson(feedUrl: string): Promise<any> { protected async fetchResponseAsJson(feedUrl: string): Promise<any> {
try {
const response = await this.fetchResponse(feedUrl); const response = await this.fetchResponse(feedUrl);
return await response.json(); return await response.json();
}
catch (error) {
throw error;
}
} }
/** /**

View File

@ -1,10 +1,13 @@
import { MockCalendarService } from "./MockCalendarService"; /* eslint-disable @typescript-eslint/no-explicit-any */
//import { MockCalendarService } from "./MockCalendarService";
import { RSSCalendarService } from "./RSSCalendarService"; import { RSSCalendarService } from "./RSSCalendarService";
import { WordPressFullCalendarService } from "./WordPressFullCalendarService"; import { WordPressFullCalendarService } from "./WordPressFullCalendarService";
import { iCalCalendarService } from "./iCalCalendarService"; import { iCalCalendarService } from "./iCalCalendarService";
import { ExchangePublicCalendarService } from "./ExchangePublicCalendarService"; import { ExchangePublicCalendarService } from "./ExchangePublicCalendarService";
import { SharePointCalendarService } from "./SharePointCalendarService"; import { SharePointCalendarService } from "./SharePointCalendarService";
// Localization // Localization
import * as strings from "CalendarServicesStrings"; import * as strings from "CalendarServicesStrings";
@ -48,13 +51,13 @@ export class CalendarServiceProviderList {
]; ];
// only include the Mock service provider in DEBUG // only include the Mock service provider in DEBUG
if (DEBUG) { /*
providers.push({ providers.push({
label: strings.MockProviderName, label: strings.MockProviderName,
key: CalendarServiceProviderType.Mock, key: CalendarServiceProviderType.Mock,
initialize: () => new MockCalendarService() initialize: () => new MockCalendarService()
}); });
} */
return providers; return providers;
} }

View File

@ -5,7 +5,7 @@ import { ICalendarService } from "..";
import { ICalendarEvent } from "../ICalendarEvent"; import { ICalendarEvent } from "../ICalendarEvent";
import { iCalCalendarService } from "../iCalCalendarService"; import { iCalCalendarService } from "../iCalCalendarService";
// tslint:disable-next-line:class-name
export class ExchangePublicCalendarService extends iCalCalendarService implements ICalendarService { export class ExchangePublicCalendarService extends iCalCalendarService implements ICalendarService {
constructor() { constructor() {
super(); super();
@ -17,7 +17,7 @@ export class ExchangePublicCalendarService extends iCalCalendarService implement
// we allow users to pass either the .html URL or // we allow users to pass either the .html URL or
// the .ics, but we really need the .ics file // the .ics, but we really need the .ics file
const htmlExtension:string = ".html"; const htmlExtension = ".html";
if (this.FeedUrl.indexOf(htmlExtension, this.FeedUrl.length - htmlExtension.length) >= 0) { if (this.FeedUrl.indexOf(htmlExtension, this.FeedUrl.length - htmlExtension.length) >= 0) {
// the url ends with .html. Replace it with .ics // the url ends with .html. Replace it with .ics
const root: string = this.FeedUrl.substring(0, this.FeedUrl.length - htmlExtension.length); const root: string = this.FeedUrl.substring(0, this.FeedUrl.length - htmlExtension.length);

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/** /**
* MockExtensionService * MockExtensionService
* This provider will NOT be listed in the list of available providers when this solution is packaged with --ship. * This provider will NOT be listed in the list of available providers when this solution is packaged with --ship.

View File

@ -9,7 +9,7 @@
import { ICalendarService } from ".."; import { ICalendarService } from "..";
import { BaseCalendarService } from "../BaseCalendarService"; import { BaseCalendarService } from "../BaseCalendarService";
import { ICalendarEvent } from "../ICalendarEvent"; import { ICalendarEvent } from "../ICalendarEvent";
import RSSParser from 'rss-parser'; import * as RSSParser from 'rss-parser';
export class RSSCalendarService extends BaseCalendarService implements ICalendarService { export class RSSCalendarService extends BaseCalendarService implements ICalendarService {
constructor() { constructor() {
@ -20,11 +20,11 @@ export class RSSCalendarService extends BaseCalendarService implements ICalendar
public getEvents = (): Promise<ICalendarEvent[]> => { public getEvents = (): Promise<ICalendarEvent[]> => {
const parameterizedFeedUrl: string = this.getCORSUrl(this.replaceTokens(this.FeedUrl, this.EventRange)); const parameterizedFeedUrl: string = this.getCORSUrl(this.replaceTokens(this.FeedUrl, this.EventRange));
let parser = new RSSParser(); const parser = new RSSParser();
return parser.parseURL(parameterizedFeedUrl).then(feed => { return parser.parseURL(parameterizedFeedUrl).then(feed => {
let events: ICalendarEvent[] = feed.items.map(item => { const events: ICalendarEvent[] = feed.items.map(item => {
let pubDate: Date = this.convertToDate(item.isoDate); const pubDate: Date = this.convertToDate(item.isoDate);
const eventItem: ICalendarEvent = { const eventItem: ICalendarEvent = {
title: item.title, title: item.title,
start: pubDate, start: pubDate,

View File

@ -1,7 +1,8 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/** /**
* ExtensionService * ExtensionService
*/ */
import { HttpClientResponse } from "@microsoft/sp-http";
import { ICalendarService } from ".."; import { ICalendarService } from "..";
import { BaseCalendarService } from "../BaseCalendarService"; import { BaseCalendarService } from "../BaseCalendarService";
import { ICalendarEvent } from "../ICalendarEvent"; import { ICalendarEvent } from "../ICalendarEvent";
@ -22,28 +23,28 @@ export class SharePointCalendarService extends BaseCalendarService
); );
// Get the URL // Get the URL
let webUrl = parameterizedFeedUrl.toLowerCase(); const webUrl = parameterizedFeedUrl.toLowerCase();
// Break the URL into parts // Break the URL into parts
let urlParts = webUrl.split("/"); const urlParts = webUrl.split("/");
// Get the web root // Get the web root
let webRoot = urlParts[0] + "/" + urlParts[1] + "/" + urlParts[2]; const webRoot = urlParts[0] + "/" + urlParts[1] + "/" + urlParts[2];
// Get the list URL // Get the list URL
let listUrl = webUrl.substring(webRoot.length); const listUrl = webUrl.substring(webRoot.length);
// Find the "lists" portion of the URL to get the site URL // Find the "lists" portion of the URL to get the site URL
let webLocation = listUrl.substr(0, listUrl.indexOf("lists/")); const webLocation = listUrl.substr(0, listUrl.indexOf("lists/"));
let siteUrl = webRoot + webLocation; const siteUrl = webRoot + webLocation;
// Open the web associated to the site // Open the web associated to the site
let web = new Web(siteUrl); const web = new Web(siteUrl);
// Get the web // Get the web
await web.get(); await web.get();
// Build a filter so that we don't retrieve every single thing unless necesssary // Build a filter so that we don't retrieve every single thing unless necesssary
let dateFilter: string = "EventDate ge datetime'" + this.EventRange.Start.toISOString() + "' and EndDate lt datetime'" + this.EventRange.End.toISOString() + "'"; const dateFilter: string = "EventDate ge datetime'" + this.EventRange.Start.toISOString() + "' and EndDate lt datetime'" + this.EventRange.End.toISOString() + "'";
try { try {
const items = await web.getList(listUrl) const items = await web.getList(listUrl)
.items.select("Id,Title,Description,EventDate,EndDate,fAllDayEvent,Category,Location") .items.select("Id,Title,Description,EventDate,EndDate,fAllDayEvent,Category,Location")
@ -51,8 +52,8 @@ export class SharePointCalendarService extends BaseCalendarService
.filter(dateFilter) .filter(dateFilter)
.get(); .get();
// Once we get the list, convert to calendar events // Once we get the list, convert to calendar events
let events: ICalendarEvent[] = items.map((item: any) => { const events: ICalendarEvent[] = items.map((item: any) => {
let eventUrl: string = combine(webUrl, "DispForm.aspx?ID=" + item.Id); const eventUrl: string = combine(webUrl, "DispForm.aspx?ID=" + item.Id);
const eventItem: ICalendarEvent = { const eventItem: ICalendarEvent = {
title: item.Title, title: item.Title,
start: item.EventDate, start: item.EventDate,

View File

@ -17,7 +17,7 @@ export class WordPressFullCalendarService extends BaseCalendarService implements
try { try {
const data = await this.fetchResponseAsJson(parameterizedFeedUrl); const data = await this.fetchResponseAsJson(parameterizedFeedUrl);
let events: ICalendarEvent[] = data.map((e: IWordPressFullCalendarEventResponse) => { const events: ICalendarEvent[] = data.map((e: IWordPressFullCalendarEventResponse) => {
const startDate: Date = this.convertToDate(e.start); const startDate: Date = this.convertToDate(e.start);
const endDate: Date = this.convertToDate(e.end); const endDate: Date = this.convertToDate(e.end);
const eventItem: ICalendarEvent = { const eventItem: ICalendarEvent = {

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/** /**
* ExtensionService * ExtensionService
*/ */
@ -22,10 +23,10 @@ export class iCalCalendarService extends BaseCalendarService implements ICalenda
const jsonified: any = ICAL.parse(data); const jsonified: any = ICAL.parse(data);
const comp: any = new ICAL.Component(jsonified); const comp: any = new ICAL.Component(jsonified);
const veventList: any[] = comp.getAllSubcomponents("vevent"); const veventList: any[] = comp.getAllSubcomponents("vevent");
let events: ICalendarEvent[] = veventList.map((vevent: any) => { const events: ICalendarEvent[] = veventList.map((vevent: any) => {
const event: ICAL.Event = new ICAL.Event(vevent); const event: ICAL.Event = new ICAL.Event(vevent);
let startDate = this.convertToDate(event.startDate); const startDate = this.convertToDate(event.startDate);
let endDate = this.convertToDate(event.endDate); const endDate = this.convertToDate(event.endDate);
const eventItem: ICalendarEvent = { const eventItem: ICalendarEvent = {
title: event.summary, title: event.summary,

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import * as React from "react"; import * as React from "react";
import * as ReactDom from "react-dom"; import * as ReactDom from "react-dom";
@ -35,7 +36,7 @@ import CalendarFeedSummary from "./components/CalendarFeedSummary";
import { ICalendarFeedSummaryProps } from "./components/CalendarFeedSummary.types"; import { ICalendarFeedSummaryProps } from "./components/CalendarFeedSummary.types";
// Support for theme variants // Support for theme variants
import { ThemeProvider, ThemeChangedEventArgs, IReadonlyTheme, ISemanticColors } from '@microsoft/sp-component-base'; import { ThemeProvider, ThemeChangedEventArgs, IReadonlyTheme } from '@microsoft/sp-component-base';
/** /**
* Calendar Feed Summary Web Part * Calendar Feed Summary Web Part
@ -56,7 +57,7 @@ export default class CalendarFeedSummaryWebPart extends BaseClientSideWebPart<IC
} }
protected onInit(): Promise<void> { protected onInit(): Promise<void> {
return new Promise<void>((resolve, _reject) => { return new Promise<void>((resolve) => {
// Consume the new ThemeProvider service // Consume the new ThemeProvider service
this._themeProvider = this.context.serviceScope.consume(ThemeProvider.serviceKey); this._themeProvider = this.context.serviceScope.consume(ThemeProvider.serviceKey);
@ -270,6 +271,7 @@ export default class CalendarFeedSummaryWebPart extends BaseClientSideWebPart<IC
/** /**
* If we get resized, call the Render method so that we can switch between the narrow view and the regular view * If we get resized, call the Render method so that we can switch between the narrow view and the regular view
*/ */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
protected onAfterResize(newWidth: number): void { protected onAfterResize(newWidth: number): void {
// redraw the web part // redraw the web part
this.render(); this.render();
@ -324,7 +326,7 @@ export default class CalendarFeedSummaryWebPart extends BaseClientSideWebPart<IC
return strings.FeedUrlValidationNoUrl; return strings.FeedUrlValidationNoUrl;
} }
if (!feedUrl.match(/(http|https):\/\/(\w+:{0,1}\w*)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%!\-\/]))?/)) { if (!feedUrl.match(/(http|https):\/\/(\w+:{0,1}\w*)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%!\-/]))?/)) {
return strings.FeedUrlValidationInvalidFormat; return strings.FeedUrlValidationInvalidFormat;
} }
@ -345,7 +347,7 @@ export default class CalendarFeedSummaryWebPart extends BaseClientSideWebPart<IC
} = this.properties; } = this.properties;
// get the first provider matching the type selected // get the first provider matching the type selected
let providerItem: any = this._providerList.filter(p => p.key === this.properties.feedType)[0]; const providerItem: any = this._providerList.filter(p => p.key === this.properties.feedType)[0];
// make sure we got a valid provider // make sure we got a valid provider
if (!providerItem) { if (!providerItem) {
@ -354,7 +356,7 @@ export default class CalendarFeedSummaryWebPart extends BaseClientSideWebPart<IC
} }
// get an instance // get an instance
let provider: ICalendarService = providerItem.initialize(); const provider: ICalendarService = providerItem.initialize();
// pass props // pass props
provider.Context = this.context; provider.Context = this.context;

View File

@ -14,10 +14,10 @@ import { FilmstripLayout } from "../../../shared/components/filmstripLayout/inde
import { IReadonlyTheme } from '@microsoft/sp-component-base'; import { IReadonlyTheme } from '@microsoft/sp-component-base';
// the key used when caching events // the key used when caching events
const CacheKey: string = "calendarFeedSummary"; const CacheKey = "calendarFeedSummary";
// this is the same width that the SharePoint events web parts use to render as narrow // this is the same width that the SharePoint events web parts use to render as narrow
const MaxMobileWidth: number = 480; const MaxMobileWidth = 480;
/** /**
* Displays a feed summary from a given calendar feed provider. Renders a different view for mobile/narrow web parts. * Displays a feed summary from a given calendar feed provider. Renders a different view for mobile/narrow web parts.
@ -47,7 +47,7 @@ export default class CalendarFeedSummary extends React.Component<ICalendarFeedSu
* @param prevProps The previous props before changes are applied * @param prevProps The previous props before changes are applied
* @param prevState The previous state before changes are applied * @param prevState The previous state before changes are applied
*/ */
public componentDidUpdate(prevProps: ICalendarFeedSummaryProps, prevState: ICalendarFeedSummaryState): void { public componentDidUpdate(prevProps: ICalendarFeedSummaryProps): void {
// only reload if the provider info has changed // only reload if the provider info has changed
const prevProvider: ICalendarService = prevProps.provider; const prevProvider: ICalendarService = prevProps.provider;
const currProvider: ICalendarService = this.props.provider; const currProvider: ICalendarService = this.props.provider;
@ -240,7 +240,7 @@ export default class CalendarFeedSummary extends React.Component<ICalendarFeedSu
const isEditMode: boolean = this.props.displayMode === DisplayMode.Edit; const isEditMode: boolean = this.props.displayMode === DisplayMode.Edit;
let pagedEvents: ICalendarEvent[] = events; let pagedEvents: ICalendarEvent[] = events;
let usePaging: boolean = false; let usePaging = false;
if (maxEvents > 0 && events.length > maxEvents) { if (maxEvents > 0 && events.length > maxEvents) {
// calculate the page size // calculate the page size
@ -261,6 +261,7 @@ export default class CalendarFeedSummary extends React.Component<ICalendarFeedSu
items={pagedEvents} items={pagedEvents}
onRenderCell={(item, _index) => ( onRenderCell={(item, _index) => (
<EventCard <EventCard
key={'eventCard'+_index}
isEditMode={isEditMode} isEditMode={isEditMode}
event={item} event={item}
isNarrow={true} isNarrow={true}
@ -330,7 +331,7 @@ export default class CalendarFeedSummary extends React.Component<ICalendarFeedSu
// load from cache if: 1) we said to use cache, and b) if we have something in cache // load from cache if: 1) we said to use cache, and b) if we have something in cache
if (useCacheIfPossible && localStorage.getItem(CacheKey)) { if (useCacheIfPossible && localStorage.getItem(CacheKey)) {
let feedCache: IFeedCache = JSON.parse(localStorage.getItem(CacheKey)); const feedCache: IFeedCache = JSON.parse(localStorage.getItem(CacheKey));
const { Name, FeedUrl } = this.props.provider; const { Name, FeedUrl } = this.props.provider;
const cacheStillValid: boolean = moment().isBefore(feedCache.expiry); const cacheStillValid: boolean = moment().isBefore(feedCache.expiry);

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/** /**
* CalendarFeedSummary Types * CalendarFeedSummary Types
* Contains the various types used by the component. * Contains the various types used by the component.
@ -6,7 +7,7 @@
* I kinda liked it. * I kinda liked it.
*/ */
import { DisplayMode } from "@microsoft/sp-core-library"; import { DisplayMode } from "@microsoft/sp-core-library";
import { IWebPartContext } from "@microsoft/sp-webpart-base"; import { WebPartContext } from "@microsoft/sp-webpart-base";
import { Moment } from "moment"; import { Moment } from "moment";
import { ICalendarEvent, ICalendarService } from "../../../shared/services/CalendarService"; import { ICalendarEvent, ICalendarService } from "../../../shared/services/CalendarService";
import { IReadonlyTheme } from '@microsoft/sp-component-base'; import { IReadonlyTheme } from '@microsoft/sp-component-base';
@ -17,7 +18,7 @@ import { IReadonlyTheme } from '@microsoft/sp-component-base';
export interface ICalendarFeedSummaryProps { export interface ICalendarFeedSummaryProps {
title: string; title: string;
displayMode: DisplayMode; displayMode: DisplayMode;
context: IWebPartContext; context: WebPartContext;
updateProperty: (value: string) => void; updateProperty: (value: string) => void;
isConfigured: boolean; isConfigured: boolean;
provider: ICalendarService; provider: ICalendarService;

View File

@ -1,5 +1,5 @@
{ {
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.3/includes/tsconfig-web.json", "extends": "./node_modules/@microsoft/rush-stack-compiler-4.5/includes/tsconfig-web.json",
"compilerOptions": { "compilerOptions": {
"inlineSources": false, "inlineSources": false,
"strictNullChecks": false, "strictNullChecks": false,
@ -19,21 +19,17 @@
"./node_modules/@microsoft" "./node_modules/@microsoft"
], ],
"types": [ "types": [
"es6-promise", "node"
"webpack-env"
], ],
"lib": [ "lib": [
"es5", "es5",
"dom", "dom",
"es2015.collection" "es2015.collection",
"es2015.promise"
] ]
}, },
"include": [ "include": [
"src/**/*.ts" "src/**/*.ts",
"src/**/*.tsx"
], ],
"exclude": [
"node_modules",
"lib"
],
"extends": "./node_modules/@microsoft/rush-stack-compiler-2.9/includes/tsconfig-web.json"
} }

View File

@ -1,30 +0,0 @@
{
"extends": "@microsoft/sp-tslint-rules/base-tslint.json",
"rules": {
"class-name": false,
"export-name": false,
"forin": false,
"label-position": false,
"member-access": true,
"no-arg": false,
"no-console": false,
"no-construct": false,
"no-duplicate-variable": true,
"no-eval": false,
"no-function-expression": true,
"no-internal-module": true,
"no-shadowed-variable": true,
"no-switch-case-fall-through": true,
"no-unnecessary-semicolons": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-with-statement": true,
"semicolon": true,
"trailing-comma": false,
"typedef": false,
"typedef-whitespace": false,
"use-named-parameter": true,
"variable-name": false,
"whitespace": false
}
}