Js public unjoined teams (#779)
* Added a web part that lists all the public teams the current user is not a member of yet. * Update README.md
This commit is contained in:
parent
3847c3744b
commit
4e4c3ea22f
|
@ -0,0 +1,25 @@
|
|||
# EditorConfig helps developers define and maintain consistent
|
||||
# coding styles between different editors and IDEs
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
|
||||
[*]
|
||||
|
||||
# change these settings to your own preference
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# we recommend you to keep these unchanged
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[{package,bower}.json]
|
||||
indent_style = space
|
||||
indent_size = 2
|
|
@ -0,0 +1,32 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Dependency directories
|
||||
node_modules
|
||||
|
||||
# Build generated files
|
||||
dist
|
||||
lib
|
||||
solution
|
||||
temp
|
||||
*.sppkg
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# OSX
|
||||
.DS_Store
|
||||
|
||||
# Visual Studio files
|
||||
.ntvs_analysis.dat
|
||||
.vs
|
||||
bin
|
||||
obj
|
||||
|
||||
# Resx Generated Code
|
||||
*.resx.ts
|
||||
|
||||
# Styles Generated Code
|
||||
*.scss.ts
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"@microsoft/generator-sharepoint": {
|
||||
"plusBeta": true,
|
||||
"isCreatingSolution": true,
|
||||
"environment": "spo",
|
||||
"version": "1.7.1",
|
||||
"libraryName": "js-public-unjoined-teams",
|
||||
"libraryId": "f04aba32-b056-47c4-811b-6f17d94cc8d9",
|
||||
"packageManager": "npm",
|
||||
"isDomainIsolated": false,
|
||||
"componentType": "webpart"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
# Public teams I'm not a member of
|
||||
|
||||
## Summary
|
||||
This web part lists all the public teams the current user is not yet a member of. They can then join any of those teams by clicking the button right next to the team name. This web part can also be added to Teams as a tab (built with the 1.7.1 plusbeta/preview version).
|
||||
|
||||
![picture of the web part in action](./assets/js-public-unjoined-teams.gif)
|
||||
|
||||
## Used SharePoint Framework Version
|
||||
![drop](https://img.shields.io/badge/version-1.7.1-green.svg)
|
||||
|
||||
## Applies to
|
||||
|
||||
* [SharePoint Framework](https:/dev.office.com/sharepoint)
|
||||
* [Office 365 tenant](https://dev.office.com/sharepoint/docs/spfx/set-up-your-development-environment)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
To be able to pin this web part as a Teams tab, your tenant currently needs to be in the targeted release mode. If your tenant is not in targeted release, you can still use the web part in SharePoint as usual.
|
||||
|
||||
## Solution
|
||||
|
||||
Solution|Author(s)
|
||||
--------|---------
|
||||
js-public-unjoined-teams | Laura Kokkarinen ([laurakokkarinen.com](https://laurakokkarinen.com), [@LauraKokkarinen](https://twitter.com/LauraKokkarinen))
|
||||
|
||||
## Version history
|
||||
|
||||
Version|Date|Comments
|
||||
-------|----|--------
|
||||
1.0|February 5, 2019|Initial release
|
||||
|
||||
## Disclaimer
|
||||
**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.**
|
||||
|
||||
---
|
||||
|
||||
## Minimal Path to Awesome
|
||||
|
||||
- Clone this repository
|
||||
- in the command line run:
|
||||
- `npm install`
|
||||
- `gulp bundle --ship`
|
||||
- `gulp package solution --ship`
|
||||
- Deploy the solution package under \sharepoint\solution to the SharePoint app catalog
|
||||
- Approve the required Microsoft Graph permissions in the SharePoint admin center (Preview, API management)
|
||||
|
||||
## Features
|
||||
|
||||
This web part lists all the public teams the current user is not yet a member of. They can then join any of those teams by clicking the button right next to the team name. The Teams client does not present this kind of a complete list by default, so the web part is particularly handy to all end users who might not even be aware of all the public teams that are available. This web part can also be added to Teams as a tab. The web part colors are based on the site color theme.
|
||||
|
||||
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/js-public-unjoined-teams" />
|
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||
"version": "2.0",
|
||||
"bundles": {
|
||||
"public-unjoined-teams-web-part": {
|
||||
"components": [
|
||||
{
|
||||
"entrypoint": "./lib/webparts/publicUnjoinedTeams/PublicUnjoinedTeamsWebPart.js",
|
||||
"manifest": "./src/webparts/publicUnjoinedTeams/PublicUnjoinedTeamsWebPart.manifest.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"externals": {},
|
||||
"localizedResources": {
|
||||
"PublicUnjoinedTeamsWebPartStrings": "lib/webparts/publicUnjoinedTeams/loc/{locale}.js"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
|
||||
"deployCdnPath": "temp/deploy"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
|
||||
"workingDir": "./temp/deploy/",
|
||||
"account": "<!-- STORAGE ACCOUNT NAME -->",
|
||||
"container": "js-public-unjoined-teams",
|
||||
"accessKey": "<!-- ACCESS KEY -->"
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
|
||||
"solution": {
|
||||
"name": "js-public-unjoined-teams-client-side-solution",
|
||||
"id": "f04aba32-b056-47c4-811b-6f17d94cc8d9",
|
||||
"version": "1.0.0.0",
|
||||
"includeClientSideAssets": true,
|
||||
"skipFeatureDeployment": true,
|
||||
"isDomainIsolated": false,
|
||||
"webApiPermissionRequests": [
|
||||
{
|
||||
"resource": "Microsoft Graph",
|
||||
"scope": "User.Read.All"
|
||||
},
|
||||
{
|
||||
"resource": "Microsoft Graph",
|
||||
"scope": "Directory.Read.All"
|
||||
},
|
||||
{
|
||||
"resource": "Microsoft Graph",
|
||||
"scope": "Group.ReadWrite.All"
|
||||
}
|
||||
]
|
||||
},
|
||||
"paths": {
|
||||
"zippedPackage": "solution/js-public-unjoined-teams.sppkg"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
|
||||
"port": 4321,
|
||||
"https": true,
|
||||
"initialPage": "https://localhost:5432/workbench",
|
||||
"api": {
|
||||
"port": 5432,
|
||||
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
|
||||
"cdnBasePath": "<!-- PATH TO CDN -->"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
const gulp = require('gulp');
|
||||
const build = require('@microsoft/sp-build-web');
|
||||
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
|
||||
|
||||
build.initialize(gulp);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"name": "js-public-unjoined-teams",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "gulp bundle",
|
||||
"clean": "gulp clean",
|
||||
"test": "gulp test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/sp-core-library": "1.7.1-plusbeta",
|
||||
"@microsoft/sp-webpart-base": "1.7.1-plusbeta",
|
||||
"@microsoft/sp-lodash-subset": "1.7.1-plusbeta",
|
||||
"@microsoft/sp-office-ui-fabric-core": "1.7.1-plusbeta",
|
||||
"@types/webpack-env": "1.13.1",
|
||||
"@types/es6-promise": "0.0.33"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/sp-build-web": "1.7.1-plusbeta",
|
||||
"@microsoft/sp-tslint-rules": "1.7.1-plusbeta",
|
||||
"@microsoft/sp-module-interfaces": "1.7.1-plusbeta",
|
||||
"@microsoft/sp-webpart-workbench": "1.7.1-plusbeta",
|
||||
"gulp": "~3.9.1",
|
||||
"@types/chai": "3.4.34",
|
||||
"@types/mocha": "2.2.38",
|
||||
"ajv": "~5.2.2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
// A file is required to be in the root of the /src directory by the TypeScript compiler
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
|
||||
"id": "62eb4ba8-6014-4bd8-a428-097ff64f01c6",
|
||||
"alias": "PublicUnjoinedTeamsWebPart",
|
||||
"componentType": "WebPart",
|
||||
"version": "*",
|
||||
"manifestVersion": 2,
|
||||
"requiresCustomScript": false,
|
||||
"preconfiguredEntries": [{
|
||||
"groupId": "5c03119e-3074-46fd-976b-c60198311f70",
|
||||
"group": { "default": "Other" },
|
||||
"title": { "default": "Public Unjoined Teams" },
|
||||
"description": { "default": "Lists all the public teams the current user is not a member of, and displays a button for joining." },
|
||||
"officeFabricIconFontName": "TeamsLogoInverse",
|
||||
"properties": {
|
||||
"title": "Public Teams I'm not a member of"
|
||||
}
|
||||
}]
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
|
||||
|
||||
.publicUnjoinedTeams {
|
||||
.title {
|
||||
@include ms-font-xl;
|
||||
color: "[theme: neutralSecondary, default: #80539D]";
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 700px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.row {
|
||||
@include ms-Grid-row;
|
||||
padding: 4px 15px;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
background-color: $ms-color-neutralLight;
|
||||
color: $ms-color-black;
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
button {
|
||||
text-decoration: none;
|
||||
height: 30px;
|
||||
min-width: 65px;
|
||||
background-color: $ms-color-themePrimary;
|
||||
border: none;
|
||||
color: $ms-color-white;
|
||||
cursor: pointer;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
import { Version } from '@microsoft/sp-core-library';
|
||||
import {
|
||||
BaseClientSideWebPart,
|
||||
IPropertyPaneConfiguration,
|
||||
PropertyPaneTextField
|
||||
} from '@microsoft/sp-webpart-base';
|
||||
|
||||
import { difference, find } from '@microsoft/sp-lodash-subset';
|
||||
import { MSGraphClient } from '@microsoft/sp-http';
|
||||
|
||||
import styles from './PublicUnjoinedTeamsWebPart.module.scss';
|
||||
import * as strings from 'PublicUnjoinedTeamsWebPartStrings';
|
||||
|
||||
export interface IPublicUnjoinedTeamsWebPartProps {
|
||||
title: string;
|
||||
}
|
||||
|
||||
export default class PublicUnjoinedTeamsWebPart extends BaseClientSideWebPart<IPublicUnjoinedTeamsWebPartProps> {
|
||||
|
||||
public async render(): Promise<any> {
|
||||
var unjoinedTeams = await this.getUnjoinedTeams();
|
||||
|
||||
this.domElement.innerHTML = `
|
||||
<div class="${ styles.publicUnjoinedTeams }">
|
||||
<div class="${styles.title}">
|
||||
${this.properties.title}
|
||||
</div>
|
||||
<div id="container" class="${styles.container}">
|
||||
<!-- content gets appended here -->
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
await this.renderTeams(unjoinedTeams);
|
||||
}
|
||||
|
||||
protected async getUnjoinedTeams(): Promise<any> {
|
||||
var joinedTeamIds:Array<string> = (await this.graphGet(`https://graph.microsoft.com/beta/me/joinedTeams`)).value.map(x => x.id);
|
||||
var allTeams= (await this.graphGet(`https://graph.microsoft.com/beta/groups?$filter=resourceProvisioningOptions/Any(x:x eq 'Team')&$top=999`)).value;
|
||||
|
||||
var publicTeamIds:Array<string> = allTeams.filter(team => team.visibility === 'Public').map(x => x.id);
|
||||
var missingTeamIds:Array<string> = difference(publicTeamIds, joinedTeamIds);
|
||||
|
||||
let missingTeams = JSON.parse('{"value": []}');
|
||||
|
||||
missingTeamIds.forEach(teamId => {
|
||||
var team = find(allTeams, {'id': teamId});
|
||||
missingTeams['value'].push(team);
|
||||
});
|
||||
|
||||
return missingTeams.value;
|
||||
}
|
||||
|
||||
protected async renderTeams(teamsToShow) {
|
||||
const container: Element = this.domElement.querySelector('#container');
|
||||
var userId:string = (await this.graphGet("me")).id;
|
||||
|
||||
for (var team of teamsToShow)
|
||||
{
|
||||
var row:HTMLDivElement = document.createElement("div");
|
||||
row.className = styles.row;
|
||||
container.appendChild(row);
|
||||
|
||||
var button:HTMLButtonElement = document.createElement("button");
|
||||
button.id = team.id;
|
||||
button.innerHTML = "Join";
|
||||
button.onclick = (e:Event) => this.addMember(e.srcElement, userId);
|
||||
row.appendChild(button);
|
||||
|
||||
var span:HTMLSpanElement = document.createElement("span");
|
||||
span.innerHTML = ` ${team.displayName}`;
|
||||
row.appendChild(span);
|
||||
}
|
||||
}
|
||||
|
||||
protected addMember(source:Element, userId:string) {
|
||||
var button:HTMLButtonElement = <HTMLButtonElement>source;
|
||||
button.disabled = true;
|
||||
button.innerText = "Joined!";
|
||||
|
||||
var body:string = `{"@odata.id": "https://graph.microsoft.com/v1.0/directoryObjects/${userId}"}`;
|
||||
this.graphPost(`https://graph.microsoft.com/v1.0/groups/${button.id}/members/$ref`, body);
|
||||
}
|
||||
|
||||
protected async graphGet(url: string, value?:Array<string>) {
|
||||
return await this.callGraph(url, "GET");
|
||||
}
|
||||
|
||||
protected async graphPost(url: string, body:string) {
|
||||
return await this.callGraph(url, "POST", body);
|
||||
}
|
||||
|
||||
protected async callGraph(url: string, method:string, body?:string): Promise<any> {
|
||||
const graphClient:MSGraphClient = await this.context.msGraphClientFactory.getClient();
|
||||
|
||||
var response;
|
||||
if (method.toUpperCase() == "GET") {
|
||||
response = await graphClient.api(url).get();
|
||||
}
|
||||
if (method.toUpperCase() == "POST") {
|
||||
response = await graphClient.api(url).post(body);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
protected get dataVersion(): Version {
|
||||
return Version.parse('1.0');
|
||||
}
|
||||
|
||||
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||||
return {
|
||||
pages: [
|
||||
{
|
||||
header: {
|
||||
description: strings.PropertyPaneDescription
|
||||
},
|
||||
groups: [
|
||||
{
|
||||
groupName: strings.BasicGroupName,
|
||||
groupFields: [
|
||||
PropertyPaneTextField('title', {
|
||||
label: strings.TitleFieldLabel
|
||||
})
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
define([], function() {
|
||||
return {
|
||||
"PropertyPaneDescription": "Lists all the public teams the current user is not a member of, and displays a button for joining.",
|
||||
"BasicGroupName": "Group Name",
|
||||
"TitleFieldLabel": "Title"
|
||||
}
|
||||
});
|
10
samples/js-public-unjoined-teams/src/webparts/publicUnjoinedTeams/loc/mystrings.d.ts
vendored
Normal file
10
samples/js-public-unjoined-teams/src/webparts/publicUnjoinedTeams/loc/mystrings.d.ts
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
declare interface IPublicUnjoinedTeamsWebPartStrings {
|
||||
PropertyPaneDescription: string;
|
||||
BasicGroupName: string;
|
||||
TitleFieldLabel: string;
|
||||
}
|
||||
|
||||
declare module 'PublicUnjoinedTeamsWebPartStrings' {
|
||||
const strings: IPublicUnjoinedTeamsWebPartStrings;
|
||||
export = strings;
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.2/MicrosoftTeams.schema.json",
|
||||
"manifestVersion": "1.2",
|
||||
"packageName": "PublicUnjoinedTeams",
|
||||
"id": "62eb4ba8-6014-4bd8-a428-097ff64f01c6",
|
||||
"version": "0.1",
|
||||
"developer": {
|
||||
"name": "SPFx + Teams Dev",
|
||||
"websiteUrl": "https://products.office.com/en-us/sharepoint/collaboration",
|
||||
"privacyUrl": "https://privacy.microsoft.com/en-us/privacystatement",
|
||||
"termsOfUseUrl": "https://www.microsoft.com/en-us/servicesagreement"
|
||||
},
|
||||
"name": {
|
||||
"short": "PublicUnjoinedTeams"
|
||||
},
|
||||
"description": {
|
||||
"short": "Allows users to join public Teams they are not yet members of.",
|
||||
"full": "Allows users to join public Teams they are not yet members of."
|
||||
},
|
||||
"icons": {
|
||||
"outline": "tab20x20.png",
|
||||
"color": "tab96x96.png"
|
||||
},
|
||||
"accentColor": "#004578",
|
||||
"configurableTabs": [
|
||||
{
|
||||
"configurationUrl": "https://{teamSiteDomain}{teamSitePath}/_layouts/15/TeamsLogon.aspx?SPFX=true&dest={teamSitePath}/_layouts/15/teamshostedapp.aspx%3FopenPropertyPane=true%26teams%26componentId=62eb4ba8-6014-4bd8-a428-097ff64f01c6",
|
||||
"canUpdateConfiguration": false,
|
||||
"scopes": [
|
||||
"team"
|
||||
]
|
||||
}
|
||||
],
|
||||
"validDomains": [
|
||||
"*.login.microsoftonline.com",
|
||||
"*.sharepoint.com",
|
||||
"*.sharepoint-df.com",
|
||||
"spoppe-a.akamaihd.net",
|
||||
"spoprod-a.akamaihd.net",
|
||||
"resourceseng.blob.core.windows.net",
|
||||
"msft.spoppe.com"
|
||||
],
|
||||
"webApplicationInfo": {
|
||||
"resource": "https://{teamSiteDomain}",
|
||||
"id": "00000003-0000-0ff1-ce00-000000000000"
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 933 B |
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"jsx": "react",
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"experimentalDecorators": true,
|
||||
"skipLibCheck": true,
|
||||
"outDir": "lib",
|
||||
"typeRoots": [
|
||||
"./node_modules/@types",
|
||||
"./node_modules/@microsoft"
|
||||
],
|
||||
"types": [
|
||||
"es6-promise",
|
||||
"webpack-env"
|
||||
],
|
||||
"lib": [
|
||||
"es5",
|
||||
"dom",
|
||||
"es2015.collection"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"lib"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue