Skip to content
This repository was archived by the owner on Nov 16, 2023. It is now read-only.

Change outlook support to limit samples to the loaded endpoint #760

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/client/app/actions/snippet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export class ImportSuccessAction implements Action {
export class UpdateInfoAction implements Action {
readonly type = SnippetActionTypes.UPDATE_INFO;

constructor(public payload: { id: string, name?: string, description?: string, gist?: string, gistOwnerId?: string }) { }
constructor(public payload: { id: string, name?: string, description?: string, gist?: string, gistOwnerId?: string, endpoints?: string[] }) { }
}

export class RunAction implements Action {
Expand Down
140 changes: 134 additions & 6 deletions src/client/app/components/snippet.info.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Component, Input, ChangeDetectionStrategy, Output, EventEmitter } from '@angular/core';
import { getGistUrl, environment, storage } from '../helpers';
import { getGistUrl, environment, storage, outlookEndpoints } from '../helpers';
import { Strings } from '../strings';
import { isNil } from 'lodash';

Expand All @@ -19,19 +19,51 @@ import { isNil } from 'lodash';
<textarea class="ms-TextField-field ms-font-m" [(ngModel)]="snippet.description" placeholder="{{strings.descriptionPlaceholder}}"></textarea>
</div>

<div *ngIf="inOutlook">
<label class="ms-fontWeight-semibold ms-fontColor-neutralPrimary">{{strings.extensionPointsLabel}}</label>
<ul class="ms-ChoiceFieldGroup-list">
<li>
<label class="container">
<span class="ms-Label">{{strings.mailRead}}</span>
<input type="checkbox" [(ngModel)]="MailRead"/>
<span class="checkmark"></span>
</label>
</li>
<li>
<label class="container">
<span class="ms-Label">{{strings.mailCompose}}</span>
<input type="checkbox" [(ngModel)]="MailCompose"/>
<span class="checkmark"></span>
</label>
</li>
<li>
<label class="container">
<span class="ms-Label">{{strings.appointmentOrganizer}}</span>
<input type="checkbox" [(ngModel)]="AppointmentOrganizer"/>
<span class="checkmark"></span>
</label>
</li>
<li>
<label class="container">
<span class="ms-Label">{{strings.appointmentAttendee}}</span>
<input type="checkbox" [(ngModel)]="AppointmentAttendee"/>
<span class="checkmark"></span>
</label>
</li>
</ul>
</div>

<div *ngIf="showGistUrl" class="ms-TextField">
<label class="ms-Label">{{strings.gistUrlLabel}}</label>
<label class="ms-Label ms-fontWeight-semibold">{{strings.gistUrlLabel}}</label>
<a href="{{gistUrl}}" target="_blank">{{strings.gistUrlLinkLabel}}</a>
</div>
<br/>
</div>
<div class="ms-Dialog-actions">
<div class="ms-Dialog-actionsRight">
<button class="ms-Dialog-action ms-Button" (click)="dismiss.emit(true)">
<button [ngClass]="buttonClasses" (click)="saveDisabled ? null : dismiss.emit(true)">
<span class="ms-Button-label">{{strings.save}}</span>
</button>
<button class="ms-Dialog-action ms-Button" (click)="dismiss.emit(false)">
<span class="ms-Button-label">{{strings.cancel}}</span>
</button>
</div>
</div>
</dialog>
Expand All @@ -45,6 +77,13 @@ export class SnippetInfo {

strings = Strings();

get buttonClasses() {
return {
'ms-Button ms-Button--primary': true,
'is-disabled': this.saveDisabled,
};
}

get showGistUrl() {
if (!this.snippet.gist) {
return false;
Expand All @@ -67,4 +106,93 @@ export class SnippetInfo {
let host = this.snippet.host.toLowerCase();
return `${environment.current.config.editorUrl}/#/view/${host}/gist/${this.snippet.gist}`;
}

// Outlook Specific tooling

get inOutlook() {
return this.snippet.host.toLowerCase() === 'outlook';
}

get MailRead() {
if (this.snippet.endpoints === undefined) {
return false;
}
return this.snippet.endpoints.indexOf(outlookEndpoints.MailRead) !== -1;
}

@Input()
set MailRead(checked: boolean) {
this.snippet.endpoints = this.snippet.endpoints ? this.snippet.endpoints : [];
if (checked) {
if (this.snippet.endpoints.indexOf(outlookEndpoints.MailRead) === -1) {
this.snippet.endpoints.push(outlookEndpoints.MailRead);
}
} else {
this.snippet.endpoints = this.snippet.endpoints.filter(endpoint => endpoint !== outlookEndpoints.MailRead);
}
}

get MailCompose() {
if (this.snippet.endpoints === undefined) {
return false;
}
return this.snippet.endpoints.indexOf(outlookEndpoints.MailCompose) !== -1;
}

@Input()
set MailCompose(checked: boolean) {
this.snippet.endpoints = this.snippet.endpoints ? this.snippet.endpoints : [];
if (checked) {
if (this.snippet.endpoints.indexOf(outlookEndpoints.MailCompose) === -1) {
this.snippet.endpoints.push(outlookEndpoints.MailCompose);
}
} else {
this.snippet.endpoints = this.snippet.endpoints.filter(endpoint => endpoint !== outlookEndpoints.MailCompose);
}
}

get AppointmentOrganizer() {
if (this.snippet.endpoints === undefined) {
return false;
}
return this.snippet.endpoints.indexOf(outlookEndpoints.AppointmentOrganizer) !== -1;
}

@Input()
set AppointmentOrganizer(checked: boolean) {
this.snippet.endpoints = this.snippet.endpoints ? this.snippet.endpoints : [];
if (checked) {
if (this.snippet.endpoints.indexOf(outlookEndpoints.AppointmentOrganizer) === -1) {
this.snippet.endpoints.push(outlookEndpoints.AppointmentOrganizer);
}
} else {
this.snippet.endpoints = this.snippet.endpoints.filter(endpoint => endpoint !== outlookEndpoints.AppointmentOrganizer);
}
}

get AppointmentAttendee() {
if (this.snippet.endpoints === undefined) {
return false;
}
return this.snippet.endpoints.indexOf(outlookEndpoints.AppointmentAttendee) !== -1;
}

@Input()
set AppointmentAttendee(checked: boolean) {
this.snippet.endpoints = this.snippet.endpoints ? this.snippet.endpoints : [];
if (checked) {
if (this.snippet.endpoints.indexOf(outlookEndpoints.AppointmentAttendee) === -1) {
this.snippet.endpoints.push(outlookEndpoints.AppointmentAttendee);
}
} else {
this.snippet.endpoints = this.snippet.endpoints.filter(endpoint => endpoint !== outlookEndpoints.AppointmentAttendee);
}
}

@Input()
get saveDisabled() {
// In outlook, at least one endpoint must be enabled, so we disable the save button unless at least one is checked.
return this.inOutlook && !(this.MailRead || this.MailCompose || this.AppointmentAttendee || this.AppointmentOrganizer);
}

}
13 changes: 12 additions & 1 deletion src/client/app/containers/editor.mode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import { Subscription } from 'rxjs/Subscription';
</footer>
</main>
<about [(show)]="showAbout"></about>
<snippet-info [show]="showInfo" [snippet]="snippet" (dismiss)="create($event); showInfo=false"></snippet-info>
<snippet-info [show]="shouldShowInfo" [snippet]="snippet" (dismiss)="create($event); showInfo=false"></snippet-info>
<profile [show]="showProfile" [profile]="profile$|async" (dismiss)="logout($event); showProfile=false"></profile>
<import [hidden]="!(showImport$|async)"></import>
<alert></alert>
Expand All @@ -56,13 +56,15 @@ export class EditorMode {
snippet: ISnippet;
isEmpty: boolean;
isDisabled: boolean;
showInfo: boolean;

strings = Strings();

private snippetSub: Subscription;
private sharingSub: Subscription;
private errorsSub: Subscription;


constructor(
private _store: Store<fromRoot.State>,
private _effects: UIEffects,
Expand All @@ -72,6 +74,11 @@ export class EditorMode {
this.snippetSub = this._store.select(fromRoot.getCurrent).subscribe(snippet => {
this.isEmpty = snippet == null;
this.snippet = snippet;
const inOutlook = this.snippet !== null && this.snippet.host.toLowerCase() === 'outlook';
const outlookNeedsEndpoints = inOutlook && (this.snippet.endpoints === undefined || this.snippet.endpoints.length === 0);
if (outlookNeedsEndpoints) {
this.showInfo = true;
}
});

this.sharingSub = this._store.select(fromRoot.getSharing).subscribe(sharing => {
Expand All @@ -83,6 +90,10 @@ export class EditorMode {
this.parseEditorRoutingParams();
}

get shouldShowInfo() {
return this.showInfo;
}

get isAddinCommands() {
return environment.current.isAddinCommands;
}
Expand Down
15 changes: 13 additions & 2 deletions src/client/app/effects/snippet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ import { isEmpty, isNil, find, assign, reduce, forIn, isEqual } from 'lodash';
import * as sha1 from 'crypto-js/sha1';
import { Utilities, HostType } from '@microsoft/office-js-helpers';

function playlistUrl() {
let host = environment.current.host.toLowerCase();
if (environment.current.endpoint !== null) {
host += `-${environment.current.endpoint}`;
}
return `${environment.current.config.samplesUrl}/playlists/${host}.yaml`;
}

@Injectable()
export class SnippetEffects {
constructor(
Expand Down Expand Up @@ -173,7 +181,7 @@ export class SnippetEffects {
.map((action: Snippet.LoadTemplatesAction) => action.payload)
.mergeMap(source => {
if (source === 'LOCAL') {
let snippetJsonUrl = `${environment.current.config.samplesUrl}/playlists/${environment.current.host.toLowerCase()}.yaml`;
let snippetJsonUrl = playlistUrl();
return this._request.get<ITemplate[]>(snippetJsonUrl, ResponseTypes.YAML);
}
else {
Expand All @@ -192,7 +200,7 @@ export class SnippetEffects {
updateInfo$: Observable<Action> = this.actions$
.ofType(Snippet.SnippetActionTypes.UPDATE_INFO)
.map(({ payload }) => {
let { id, name, description, gist, gistOwnerId } = payload;
let { id, name, description, gist, gistOwnerId, endpoints } = payload;
let snippet: ISnippet = storage.lastOpened;
if (storage.snippets.contains(id)) {
snippet = storage.snippets.get(id);
Expand All @@ -210,6 +218,9 @@ export class SnippetEffects {
if (!isNil(gistOwnerId)) {
snippet.gistOwnerId = gistOwnerId;
}
if (!isNil(endpoints)) {
snippet.endpoints = endpoints;
}

/* updates snippet */
storage.snippets.insert(id, snippet);
Expand Down
5 changes: 5 additions & 0 deletions src/client/app/helpers/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class Environment {

host: null,
platform: null,
endpoint: null,

runtimeSessionTimestamp: (new Date()).getTime().toString()
};
Expand Down Expand Up @@ -164,6 +165,7 @@ class Environment {
commands: any/* whether app-commands are available, relevant for Office Add-ins */,
mode: string /* and older way of opening Script Lab to a particular host */,
host: string /* same as "mode", also needed here so that overrides can also have this parameter */,
endpoint: string /* Defines which type of outlook experience is active */,
wacUrl: string,
tryIt: any,
};
Expand Down Expand Up @@ -200,6 +202,9 @@ class Environment {
}
if (isValidHost(pageParams.mode)) {
this.appendCurrent({ host: pageParams.mode.toUpperCase() });
if (pageParams.endpoint) {
this.appendCurrent({endpoint: pageParams.endpoint.toLowerCase()});
}
return true;
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/client/app/helpers/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ const officeHostsToAppNames = {
'WORD': 'Word'
};

export const outlookEndpoints = {
MailRead: 'messageread',
MailCompose: 'messagecompose',
AppointmentOrganizer: 'appointmentcompose',
AppointmentAttendee: 'appointmentread',
};

export function isValidHost(host: string) {
host = host.toUpperCase();
return isOfficeHost(host) || (host === 'WEB');
Expand Down
5 changes: 5 additions & 0 deletions src/client/app/strings/chinese-simplified.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@ export function getChineseSimplifiedStrings(): ClientStringsPerLanguage {
// Outlook-only strings

noRunInOutlook: getEnglishSubstitutesForNotYetTranslated().noRunInOutlook,
extensionPointsLabel: getEnglishSubstitutesForNotYetTranslated().extensionPointsLabel,
mailRead: getEnglishSubstitutesForNotYetTranslated().mailRead,
mailCompose: getEnglishSubstitutesForNotYetTranslated().mailCompose,
appointmentOrganizer: getEnglishSubstitutesForNotYetTranslated().appointmentOrganizer,
appointmentAttendee: getEnglishSubstitutesForNotYetTranslated().appointmentAttendee,

// import.ts strings

Expand Down
5 changes: 5 additions & 0 deletions src/client/app/strings/english.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@ export function getEnglishStrings(): ClientStringsPerLanguage {
// Outlook-only strings

noRunInOutlook: /** NEEDS STRING REVIEW **/ `You cannot run your snippet from the code window in Outlook. Please open the "Run" pane in Outlook to run your snippet.`,
extensionPointsLabel: /** NEEDS STRING REVIEW **/ `Supported Extension Points`,
mailRead: /** NEEDS STRING REVIEW **/ `Mail Read`,
mailCompose: /** NEEDS STRING REVIEW **/ `Mail Compose`,
appointmentOrganizer: /** NEEDS STRING REVIEW **/ `Appointment Organizer`,
appointmentAttendee: /** NEEDS STRING REVIEW **/ `Appointment Attendee`,

// import.ts strings

Expand Down
5 changes: 5 additions & 0 deletions src/client/app/strings/german.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,11 @@ export function getGermanStrings(): ClientStringsPerLanguage {
// Outlook-only strings

noRunInOutlook: 'Das Code-Schnipsel kann in Outlook nicht aus dem Code-Fenster heraus ausgeführt werden. Bitte öffnen Sie den Aufgabenbereich zur Code-Ausführung und rufen Sie das Schnipsel von dort aus auf.',
extensionPointsLabel: getEnglishSubstitutesForNotYetTranslated().extensionPointsLabel,
mailRead: getEnglishSubstitutesForNotYetTranslated().mailRead,
mailCompose: getEnglishSubstitutesForNotYetTranslated().mailCompose,
appointmentOrganizer: getEnglishSubstitutesForNotYetTranslated().appointmentOrganizer,
appointmentAttendee: getEnglishSubstitutesForNotYetTranslated().appointmentAttendee,

// import.ts strings

Expand Down
5 changes: 5 additions & 0 deletions src/client/app/strings/spanish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,11 @@ export function getSpanishStrings(): ClientStringsPerLanguage {
// Outlook-only strings

noRunInOutlook: getEnglishSubstitutesForNotYetTranslated().noRunInOutlook,
extensionPointsLabel: getEnglishSubstitutesForNotYetTranslated().extensionPointsLabel,
mailRead: getEnglishSubstitutesForNotYetTranslated().mailRead,
mailCompose: getEnglishSubstitutesForNotYetTranslated().mailCompose,
appointmentOrganizer: getEnglishSubstitutesForNotYetTranslated().appointmentOrganizer,
appointmentAttendee: getEnglishSubstitutesForNotYetTranslated().appointmentAttendee,

// import.ts strings

Expand Down
1 change: 1 addition & 0 deletions src/client/assets/styles/common.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
@import 'mixins';
@import 'components/spinner';
@import 'components/command';
@import 'components/checkbox';

* {
margin: 0;
Expand Down
Loading