Skip to content

Commit 49c7ee8

Browse files
committed
second commit - fix preview; add read more link feature; organize code
1 parent d24e474 commit 49c7ee8

23 files changed

+1276
-269
lines changed

LICENSE

Lines changed: 674 additions & 0 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 49 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,39 @@
11
# sharepoint-wordpress-rss-feed
22

3-
## Summary
3+
This SharePoint Framework (SPFx) Web Part allows you to fetch and display posts from a WordPress site's RSS feed using the WordPress REST API. Users can filter posts by tags, categories, post title patterns, and date ranges. This component is ideal for SharePoint environments that need to embed dynamic blog content from a WordPress site.
44

5-
Short summary on functionality and used technologies.
5+
## ✨ Features
66

7-
[picture of the solution in action, if possible]
7+
- 🔗 Connect to any WordPress site URL
8+
- 🏷️ Filter posts by tags and categories
9+
- 📆 Limit posts by date range (e.g., last 7 days)
10+
- 🔍 Filter post titles with regular expressions (wildcard matching)
11+
- ⚖️ Combine filters using `AND` / `OR` logic
12+
- 🔄 Refresh feed and clear feed settings with one click
13+
- 📃 Fully integrated with Fluent UI for a responsive SharePoint look and feel
14+
15+
16+
## 🛠️ Usage
17+
Once the Web Part is added to a page:
18+
19+
1. Enter your WordPress site URL (e.g. https://example.com)
20+
2. Fetch site information (will fail if feed is not available). Once site information is fetched you can:
21+
3. Set limits: # posts, # days back
22+
4. Select desired tags or categories to filter by. **These are multiselect dropdowns populated from your site's available tags and categories.**
23+
5. Optionally enter a regular expression to filter post titles.
24+
6. Choose AND or OR to define how tag/category filters combine.
25+
7. Choose your layout: List | Grid -- **Note here that images will only display if the parent post has associated media**
26+
8. Click Update Feed.
27+
9. Use Clear Filters to reset everything.
28+
29+
## 📸 Demo Screenshots
30+
![Enter site URL](./doc/img/enter-site-url.png)
31+
32+
![Site connected successfully - all settings](./doc/img/site-connected.png)
33+
34+
![List preview](./doc/img/list-preview.png)
35+
36+
![Grid preview](./doc/img/grid-preview.png)
837

938
## Used SharePoint Framework Version
1039

@@ -19,55 +48,40 @@ Short summary on functionality and used technologies.
1948
2049
## Prerequisites
2150

22-
> Any special pre-requisites?
51+
### For using:
52+
53+
> Have a Wordpress site in mind that allows access to the /wp-json path for RSS feed consumption
54+
> Have a SharePoint Online site
55+
56+
### For developing:
57+
58+
- [Node.js (LTS version)](https://nodejs.org/)
59+
- [Yeoman and SPFx Generator](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/set-up-your-development-environment)
60+
- SharePoint Online Developer site (or compatible local workbench)
2361

2462
## Solution
2563

2664
| Solution | Author(s) |
2765
| ----------- | ------------------------------------------------------- |
28-
| folder name | Author details (name, company, twitter alias with link) |
66+
| sharepoint-wordpress-rss-feed | Austin Hunt, austinjhunt.com |
2967

3068
## Version history
3169

3270
| Version | Date | Comments |
33-
| ------- | ---------------- | --------------- |
34-
| 1.1 | March 10, 2021 | Update comment |
35-
| 1.0 | January 29, 2021 | Initial release |
71+
| ------- | ---------------- | --------------- |
72+
| 1.0 | April 03, 2025 | Initial release |
3673

3774
## Disclaimer
3875

3976
**THIS CODE IS PROVIDED _AS IS_ WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
4077

4178
---
42-
43-
## Minimal Path to Awesome
44-
79+
80+
## Further Development
4581
- Clone this repository
4682
- Ensure that you are at the solution folder
4783
- in the command-line run:
4884
- **npm install**
49-
- **gulp serve**
50-
51-
> Include any additional steps as needed.
52-
53-
## Features
54-
55-
Description of the extension that expands upon high-level summary above.
56-
57-
This extension illustrates the following concepts:
58-
59-
- topic 1
60-
- topic 2
61-
- topic 3
62-
63-
> Notice that better pictures and documentation will increase the sample usage and the value you are providing for others. Thanks for your submissions advance.
64-
65-
> Share your web part with others through Microsoft 365 Patterns and Practices program to get visibility and exposure. More details on the community, open-source projects and other activities from http://aka.ms/m365pnp.
66-
67-
## References
85+
- **gulp serve**
6886

69-
- [Getting started with SharePoint Framework](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/set-up-your-developer-tenant)
70-
- [Building for Microsoft teams](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/build-for-teams-overview)
71-
- [Use Microsoft Graph in your solution](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/using-microsoft-graph-apis)
72-
- [Publish SharePoint Framework applications to the Marketplace](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/publish-to-marketplace-overview)
73-
- [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - Guidance, tooling, samples and open-source controls for your Microsoft 365 development
87+

doc/img/enter-site-url.png

33.5 KB
Loading

doc/img/grid-preview.png

695 KB
Loading

doc/img/list-preview.png

287 KB
Loading

doc/img/site-connected.png

129 KB
Loading
Lines changed: 58 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,76 @@
1-
import * as React from 'react';
2-
import * as ReactDom from 'react-dom';
3-
import { Version } from '@microsoft/sp-core-library';
4-
import {
5-
type IPropertyPaneConfiguration,
6-
PropertyPaneTextField
7-
} from '@microsoft/sp-property-pane';
8-
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
9-
import { IReadonlyTheme } from '@microsoft/sp-component-base';
10-
import WordPressRssFeed from './components/WordPressRssFeed';
1+
import * as React from "react";
2+
import * as ReactDom from "react-dom";
3+
import { Version } from "@microsoft/sp-core-library";
114

12-
export interface IWordPressRssFeedWebPartProps {
13-
siteUrl: string;
14-
}
5+
import { type IPropertyPaneConfiguration, PropertyPaneTextField } from "@microsoft/sp-property-pane";
6+
import { BaseClientSideWebPart } from "@microsoft/sp-webpart-base";
7+
import { IReadonlyTheme } from "@microsoft/sp-component-base";
8+
import WordPressRssFeed from "./components/WordPressRssFeed";
9+
import { IWordPressRssFeedWebPartProps } from "./interfaces";
10+
import { DEFAULTS, defaultSettings } from "./defaults";
1511

1612
export default class WordPressRssFeedWebPart extends BaseClientSideWebPart<IWordPressRssFeedWebPartProps> {
17-
18-
1913
public render(): void {
20-
const element: React.ReactElement = React.createElement(
21-
WordPressRssFeed
22-
);
23-
14+
const element: React.ReactElement<IWordPressRssFeedWebPartProps> = React.createElement(WordPressRssFeed, {
15+
displayMode: this.displayMode,
16+
title: this.properties.title,
17+
description: this.properties.description,
18+
readMoreLink: this.properties.readMoreLink,
19+
siteInfo: this.properties.siteInfo,
20+
feedSettings: this.properties.feedSettings,
21+
url: this.properties.url,
22+
// provide a way to save values to database
23+
updateProperty: (key: string, value: any) => {
24+
this.properties[key] = value;
25+
},
26+
});
27+
2428
ReactDom.render(element, this.domElement);
2529
}
2630

27-
// protected onInit(): Promise<void> {
28-
// return this._getEnvironmentMessage().then(message => {
29-
// this._environmentMessage = message;
30-
// });
31-
// }
32-
33-
34-
35-
// private _getEnvironmentMessage(): Promise<string> {
36-
// if (!!this.context.sdks.microsoftTeams) { // running in Teams, office.com or Outlook
37-
// return this.context.sdks.microsoftTeams.teamsJs.app.getContext()
38-
// .then(context => {
39-
// let environmentMessage: string = '';
40-
// switch (context.app.host.name) {
41-
// case 'Office': // running in Office
42-
// environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentOffice : strings.AppOfficeEnvironment;
43-
// break;
44-
// case 'Outlook': // running in Outlook
45-
// environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentOutlook : strings.AppOutlookEnvironment;
46-
// break;
47-
// case 'Teams': // running in Teams
48-
// case 'TeamsModern':
49-
// environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentTeams : strings.AppTeamsTabEnvironment;
50-
// break;
51-
// default:
52-
// environmentMessage = strings.UnknownEnvironment;
53-
// }
54-
55-
// return environmentMessage;
56-
// });
57-
// }
31+
protected onInit(): Promise<void> {
32+
// initialize values to defaults if database instance has nothing stored
33+
if (!this.properties.siteInfo) {
34+
this.properties.siteInfo = DEFAULTS.siteInfo;
35+
}
36+
if (!this.properties.url) {
37+
this.properties.url = DEFAULTS.siteUrl;
38+
}
39+
if (!this.properties.feedSettings) {
40+
this.properties.feedSettings = defaultSettings;
41+
}
42+
if (!this.properties.title) {
43+
this.properties.title = DEFAULTS.title;
44+
}
45+
if (!this.properties.description) {
46+
this.properties.description = DEFAULTS.description;
47+
}
48+
if (!this.properties.readMoreLink) {
49+
this.properties.readMoreLink = DEFAULTS.readMoreLink;
50+
}
5851

59-
// return Promise.resolve(this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentSharePoint : strings.AppSharePointEnvironment);
60-
// }
52+
return super.onInit();
53+
}
6154

6255
protected onThemeChanged(currentTheme: IReadonlyTheme | undefined): void {
6356
if (!currentTheme) {
6457
return;
65-
}
66-
const {
67-
semanticColors
68-
} = currentTheme;
58+
}
59+
const { semanticColors } = currentTheme;
6960

7061
if (semanticColors) {
71-
this.domElement.style.setProperty('--bodyText', semanticColors.bodyText || null);
72-
this.domElement.style.setProperty('--link', semanticColors.link || null);
73-
this.domElement.style.setProperty('--linkHovered', semanticColors.linkHovered || null);
62+
this.domElement.style.setProperty("--bodyText", semanticColors.bodyText || null);
63+
this.domElement.style.setProperty("--link", semanticColors.link || null);
64+
this.domElement.style.setProperty("--linkHovered", semanticColors.linkHovered || null);
7465
}
75-
7666
}
7767

7868
protected onDispose(): void {
7969
ReactDom.unmountComponentAtNode(this.domElement);
8070
}
8171

8272
protected get dataVersion(): Version {
83-
return Version.parse('1.0');
73+
return Version.parse("1.0");
8474
}
8575

8676
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
@@ -93,14 +83,13 @@ export default class WordPressRssFeedWebPart extends BaseClientSideWebPart<IWord
9383
groupName: "Settings",
9484
groupFields: [
9585
PropertyPaneTextField("siteUrl", {
96-
label: "WordPress Site URL"
97-
})
98-
]
99-
}
100-
]
101-
}
102-
]
86+
label: "WordPress Site URL",
87+
}),
88+
],
89+
},
90+
],
91+
},
92+
],
10393
};
10494
}
105-
10695
}

src/webparts/wordPressRssFeed/components/Alert.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@ interface AlertProps {
66
type: "success" | "warning" | "error";
77
}
88
const MESSAGES = {
9+
ERROR: {
10+
failedSaveSettings: "Failed to save settings. Please try modifying your settings and resaving.",
11+
},
912
SUCCESS: {
1013
connectedToSite: "Connected to site",
14+
savedSettings: "Settings saved successfully.",
1115
},
1216
WARNING: {
1317
noPostsToDisplay: "There are no posts to display currently",

0 commit comments

Comments
 (0)