Logic implemented
This commit is contained in:
parent
3738bd7bfd
commit
8896b08ecb
|
@ -1,30 +0,0 @@
|
|||
/**
|
||||
* Represents SharePoint View object
|
||||
*/
|
||||
export interface ISPView {
|
||||
Title: string;
|
||||
Id: string;
|
||||
ListId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents SharePoint List object
|
||||
*/
|
||||
export interface ISPList {
|
||||
Title: string;
|
||||
Id: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents SharePoint REST service response for /_api/web/lists service call
|
||||
*/
|
||||
export interface ISPLists {
|
||||
value: ISPList[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents SharePoint REST service response for /_api/web/lists('id')/views service call
|
||||
*/
|
||||
export interface ISPViews {
|
||||
value: ISPView[];
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
import * as SP from '../typings/SP';
|
||||
|
||||
/**
|
||||
* Data Helpers interface
|
||||
*/
|
||||
export interface IDataHelper {
|
||||
/**
|
||||
* API to get lists from the source
|
||||
*/
|
||||
getLists(): Promise<ISPList[]>;
|
||||
/**
|
||||
* API to get views from the source
|
||||
*/
|
||||
getViews(listId: string): Promise<ISPView[]>;
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
import { ISPList, ISPView } from '../common/SPEntities';
|
||||
import { IDataHelper } from './DataHelperBase';
|
||||
|
||||
/**
|
||||
* MOCK data helper. Gets data from hardcoded values
|
||||
*/
|
||||
export class DataHelperMock implements IDataHelper {
|
||||
/**
|
||||
* hardcoded collection of lists
|
||||
*/
|
||||
private static _lists: ISPList[] = [{ Title: 'Test 1', Id: '1' }, { Title: 'Test 2', Id: '2' }, { Title: 'Test 3', Id: '3' }];
|
||||
/**
|
||||
* hardcoded collection of views
|
||||
*/
|
||||
private static _views: ISPView[] = [{ Title: 'All Items', Id: '1', ListId: '1' }, { Title: 'Demo', Id: '2', ListId: '1' }, { Title: 'All Items', Id: '1', ListId: '2' }, { Title: 'All Items', Id: '1', ListId: '3' }];
|
||||
|
||||
/**
|
||||
* API to get lists from the source
|
||||
*/
|
||||
public getLists(): Promise<ISPList[]> {
|
||||
return new Promise<ISPList[]>((resolve) => {
|
||||
resolve(DataHelperMock._lists); // returning all the lists
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* API to get views from the source
|
||||
*/
|
||||
public getViews(listId: string): Promise<ISPView[]> {
|
||||
return new Promise<ISPView[]>((resolve) => {
|
||||
// filtering views based on list id
|
||||
const result: ISPView[] = DataHelperMock._views.filter((value, index, array) => {
|
||||
return value.ListId === listId;
|
||||
});
|
||||
resolve(result);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
import {
|
||||
IWebPartContext
|
||||
} from '@microsoft/sp-webpart-base';
|
||||
import { ISPList, ISPView, ISPLists, ISPViews } from '../common/SPEntities';
|
||||
import { IDataHelper } from './DataHelperBase';
|
||||
|
||||
/**
|
||||
* List with views interface
|
||||
*/
|
||||
interface ISPListWithViews extends ISPList {
|
||||
/**
|
||||
* List Views
|
||||
*/
|
||||
Views: ISPView[];
|
||||
}
|
||||
|
||||
/**
|
||||
* SharePoint Data Helper class.
|
||||
* Gets information from current web
|
||||
*/
|
||||
export class DataHelperSP implements IDataHelper {
|
||||
/**
|
||||
* Web part context
|
||||
*/
|
||||
public context: IWebPartContext;
|
||||
|
||||
/**
|
||||
* Loaded lists
|
||||
*/
|
||||
private _lists: ISPListWithViews[];
|
||||
|
||||
/**
|
||||
* ctor
|
||||
*/
|
||||
public constructor(_context: IWebPartContext) {
|
||||
this.context = _context;
|
||||
}
|
||||
|
||||
/**
|
||||
* API to get lists from the source
|
||||
*/
|
||||
public getLists(): Promise<ISPList[]> {
|
||||
return this.context.httpClient.get(this.context.pageContext.web.absoluteUrl + `/_api/web/lists?$filter=Hidden eq false`) // sending the request to SharePoint REST API
|
||||
.then((response: Response) => { // httpClient.get method returns a response object where json method creates a Promise of getting result
|
||||
return response.json();
|
||||
}).then((response: ISPLists) => { // response is an ISPLists object
|
||||
return response.value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* API to get views from the source
|
||||
*/
|
||||
public getViews(listId: string): Promise<ISPView[]> {
|
||||
if (listId && listId == '-1' || listId == '0')
|
||||
return new Promise<ISPView[]>((resolve) => {
|
||||
resolve(new Array<ISPView>());
|
||||
});
|
||||
|
||||
//
|
||||
// trying to get views from cache
|
||||
//
|
||||
const lists: ISPListWithViews[] = this._lists && this._lists.length && this._lists.filter((value, index, array) => { return value.Id === listId; });
|
||||
|
||||
if (lists && lists.length) {
|
||||
return new Promise<ISPView[]>((resolve) => {
|
||||
resolve(lists[0].Views);
|
||||
});
|
||||
}
|
||||
else {
|
||||
return this.context.httpClient.get(this.context.pageContext.web.absoluteUrl + '/_api/web/lists(\'' + listId + '\')/views') // requesting views from SharePoint REST API
|
||||
.then((response: Response) => { // httpClient.get method returns a response object where json method creates a Promise of getting result
|
||||
return response.json();
|
||||
}).then((response: ISPViews) => { // response is an ISPViews object
|
||||
var views = response.value;
|
||||
if (!this._lists || !this._lists.length)
|
||||
this._lists = new Array<ISPListWithViews>();
|
||||
this._lists.push({ Id: listId, Title: '', Views: views });
|
||||
return views;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
declare namespace SP {
|
||||
export class ClientContext {
|
||||
static get_current(): ClientContext;
|
||||
load(obj: any, ...rest: string[]): void;
|
||||
executeQueryAsyncCallback(success: (result: any) => void, error: (error: any) => void);
|
||||
}
|
||||
|
||||
namespace Taxonomy {
|
||||
export class TaxonomySession {
|
||||
static getTaxonomySession(spContext: ClientRect): TaxonomySession;
|
||||
get_termStores(): ITermStoreCollection;
|
||||
}
|
||||
|
||||
export interface ITermStore {
|
||||
get_groups(): ITermGroupCollection;
|
||||
}
|
||||
|
||||
export interface ITermStoreCollection {
|
||||
get_item(index: number): ITermStore;
|
||||
}
|
||||
|
||||
export interface ITermGroup {
|
||||
}
|
||||
|
||||
export interface ITermGroupCollection {
|
||||
get_item(index: number): ITermGroup;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,4 +18,6 @@
|
|||
"lib": true,
|
||||
"temp": true
|
||||
}
|
||||
,
|
||||
"typescript.tsdk": "./node_modules/typescript/lib"
|
||||
}
|
|
@ -15,7 +15,7 @@
|
|||
"react": "node_modules/react/dist/react.min.js",
|
||||
"react-dom": "node_modules/react-dom/dist/react-dom.min.js",
|
||||
"react-dom/server": "node_modules/react-dom/dist/react-dom-server.min.js",
|
||||
"knockout": "node_modules/knockout/build/output/knockout-latest.js"
|
||||
"knockout": "node_modules/knockout/build/output/knockout-latest.debug.js"
|
||||
},
|
||||
"localizedResources": {
|
||||
"termSetRequesterStrings": "webparts/termSetRequester/loc/{locale}.js"
|
|
@ -12,6 +12,7 @@
|
|||
"devDependencies": {
|
||||
"@microsoft/sp-build-web": "~0.7.0",
|
||||
"@microsoft/sp-module-interfaces": "~0.4.0",
|
||||
"@microsoft/sp-webpart-base": "^0.1.0",
|
||||
"@microsoft/sp-webpart-workbench": "~0.5.0",
|
||||
"gulp": "~3.9.1",
|
||||
"knockout": "^3.4.1"
|
|
@ -9,6 +9,8 @@ import styles from './TermSetRequester.module.scss';
|
|||
import * as strings from 'termSetRequesterStrings';
|
||||
import { ITermSetRequesterWebPartProps } from './ITermSetRequesterWebPartProps';
|
||||
|
||||
import { TaxonomyControl } from './controls/TaxonomyControl';
|
||||
|
||||
export default class TermSetRequesterWebPart extends BaseClientSideWebPart<ITermSetRequesterWebPartProps> {
|
||||
|
||||
public constructor(context: IWebPartContext) {
|
||||
|
@ -19,18 +21,13 @@ export default class TermSetRequesterWebPart extends BaseClientSideWebPart<ITerm
|
|||
this.domElement.innerHTML = `
|
||||
<div class="${styles.termSetRequester}">
|
||||
<div class="${styles.container}">
|
||||
<div class="ms-Grid-row ms-bgColor-themeDark ms-fontColor-white ${styles.row}">
|
||||
<div class="ms-Grid-col ms-u-lg10 ms-u-xl8 ms-u-xlPush2 ms-u-lgPush1">
|
||||
<span class="ms-font-xl ms-fontColor-white">Welcome to SharePoint!</span>
|
||||
<p class="ms-font-l ms-fontColor-white">Customize SharePoint experiences using Web Parts.</p>
|
||||
<p class="ms-font-l ms-fontColor-white">${this.properties.description}</p>
|
||||
<a href="https://github.com/SharePoint/sp-dev-docs/wiki" class="ms-Button ${styles.button}">
|
||||
<span class="ms-Button-label">Learn more</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
const container: HTMLDivElement = this.domElement.querySelector('.' + styles.container) as HTMLDivElement;
|
||||
var termStoreCtrl = new TaxonomyControl(this.context);
|
||||
termStoreCtrl.render(container);
|
||||
}
|
||||
|
||||
protected get propertyPaneSettings(): IPropertyPaneSettings {
|
|
@ -0,0 +1,99 @@
|
|||
/**
|
||||
* Base interface for Taxonomy objects
|
||||
*/
|
||||
export interface ITermBase {
|
||||
/**
|
||||
* Unique identifier
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* Name
|
||||
*/
|
||||
name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Term Store interface
|
||||
*/
|
||||
export interface ITermStore extends ITermBase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Term Group interface
|
||||
*/
|
||||
export interface ITermGroup extends ITermBase {
|
||||
/**
|
||||
* Term Group's description
|
||||
*/
|
||||
description: string;
|
||||
/**
|
||||
* Term Store ID.
|
||||
* It is added to be able to store hierarchy structure (and it helps to decrease amount of Ajax requests to SharePoint)
|
||||
*/
|
||||
termStoreId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Term Set Interface
|
||||
*/
|
||||
export interface ITermSet extends ITermBase {
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
* Term Group ID
|
||||
* It is added to be able to store hierarchy structure (and it helps to decrease amount of Ajax requests to SharePoint)
|
||||
*/
|
||||
termGroupId?: string;
|
||||
/**
|
||||
* Term Store ID.
|
||||
* It is added to be able to store hierarchy structure (and it helps to decrease amount of Ajax requests to SharePoint)
|
||||
*/
|
||||
termStoreId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Term interface`
|
||||
*/
|
||||
export interface ITerm extends ITermBase {
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
description: string;
|
||||
/**
|
||||
* Flag if the current term is a root term in a term set
|
||||
*/
|
||||
isRoot: boolean;
|
||||
/**
|
||||
* Term labels
|
||||
*/
|
||||
labels: ILabel[];
|
||||
/**
|
||||
* Number of child terms
|
||||
*/
|
||||
termsCount: number;
|
||||
/**
|
||||
* Term Set ID
|
||||
* It is added to be able to store hierarchy structure (and it helps to decrease amount of Ajax requests to SharePoint)
|
||||
*/
|
||||
termSetId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Term Label interface
|
||||
*/
|
||||
export interface ILabel {
|
||||
/**
|
||||
* Flag if the label is default for current language
|
||||
*/
|
||||
isDefaultForLanguage: boolean;
|
||||
/**
|
||||
* Label's value
|
||||
*/
|
||||
value: string;
|
||||
/**
|
||||
* Current Language
|
||||
*/
|
||||
language: string;
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
import * as ko from 'knockout';
|
||||
import {
|
||||
IWebPartContext
|
||||
} from '@microsoft/sp-webpart-base';
|
||||
|
||||
import { TaxonomyControlView } from './TaxonomyControlView';
|
||||
import { TaxonomyControlViewModel } from './TaxonomyControlViewModel';
|
||||
|
||||
/**
|
||||
* Class that represents a control to render Taxonomy hierarchy
|
||||
*/
|
||||
export class TaxonomyControl {
|
||||
/**
|
||||
* View
|
||||
*/
|
||||
private view: TaxonomyControlView;
|
||||
/**
|
||||
* ViewModel
|
||||
*/
|
||||
private viewModel: TaxonomyControlViewModel;
|
||||
|
||||
/**
|
||||
* ctor
|
||||
* @param context: web part context
|
||||
*/
|
||||
constructor(context: IWebPartContext) {
|
||||
this.view = new TaxonomyControlView();
|
||||
this.viewModel = new TaxonomyControlViewModel(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the control
|
||||
*/
|
||||
public render(element: HTMLElement): Promise<void> {
|
||||
return new Promise<void>((resolve) => {
|
||||
// renders html first
|
||||
this.view.render(element).then(() => {
|
||||
// inits view model
|
||||
this.viewModel.init().then(() => {
|
||||
// applies bindings
|
||||
ko.applyBindings(this.viewModel, element);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
import {
|
||||
ITermStore,
|
||||
ITermSet,
|
||||
ITermGroup,
|
||||
ITerm
|
||||
} from '../common/SPEntities';
|
||||
|
||||
import {
|
||||
IWebPartContext
|
||||
} from '@microsoft/sp-webpart-base';
|
||||
|
||||
import { IDataHelper } from '../data-helpers/DataHelperBase';
|
||||
import { DataHelpersFactory } from '../data-helpers/DataHelpersFactory';
|
||||
|
||||
/**
|
||||
* Taxonomy Control Model
|
||||
*/
|
||||
export class TaxonomyControlModel {
|
||||
/**
|
||||
* data helper
|
||||
*/
|
||||
private _dataHelper: IDataHelper;
|
||||
|
||||
/**
|
||||
* ctor
|
||||
* @param context: web part context
|
||||
*/
|
||||
constructor(context: IWebPartContext) {
|
||||
this._dataHelper = DataHelpersFactory.createDataHelper(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* API to get Term Stores
|
||||
*/
|
||||
public getTermStores(): Promise<ITermStore[]> {
|
||||
return this._dataHelper.getTermStores();
|
||||
}
|
||||
|
||||
/**
|
||||
* API to get Term Groups by Term Store
|
||||
*/
|
||||
public getTermGroups(termStore: ITermStore): Promise<ITermGroup[]> {
|
||||
return this._dataHelper.getTermGroups(termStore.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* API to get Term Sets by Term Group
|
||||
*/
|
||||
public getTermSets(termGroup: ITermGroup): Promise<ITermSet[]> {
|
||||
return this._dataHelper.getTermSets(termGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
* API to get Terms by Term Set
|
||||
*/
|
||||
public getTerms(termSet: ITermSet): Promise<ITerm[]> {
|
||||
return this._dataHelper.getTerms(termSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* API to get Terms by Term
|
||||
*/
|
||||
public getChildTerms(term: ITerm): Promise<ITerm[]> {
|
||||
return this._dataHelper.getChildTerms(term);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
export class TaxonomyControlView {
|
||||
private _template: string = `
|
||||
<div>
|
||||
<ul>
|
||||
<!-- ko template: { name: 'termStoreTpl', foreach: termStores } -->
|
||||
<!-- /ko -->
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<script id="termStoreTpl" type="text/html">
|
||||
<li><b>Type: Term Store</b> Name: <span data-bind="text: name"></span>; ID: <span data-bind="text: id"></span> <a href="javascript:;" data-bind="text: (isExpanded() ? 'Collapse' : 'Expand'), click: actionClick"></a>
|
||||
<ul data-bind="visible: isExpanded">
|
||||
<!-- ko template: { name: 'termGroupTpl', foreach: termGroups } -->
|
||||
<!-- /ko -->
|
||||
</ul>
|
||||
</li>
|
||||
</script>
|
||||
|
||||
<script id="termGroupTpl" type="text/html">
|
||||
<li><b>Type: Term Group</b> Name: <span data-bind="text: name"></span>; Description: <span data-bind="text: description"></span> <a href="javascript:;" data-bind="text: (isExpanded() ? 'Collapse' : 'Expand'), click: actionClick"></a>
|
||||
<ul data-bind="visible: isExpanded">
|
||||
<!-- ko template: { name: 'termSetTpl', foreach: termSets } -->
|
||||
<!-- /ko -->
|
||||
</ul>
|
||||
</li>
|
||||
</script>
|
||||
|
||||
<script id="termSetTpl" type="text/html">
|
||||
<li><b>Type: Term Set</b> Name: <span data-bind="text: name"></span>; Description: <span data-bind="text: description"></span> <a href="javascript:;" data-bind="text: (isExpanded() ? 'Collapse' : 'Expand'), click: actionClick"></a>
|
||||
<ul data-bind="visible: isExpanded">
|
||||
<!-- ko template: { name: 'termTpl', foreach: terms } -->
|
||||
<!-- /ko -->
|
||||
</ul>
|
||||
</li>
|
||||
</script>
|
||||
|
||||
<script id="termTpl" type="text/html">
|
||||
<li><b>Type: Term</b> Name: <span data-bind="text: name"></span>; ID: <span data-bind="text: id"></span>; Description: <span data-bind="text: description"></span> <a href="javascript:;" data-bind="visible: hasChildTerms, text: (isExpanded() ? 'Collapse' : 'Expand'), click: actionClick"></a>
|
||||
Labels:<br/>
|
||||
<div data-bind="foreach: labels">
|
||||
<div>Value: <span data-bind="text: value"></span>; Language: <span data-bind="text: language"></span>; Is Default: <span data-bind="text: isDefaultForLanguage"></span></div>
|
||||
</div>
|
||||
<ul data-bind="visible: isExpanded">
|
||||
<!-- ko template: { name: 'termTpl', foreach: terms } -->
|
||||
<!-- /ko -->
|
||||
</ul>
|
||||
</li>
|
||||
</script>
|
||||
`;
|
||||
|
||||
/**
|
||||
* Renders markup
|
||||
*/
|
||||
public render(element: HTMLElement): Promise<void> {
|
||||
return new Promise<void>((resolve) => {
|
||||
element.innerHTML = this._template;
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,313 @@
|
|||
import * as ko from 'knockout';
|
||||
import { TaxonomyControlModel } from './TaxonomyControlModel';
|
||||
import {
|
||||
IWebPartContext
|
||||
} from '@microsoft/sp-webpart-base';
|
||||
import {
|
||||
ITermBase,
|
||||
ITermStore,
|
||||
ITermSet,
|
||||
ITermGroup,
|
||||
ITerm,
|
||||
ILabel
|
||||
} from '../common/SPEntities';
|
||||
|
||||
/**
|
||||
* Taxonomy Control ViewModel class
|
||||
*/
|
||||
export class TaxonomyControlViewModel {
|
||||
/**
|
||||
* Collection of term store view models
|
||||
*/
|
||||
protected termStores: KnockoutObservableArray<TermStoreViewModel>;
|
||||
/**
|
||||
* Model
|
||||
*/
|
||||
private model: TaxonomyControlModel;
|
||||
|
||||
/**
|
||||
* ctor
|
||||
* @param context: web part context
|
||||
*/
|
||||
constructor(context: IWebPartContext) {
|
||||
this.model = new TaxonomyControlModel(context);
|
||||
this.termStores = ko.observableArray<TermStoreViewModel>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Inits the view model object
|
||||
*/
|
||||
public init(): Promise<void> {
|
||||
return new Promise<void>((resolve) => {
|
||||
// loading top level items of the hierarchy
|
||||
this.model.getTermStores().then((termStores) => {
|
||||
const termStoreViewModels: TermStoreViewModel[] = [];
|
||||
termStores.forEach((value) => {
|
||||
termStoreViewModels.push(new TermStoreViewModel(this.model, value));
|
||||
});
|
||||
|
||||
this.termStores(termStoreViewModels);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base viewmodel for all Taxonomy objects
|
||||
*/
|
||||
export class TermBaseViewModel {
|
||||
/**
|
||||
* Object's id
|
||||
*/
|
||||
protected id: string;
|
||||
/**
|
||||
* Name
|
||||
*/
|
||||
protected name: string;
|
||||
/**
|
||||
* Flag if the the item is expanded
|
||||
*/
|
||||
protected isExpanded: KnockoutObservable<boolean>;
|
||||
/**
|
||||
* Model
|
||||
*/
|
||||
protected model: TaxonomyControlModel;
|
||||
|
||||
/**
|
||||
* ctor
|
||||
* @param model: taxonomy control model
|
||||
* @param entity: entity that is underline current view model
|
||||
*/
|
||||
constructor(model: TaxonomyControlModel, entity: ITermBase) {
|
||||
this.model = model;
|
||||
this.isExpanded = ko.observable<boolean>(false);
|
||||
this.id = entity.id;
|
||||
this.name = entity.name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Term store viewmodel
|
||||
*/
|
||||
export class TermStoreViewModel extends TermBaseViewModel {
|
||||
/**
|
||||
* Term store entity
|
||||
*/
|
||||
private entity: ITermStore;
|
||||
/**
|
||||
* collection of viewmodels for term store groups
|
||||
*/
|
||||
protected termGroups: KnockoutObservableArray<TermGroupViewModel>;
|
||||
|
||||
/**
|
||||
* ctor
|
||||
* @param model: taxonomy control model
|
||||
* @param entity: entity that is underline current view model
|
||||
*/
|
||||
constructor(model: TaxonomyControlModel, entity: ITermStore) {
|
||||
super(model, entity);
|
||||
this.entity = entity;
|
||||
this.termGroups = ko.observableArray<TermGroupViewModel>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand\collapse click handler
|
||||
*/
|
||||
protected actionClick(ev: MouseEvent): void {
|
||||
this.isExpanded(!this.isExpanded());
|
||||
|
||||
const isExpanded = this.isExpanded();
|
||||
|
||||
if (isExpanded) {
|
||||
const unwrappedGroups = ko.utils.unwrapObservable(this.termGroups);
|
||||
|
||||
if (!unwrappedGroups || !unwrappedGroups.length) {
|
||||
this.model.getTermGroups(this.entity).then((termGroups) => {
|
||||
const termGroupViewModels: TermGroupViewModel[] = [];
|
||||
termGroups.forEach((value) => {
|
||||
termGroupViewModels.push(new TermGroupViewModel(this.model, value));
|
||||
});
|
||||
|
||||
this.termGroups(termGroupViewModels);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Term Group ViewModel
|
||||
*/
|
||||
export class TermGroupViewModel extends TermBaseViewModel {
|
||||
/**
|
||||
* Term Group entity
|
||||
*/
|
||||
private entity: ITermGroup;
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
protected description: string;
|
||||
/**
|
||||
* collection of viewmodels for nested term sets
|
||||
*/
|
||||
protected termSets: KnockoutObservableArray<TermSetViewModel>;
|
||||
|
||||
/**
|
||||
* ctor
|
||||
* @param model: taxonomy control model
|
||||
* @param entity: entity that is underline current view model
|
||||
*/
|
||||
constructor(model: TaxonomyControlModel, entity: ITermGroup) {
|
||||
super(model, entity);
|
||||
|
||||
this.entity = entity;
|
||||
this.description = entity.description;
|
||||
this.termSets = ko.observableArray<TermSetViewModel>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand\collapse click handler
|
||||
*/
|
||||
protected actionClick(ev: MouseEvent): void {
|
||||
this.isExpanded(!this.isExpanded());
|
||||
|
||||
const isExpanded = this.isExpanded();
|
||||
|
||||
if (isExpanded) {
|
||||
const unwrappedSets = ko.utils.unwrapObservable(this.termSets);
|
||||
|
||||
if (!unwrappedSets || !unwrappedSets.length) {
|
||||
this.model.getTermSets(this.entity).then((termSets) => {
|
||||
const termSetViewModels: TermSetViewModel[] = [];
|
||||
termSets.forEach((value) => {
|
||||
termSetViewModels.push(new TermSetViewModel(this.model, value));
|
||||
});
|
||||
|
||||
this.termSets(termSetViewModels);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Term Set View Model
|
||||
*/
|
||||
export class TermSetViewModel extends TermBaseViewModel {
|
||||
/**
|
||||
* Term Set entity
|
||||
*/
|
||||
private entity: ITermSet;
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
protected description: string;
|
||||
/**
|
||||
* collection of viewmodels for nested terms
|
||||
*/
|
||||
protected terms: KnockoutObservableArray<TermViewModel>;
|
||||
|
||||
/**
|
||||
* ctor
|
||||
* @param model: taxonomy control model
|
||||
* @param entity: entity that is underline current view model
|
||||
*/
|
||||
constructor(model: TaxonomyControlModel, entity: ITermSet) {
|
||||
super(model, entity);
|
||||
|
||||
this.entity = entity;
|
||||
this.description = entity.description;
|
||||
this.terms = ko.observableArray<TermViewModel>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand\collapse click handler
|
||||
*/
|
||||
protected actionClick(ev: MouseEvent): void {
|
||||
this.isExpanded(!this.isExpanded());
|
||||
|
||||
const isExpanded = this.isExpanded();
|
||||
|
||||
if (isExpanded) {
|
||||
const unwrappedTerms = ko.utils.unwrapObservable(this.terms);
|
||||
|
||||
if (!unwrappedTerms || !unwrappedTerms.length) {
|
||||
this.model.getTerms(this.entity).then((terms) => {
|
||||
const termViewModels: TermViewModel[] = [];
|
||||
terms.forEach((value) => {
|
||||
termViewModels.push(new TermViewModel(this.model, value));
|
||||
});
|
||||
|
||||
this.terms(termViewModels);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Term ViewModel
|
||||
*/
|
||||
export class TermViewModel extends TermBaseViewModel {
|
||||
/**
|
||||
* Term emtity
|
||||
*/
|
||||
private entity: ITerm;
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
protected description: string;
|
||||
/**
|
||||
* collection of viewmodels for nested terms
|
||||
*/
|
||||
protected terms: KnockoutObservableArray<TermViewModel>;
|
||||
/**
|
||||
* Flag if current term has child terms
|
||||
*/
|
||||
protected hasChildTerms: boolean;
|
||||
/**
|
||||
* Term's labels
|
||||
*/
|
||||
protected labels: ILabel[];
|
||||
|
||||
/**
|
||||
* ctor
|
||||
* @param model: taxonomy control model
|
||||
* @param entity: entity that is underline current view model
|
||||
*/
|
||||
constructor(model: TaxonomyControlModel, entity: ITerm) {
|
||||
super(model, entity);
|
||||
|
||||
this.entity = entity;
|
||||
this.description = entity.description;
|
||||
this.terms = ko.observableArray<TermViewModel>();
|
||||
this.hasChildTerms = entity.termsCount > 0;
|
||||
this.labels = entity.labels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand\collapse click handler
|
||||
*/
|
||||
protected actionClick(ev: MouseEvent): void {
|
||||
this.isExpanded(!this.isExpanded());
|
||||
|
||||
const isExpanded = this.isExpanded();
|
||||
|
||||
if (isExpanded) {
|
||||
const unwrappedTerms = ko.utils.unwrapObservable(this.terms);
|
||||
|
||||
if (!unwrappedTerms || !unwrappedTerms.length) {
|
||||
this.model.getChildTerms(this.entity).then((terms) => {
|
||||
const termViewModels: TermViewModel[] = [];
|
||||
terms.forEach((value) => {
|
||||
termViewModels.push(new TermViewModel(this.model, value));
|
||||
});
|
||||
|
||||
this.terms(termViewModels);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
import {
|
||||
ITermStore,
|
||||
ITermSet,
|
||||
ITermGroup,
|
||||
ITerm
|
||||
} from '../common/SPEntities';
|
||||
|
||||
/**
|
||||
* Data Helpers interface
|
||||
*/
|
||||
export interface IDataHelper {
|
||||
/**
|
||||
* API to get Term Stores
|
||||
*/
|
||||
getTermStores(): Promise<ITermStore[]>;
|
||||
/**
|
||||
* API to get Term Groups by Term Store
|
||||
*/
|
||||
getTermGroups(termStoreId: string): Promise<ITermGroup[]>;
|
||||
/**
|
||||
* API to get Term Sets by Term Group
|
||||
*/
|
||||
getTermSets(termGroup: ITermGroup): Promise<ITermSet[]>;
|
||||
/**
|
||||
* API to get Terms by Term Set
|
||||
*/
|
||||
getTerms(termSet: ITermSet): Promise<ITerm[]>;
|
||||
/**
|
||||
* API to get Terms by Term
|
||||
*/
|
||||
getChildTerms(term: ITerm): Promise<ITerm[]>;
|
||||
}
|
|
@ -0,0 +1,185 @@
|
|||
import {
|
||||
ITermStore,
|
||||
ITermSet,
|
||||
ITermGroup,
|
||||
ITerm
|
||||
} from '../common/SPEntities';
|
||||
import {
|
||||
IDataHelper
|
||||
} from './DataHelperBase';
|
||||
|
||||
/**
|
||||
* Interface for Term store with groups
|
||||
*/
|
||||
interface ITermStoreWithGroups extends ITermStore {
|
||||
/**
|
||||
* nested groups
|
||||
*/
|
||||
groups?: ITermGroupWithTermSets[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for term group with term sets
|
||||
*/
|
||||
interface ITermGroupWithTermSets extends ITermGroup {
|
||||
/**
|
||||
* nested term sets
|
||||
*/
|
||||
termSets?: ITermSetWithTerms[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for term set with terms
|
||||
*/
|
||||
interface ITermSetWithTerms extends ITermSet {
|
||||
/**
|
||||
* nested terms
|
||||
*/
|
||||
terms?: ITermWithTerms[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for term with nested terms
|
||||
*/
|
||||
interface ITermWithTerms extends ITerm {
|
||||
/**
|
||||
* nested terms
|
||||
*/
|
||||
terms?: ITermWithTerms[];
|
||||
}
|
||||
|
||||
/**
|
||||
* MOCK data helper. Gets data from hardcoded values
|
||||
*/
|
||||
export class DataHelperMock implements IDataHelper {
|
||||
private static _termStores: ITermStoreWithGroups[] = [{
|
||||
name: 'Term Store 1',
|
||||
id: 'E920D42D-1540-4E39-B3F6-3795FBEF7F21',
|
||||
groups: [{
|
||||
id: '6789F532-CCDE-44C4-AE45-2EC5FC509274',
|
||||
termStoreId: 'E920D42D-1540-4E39-B3F6-3795FBEF7F21',
|
||||
name: 'Group 1 from Term Store 1',
|
||||
description: 'This is the first group of the first term store',
|
||||
termSets: [{
|
||||
name: 'TSet 1 Gr 1 TStore 1',
|
||||
id: 'B12D3D77-7DEA-435E-B1AF-3175DC9851DF',
|
||||
termGroupId: '6789F532-CCDE-44C4-AE45-2EC5FC509274',
|
||||
termStoreId: 'E920D42D-1540-4E39-B3F6-3795FBEF7F21',
|
||||
description: 'Term Set 1 from Group 1 from Term Store 1',
|
||||
terms: [{
|
||||
id: '1494A0ED-0866-4082-92A8-1C83D5B117C4',
|
||||
termSetId: 'B12D3D77-7DEA-435E-B1AF-3175DC9851DF',
|
||||
name: 'T 1 TSet 1 Gr 1 TStore 1',
|
||||
description: 'Term 1 from Term Set 1 from Group 1 from Term Store 1',
|
||||
labels: [{
|
||||
isDefaultForLanguage: true,
|
||||
value: 'Term Label',
|
||||
language: 'en-US'
|
||||
}],
|
||||
terms: [{
|
||||
id: '1494A0ED-0866-4082-92A8-1C83D5B117C4',
|
||||
termSetId: 'B12D3D77-7DEA-435E-B1AF-3175DC9851DF',
|
||||
name: 'T 1.1 TSet 1 Gr 1 TStore 1',
|
||||
description: 'Term 1 from Term Set 1 from Group 1 from Term Store 1',
|
||||
labels: [{
|
||||
isDefaultForLanguage: true,
|
||||
value: 'Term Label',
|
||||
language: 'en-US'
|
||||
}],
|
||||
termsCount: 0,
|
||||
isRoot: false
|
||||
}],
|
||||
termsCount: 1,
|
||||
isRoot: true
|
||||
}]
|
||||
}, {
|
||||
id: '6783B7F9-41CA-4405-A85C-5C74B98AC81C',
|
||||
termGroupId: '6789F532-CCDE-44C4-AE45-2EC5FC509274',
|
||||
termStoreId: 'E920D42D-1540-4E39-B3F6-3795FBEF7F21',
|
||||
name: 'TSet 2 Gr 1 TStore 1',
|
||||
description: 'Term Set 2 from Group 1 from Term Store 1',
|
||||
terms: []
|
||||
}]
|
||||
}, {
|
||||
id: '9FC4934A-EFA2-4460-A5DB-1611B8B478FE',
|
||||
termStoreId: 'E920D42D-1540-4E39-B3F6-3795FBEF7F21',
|
||||
name: 'Group 2 from Term Store 1',
|
||||
description: 'This is the second group of the first term store',
|
||||
termSets: [{
|
||||
id: '6AC24DB3-CF84-47D0-83E6-C118DD7C1D44',
|
||||
termGroupId: '9FC4934A-EFA2-4460-A5DB-1611B8B478FE',
|
||||
termStoreId: 'E920D42D-1540-4E39-B3F6-3795FBEF7F21',
|
||||
name: 'TSet 1 Gr 2 TStore 1',
|
||||
description: 'Term Set 1 from Group 2 from Term Store 1',
|
||||
terms: []
|
||||
}]
|
||||
}]
|
||||
}, {
|
||||
name: 'Term Store 2',
|
||||
id: 'BBB5D5CF-F39E-45D4-A71A-F74681133D03',
|
||||
groups: [{
|
||||
id: '96BD2791-BD83-4E1F-930C-0F2EBE943DFA',
|
||||
termStoreId: 'BBB5D5CF-F39E-45D4-A71A-F74681133D03',
|
||||
name: 'Group 1 from Term Store 2',
|
||||
description: 'This is the first group of the second term store',
|
||||
termSets: [{
|
||||
id: 'DDF892AB-00D2-4F13-B70C-378BE3A95A1D',
|
||||
termGroupId: '96BD2791-BD83-4E1F-930C-0F2EBE943DFA',
|
||||
termStoreId: 'BBB5D5CF-F39E-45D4-A71A-F74681133D03',
|
||||
name: 'TSet 1 Gr 1 TStore 2',
|
||||
description: 'Term Set 1 from Group 1 from Term Store 2',
|
||||
terms: []
|
||||
}]
|
||||
}]
|
||||
}];
|
||||
|
||||
/**
|
||||
* API to get Term Stores
|
||||
*/
|
||||
public getTermStores(): Promise<ITermStore[]> {
|
||||
return new Promise<ITermStore[]>((resolve) => {
|
||||
resolve(DataHelperMock._termStores);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* API to get Term Groups by Term Store
|
||||
*/
|
||||
public getTermGroups(termStoreId: string): Promise<ITermGroup[]> {
|
||||
return new Promise<ITermGroup[]>((resolve) => {
|
||||
for (let i = 0, len = DataHelperMock._termStores.length; i < len; i++) {
|
||||
const termStore = DataHelperMock._termStores[i];
|
||||
if (termStore.id === termStoreId) {
|
||||
resolve(termStore.groups);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* API to get Term Sets by Term Group
|
||||
*/
|
||||
public getTermSets(termGroup: ITermGroup): Promise<ITermSet[]> {
|
||||
return new Promise<ITermSet[]>((resolve) => {
|
||||
const termGroupWithTermSets: ITermGroupWithTermSets = termGroup as ITermGroupWithTermSets;
|
||||
resolve(termGroupWithTermSets.termSets);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* API to get Terms by Term Set
|
||||
*/
|
||||
public getTerms(termSet: ITermSet): Promise<ITerm[]> {
|
||||
return new Promise<ITerm[]>((resolve) => {
|
||||
const termSetWithTerms: ITermSetWithTerms = termSet as ITermSetWithTerms;
|
||||
resolve(termSetWithTerms.terms);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* API to get Terms by Term
|
||||
*/
|
||||
public getChildTerms(term: ITerm): Promise<ITerm[]> {
|
||||
return new Promise<ITerm[]>((resolve) => {
|
||||
const termWithTerms: ITermWithTerms = term as ITermWithTerms;
|
||||
resolve(termWithTerms.terms);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,663 @@
|
|||
/// <reference path="../../../../typings/SP/SP.d.ts" />
|
||||
import {
|
||||
IWebPartContext
|
||||
} from '@microsoft/sp-webpart-base';
|
||||
import {
|
||||
ITermStore,
|
||||
ITermSet,
|
||||
ITermGroup,
|
||||
ITerm
|
||||
} from '../common/SPEntities';
|
||||
import {
|
||||
IDataHelper
|
||||
} from './DataHelperBase';
|
||||
|
||||
/**
|
||||
* Interface for terms with path property and nested terns
|
||||
*/
|
||||
interface ITermWithTerms extends ITerm {
|
||||
/**
|
||||
* Term's path
|
||||
*/
|
||||
path: string[];
|
||||
/**
|
||||
* child terms
|
||||
*/
|
||||
terms?: ITerms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface that represents a map wit key term ID and value ITermWithTerms object
|
||||
*/
|
||||
interface ITerms {
|
||||
[name: string]: ITermWithTerms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface that represents a map with key term set ID and value ITerms object
|
||||
*/
|
||||
interface ITermSetTerms {
|
||||
[name: string]: ITerms;
|
||||
}
|
||||
|
||||
/**
|
||||
* SharePoint Data Helper class.
|
||||
* Gets information from current web
|
||||
*/
|
||||
export class DataHelperSP implements IDataHelper {
|
||||
/**
|
||||
* cached term stores. This property can be changed to static to be able to use the same cache in different web parts
|
||||
*/
|
||||
private _loadedTermStores: SP.Taxonomy.ITermStoreCollection;
|
||||
/**
|
||||
* cached terms' hierarchy. This property can be changed to static to be able to use the same cache in different web parts
|
||||
*/
|
||||
private _loadedTermsHierarchy: ITermSetTerms = {};
|
||||
/**
|
||||
* cached terms' flat list. This property can be changed to static to be able to use the same cache in different web parts
|
||||
*/
|
||||
private _loadedTermsFlat: ITerms[] = [];
|
||||
|
||||
/**
|
||||
* Web part context
|
||||
*/
|
||||
public context: IWebPartContext;
|
||||
|
||||
/**
|
||||
* ctor
|
||||
* @param context: web part context
|
||||
*/
|
||||
public constructor(_context: IWebPartContext) {
|
||||
this.context = _context;
|
||||
}
|
||||
|
||||
/**
|
||||
* API to get Term Stores
|
||||
*/
|
||||
public getTermStores(): Promise<ITermStore[]> {
|
||||
return new Promise<ITermStore[]>((resolve) => {
|
||||
// term stores have been already loaded
|
||||
if (this._loadedTermStores) {
|
||||
// converting SP.Taxonomy.ITermStore object to ITermStore objects
|
||||
const termStoreEntities: ITermStore[] = this.getTermStoreEntities(this._loadedTermStores);
|
||||
resolve(termStoreEntities);
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// need to load term stores
|
||||
//
|
||||
|
||||
this.loadScripts().then(() => { // loading scripts first
|
||||
const taxonomySession = this.taxonomySession;
|
||||
let termStores = taxonomySession.get_termStores();
|
||||
this.clientContext.load(termStores);
|
||||
this.clientContext.executeQueryAsync(() => {
|
||||
// converting SP.Taxonomy.ITermStore object to ITermStore objects
|
||||
const termStoreEntities: ITermStore[] = this.getTermStoreEntities(termStores);
|
||||
// caching loaded term stores
|
||||
this._loadedTermStores = termStores;
|
||||
resolve(termStoreEntities);
|
||||
}, () => {
|
||||
resolve([]);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* API to get Term Groups by Term Store
|
||||
*/
|
||||
public getTermGroups(termStoreId: string): Promise<ITermGroup[]> {
|
||||
return new Promise<ITermGroup[]>((resolve) => {
|
||||
this.getTermStoreById(termStoreId).then((termStore) => { // getting the term store
|
||||
if (!termStore) {
|
||||
resolve([]);
|
||||
return;
|
||||
}
|
||||
|
||||
let groups = termStore.get_groups();
|
||||
//
|
||||
// if Groups property is not loaded get_count will throw an error that will be handled to retrieve groups
|
||||
//
|
||||
try {
|
||||
|
||||
if (!groups.get_count()) { // this will throw error if groups were not loaded
|
||||
resolve([]);
|
||||
return;
|
||||
}
|
||||
|
||||
// converting SP.Taxonomy.ITermGroup object to ITermGroup objects
|
||||
resolve(this.getTermGroupEntities(groups, termStore.get_id().toString()));
|
||||
}
|
||||
catch (ex) { // retrieving groups
|
||||
this.clientContext.load(groups);
|
||||
this.clientContext.executeQueryAsync(() => {
|
||||
// converting SP.Taxonomy.ITermGroup object to ITermGroup objects
|
||||
resolve(this.getTermGroupEntities(groups, termStore.get_id().toString()));
|
||||
}, () => {
|
||||
resolve([]);
|
||||
});
|
||||
}
|
||||
finally {
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
/**
|
||||
* API to get Term Sets by Term Group
|
||||
*/
|
||||
public getTermSets(termGroup: ITermGroup): Promise<ITermSet[]> {
|
||||
return new Promise<ITermSet[]>((resolve) => {
|
||||
this.getTermStoreById(termGroup.termStoreId).then((termStore) => { // getting term store by id
|
||||
if (!termStore) {
|
||||
resolve([]);
|
||||
return;
|
||||
}
|
||||
|
||||
this.getTermGroupById(termStore, termGroup.id).then((group) => { // getting term group by id
|
||||
if (!group) {
|
||||
resolve([]);
|
||||
return;
|
||||
}
|
||||
let termSets = group.get_termSets();
|
||||
//
|
||||
// if termSets property is not loaded get_count will throw an error that will be handled to retrieve term sets
|
||||
//
|
||||
try {
|
||||
if (!termSets.get_count()) { // this will throw error if term sets were not loaded
|
||||
resolve([]);
|
||||
return;
|
||||
}
|
||||
|
||||
//converting SP.Taxonomy.ITermSet object to ITermSet object
|
||||
resolve(this.getTermSetEntities(termSets, termGroup.id, termGroup.termStoreId));
|
||||
}
|
||||
catch (ex) { // retrieving term sets
|
||||
this.clientContext.load(termSets);
|
||||
this.clientContext.executeQueryAsync(() => {
|
||||
//converting SP.Taxonomy.ITermSet object to ITermSet object
|
||||
resolve(this.getTermSetEntities(termSets, termGroup.id, termGroup.termStoreId));
|
||||
}, () => {
|
||||
resolve([]);
|
||||
});
|
||||
}
|
||||
finally {
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
/**
|
||||
* API to get Terms by Term Set
|
||||
*/
|
||||
public getTerms(termSet: ITermSet): Promise<ITerm[]> {
|
||||
return new Promise<ITerm[]>((resolve) => {
|
||||
// checking if terms were previously loaded
|
||||
if (this._loadedTermsHierarchy[termSet.id]) {
|
||||
const termSetTerms = this._loadedTermsHierarchy[termSet.id];
|
||||
// converting ITerms object to collection of ITerm objects
|
||||
resolve(this.getTermEntities(termSetTerms));
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// need to load terms
|
||||
//
|
||||
this.getTermStoreById(termSet.termStoreId).then((termStore) => { // getting store by id
|
||||
if (!termStore) {
|
||||
resolve([]);
|
||||
return;
|
||||
}
|
||||
|
||||
this.getTermGroupById(termStore, termSet.termGroupId).then((group) => { // getting group by id
|
||||
if (!group) {
|
||||
resolve([]);
|
||||
return;
|
||||
}
|
||||
|
||||
this.getTermSetById(termStore, group, termSet.id).then((set) => { // getting term set by id
|
||||
if (!set) {
|
||||
resolve([]);
|
||||
return;
|
||||
}
|
||||
|
||||
let allTerms: SP.Taxonomy.ITermCollection;
|
||||
//
|
||||
// if terms property is not loaded get_count will throw an error that will be handled to retrieve terms
|
||||
//
|
||||
try {
|
||||
allTerms = set.getAllTerms();
|
||||
|
||||
if (!allTerms.get_count()) { // this will throw error if terms were not loaded
|
||||
resolve([]);
|
||||
return;
|
||||
}
|
||||
|
||||
// caching terms
|
||||
this._loadedTermsHierarchy[termSet.id] = this.buildTermsHierarchy(allTerms, termSet.id);
|
||||
// converting ITerms object to collection of ITerm objects
|
||||
resolve(this.getTermEntities(this._loadedTermsHierarchy[termSet.id]));
|
||||
}
|
||||
catch (ex) { // retrieving terms
|
||||
this.clientContext.load(allTerms, 'Include(Id, Name, Description, IsRoot, TermsCount, PathOfTerm, Labels)');
|
||||
this.clientContext.executeQueryAsync(() => {
|
||||
// caching terms
|
||||
this._loadedTermsHierarchy[termSet.id] = this.buildTermsHierarchy(allTerms, termSet.id);
|
||||
// converting ITerms object to collection of ITerm objects
|
||||
resolve(this.getTermEntities(this._loadedTermsHierarchy[termSet.id]));
|
||||
}, () => {
|
||||
resolve([]);
|
||||
});
|
||||
}
|
||||
finally { }
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
/**
|
||||
* API to get Terms by Term
|
||||
*/
|
||||
public getChildTerms(term: ITerm): Promise<ITerm[]> {
|
||||
return new Promise<ITerm[]>((resolve) => {
|
||||
if (!this._loadedTermsFlat.length) {
|
||||
//
|
||||
// We can add logic to retrieve term from term Store
|
||||
// But I'll skip it for this example
|
||||
//
|
||||
resolve([]);
|
||||
return;
|
||||
}
|
||||
|
||||
let terms: ITerms;
|
||||
// iterating through flat list of terms to find needed one
|
||||
for (let i = 0, len = this._loadedTermsFlat.length; i < len; i++) {
|
||||
const currTerm = this._loadedTermsFlat[i][term.id];
|
||||
if (currTerm) {
|
||||
terms = currTerm.terms;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!terms) {
|
||||
resolve([]);
|
||||
return;
|
||||
}
|
||||
|
||||
// converting ITerms object to collection of ITerm objects
|
||||
resolve(this.getTermEntities(terms));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads scripts that are needed to work with taxonomy
|
||||
*/
|
||||
private loadScripts(): Promise<void> {
|
||||
return new Promise<void>((resolve) => {
|
||||
//
|
||||
// constructing path to Layouts folder
|
||||
//
|
||||
let layoutsUrl: string = this.context.pageContext.site.absoluteUrl;
|
||||
if (layoutsUrl.lastIndexOf('/') === layoutsUrl.length - 1)
|
||||
layoutsUrl = layoutsUrl.slice(0, -1);
|
||||
layoutsUrl += '/_layouts/15/';
|
||||
|
||||
this.loadScript(layoutsUrl + 'init.js', 'Sod').then(() => { // loading init.js
|
||||
return this.loadScript(layoutsUrl + 'sp.runtime.js', 'sp_runtime_initialize'); // loading sp.runtime.js
|
||||
}).then(() => {
|
||||
return this.loadScript(layoutsUrl + 'sp.js', 'sp_initialize'); // loading sp.js
|
||||
}).then(() => {
|
||||
return this.loadScript(layoutsUrl + 'sp.taxonomy.js', 'SP.Taxonomy'); // loading sp.taxonomy.js
|
||||
}).then(() => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads script
|
||||
* @param url: script src
|
||||
* @param globalObjectName: name of global object to check if it is loaded to the page
|
||||
*/
|
||||
private loadScript(url: string, globalObjectName: string): Promise<void> {
|
||||
return new Promise<void>((resolve) => {
|
||||
let isLoaded = true;
|
||||
if (globalObjectName.indexOf('.') !== -1) {
|
||||
const props = globalObjectName.split('.');
|
||||
let currObj: any = window;
|
||||
|
||||
for (let i = 0, len = props.length; i < len; i++) {
|
||||
if (!currObj[props[i]]) {
|
||||
isLoaded = false;
|
||||
break;
|
||||
}
|
||||
|
||||
currObj = currObj[props[i]];
|
||||
}
|
||||
}
|
||||
else {
|
||||
isLoaded = !!window[globalObjectName];
|
||||
}
|
||||
// checking if the script was previously added to the page
|
||||
if (isLoaded || document.head.querySelector('script[src="' + url + '"]')) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
// loading the script
|
||||
const script = document.createElement('script');
|
||||
script.type = 'text/javascript';
|
||||
script.src = url;
|
||||
script.onload = () => {
|
||||
resolve();
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Taxonomy session getter
|
||||
*/
|
||||
private get taxonomySession(): SP.Taxonomy.TaxonomySession {
|
||||
return SP.Taxonomy.TaxonomySession.getTaxonomySession(this.clientContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Client Context getter
|
||||
*/
|
||||
private get clientContext(): SP.ClientContext {
|
||||
return SP.ClientContext.get_current();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts SP.Taxonomy.ITermStore objects to ITermStore objects
|
||||
* @param termStores: SP.Taxonomy.ITermStoreCollection object
|
||||
*/
|
||||
private getTermStoreEntities(termStores: SP.Taxonomy.ITermStoreCollection): ITermStore[] {
|
||||
if (!termStores)
|
||||
return [];
|
||||
|
||||
const termStoreEntities: ITermStore[] = [];
|
||||
for (let i = 0, len = termStores.get_count(); i < len; i++) {
|
||||
const termStore = termStores.get_item(i);
|
||||
termStoreEntities.push({
|
||||
id: termStore.get_id().toString(),
|
||||
name: termStore.get_name()
|
||||
});
|
||||
}
|
||||
|
||||
return termStoreEntities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts SP.Taxonomy.ITermGroup objects to ITermGroup objects
|
||||
* @param termGroups: SP.Taxonomy.ITermGroupCollection object
|
||||
* @param termStoreId: the identifier of parent term store
|
||||
*/
|
||||
private getTermGroupEntities(termGroups: SP.Taxonomy.ITermGroupCollection, termStoreId: string): ITermGroup[] {
|
||||
if (!termGroups)
|
||||
return [];
|
||||
const termGroupEntities: ITermGroup[] = [];
|
||||
for (let i = 0, len = termGroups.get_count(); i < len; i++) {
|
||||
const termGroup = termGroups.get_item(i);
|
||||
termGroupEntities.push({
|
||||
id: termGroup.get_id().toString(),
|
||||
termStoreId: termStoreId,
|
||||
name: termGroup.get_name(),
|
||||
description: termGroup.get_description()
|
||||
});
|
||||
}
|
||||
|
||||
return termGroupEntities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts SP.Taxonomy.ITermSet objects to ITermSet objects
|
||||
* @param termSets: SP.Taxonomy.ITermSetCollection object
|
||||
* @param termGroupId: the identifier of parent term group
|
||||
* @param termStoreId: the identifier of parent term store
|
||||
*/
|
||||
private getTermSetEntities(termSets: SP.Taxonomy.ITermSetCollection, termGroupId: string, termStoreId: string): ITermSet[] {
|
||||
if (!termSets)
|
||||
return [];
|
||||
const termSetEntities: ITermSet[] = [];
|
||||
|
||||
for (let i = 0, len = termSets.get_count(); i < len; i++) {
|
||||
const termSet = termSets.get_item(i);
|
||||
termSetEntities.push({
|
||||
id: termSet.get_id().toString(),
|
||||
name: termSet.get_name(),
|
||||
description: termSet.get_description(),
|
||||
termGroupId: termGroupId,
|
||||
termStoreId: termStoreId
|
||||
});
|
||||
}
|
||||
|
||||
return termSetEntities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves term store by its id
|
||||
* @param termStoreId: the identifier of the store to retrieve
|
||||
*/
|
||||
private getTermStoreById(termStoreId: string): Promise<SP.Taxonomy.ITermStore> {
|
||||
return new Promise<SP.Taxonomy.ITermStore>((resolve) => {
|
||||
if (!this._loadedTermStores) { // term stores were not loaded, need to load them
|
||||
this.getTermStores().then(() => {
|
||||
return this.getTermStoreById(termStoreId);
|
||||
});
|
||||
}
|
||||
else { // term stores are loaded
|
||||
let termStore = null;
|
||||
|
||||
if (this._loadedTermStores) {
|
||||
for (let i = 0, len = this._loadedTermStores.get_count(); i < len; i++) {
|
||||
if (this._loadedTermStores.get_item(i).get_id().toString() === termStoreId) {
|
||||
termStore = this._loadedTermStores.get_item(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resolve(termStore);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves term group by its id and parent term store
|
||||
* @param termStore: parent term store
|
||||
* @param termGroupId: the identifier of the group to retrieve
|
||||
*/
|
||||
private getTermGroupById(termStore: SP.Taxonomy.ITermStore, termGroupId: string): Promise<SP.Taxonomy.ITermGroup> {
|
||||
return new Promise<SP.Taxonomy.ITermGroup>((resolve) => {
|
||||
if (!termStore || !termGroupId) {
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
|
||||
let result: SP.Taxonomy.ITermGroup;
|
||||
//
|
||||
// if Groups property is not loaded get_count will throw an error that will be handled to retrieve groups
|
||||
//
|
||||
try {
|
||||
const groups: SP.Taxonomy.ITermGroupCollection = termStore.get_groups();
|
||||
const groupsCount: number = groups.get_count();
|
||||
const groupIdUpper = termGroupId.toUpperCase();
|
||||
|
||||
for (let i = 0; i < groupsCount; i++) {
|
||||
const currGroup: SP.Taxonomy.ITermGroup = groups.get_item(i);
|
||||
if (currGroup.get_id().toString().toUpperCase() === groupIdUpper) {
|
||||
result = currGroup;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!result) { // throwing an exception to try to load the group from server again
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
resolve(result);
|
||||
}
|
||||
catch (ex) { // retrieving the groups from server
|
||||
result = termStore.getGroup(termGroupId);
|
||||
this.clientContext.load(result);
|
||||
this.clientContext.executeQueryAsync(() => {
|
||||
resolve(result);
|
||||
}, () => {
|
||||
resolve(null);
|
||||
});
|
||||
}
|
||||
finally { }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves term set by its id, parent group and parent store
|
||||
* @param termStore: parent term store
|
||||
* @param termGroup: parent term group
|
||||
* @param termSetId: the identifier of the term set to retrieve
|
||||
*/
|
||||
private getTermSetById(termStore: SP.Taxonomy.ITermStore, termGroup: SP.Taxonomy.ITermGroup, termSetId: string): Promise<SP.Taxonomy.ITermSet> {
|
||||
return new Promise<SP.Taxonomy.ITermSet>((resolve) => {
|
||||
if (!termGroup || !termSetId) {
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
|
||||
let result: SP.Taxonomy.ITermSet;
|
||||
//
|
||||
// if termSets property is not loaded get_count will throw an error that will be handled to retrieve term sets
|
||||
//
|
||||
try {
|
||||
const termSets: SP.Taxonomy.ITermSetCollection = termGroup.get_termSets();
|
||||
const setsCount: number = termSets.get_count();
|
||||
const setIdUpper = termSetId.toUpperCase();
|
||||
|
||||
for (let i = 0; i < setsCount; i++) {
|
||||
const currSet: SP.Taxonomy.ITermSet = termSets.get_item(i);
|
||||
if (currSet.get_id().toString().toUpperCase() === setIdUpper) {
|
||||
result = currSet;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!result) { // throwing an exception to try to load the term set from server again
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
resolve(result);
|
||||
}
|
||||
catch (ex) {
|
||||
result = termStore.getTermSet(termSetId);
|
||||
this.clientContext.load(result);
|
||||
this.clientContext.executeQueryAsync(() => {
|
||||
resolve(result);
|
||||
}, () => {
|
||||
resolve(null);
|
||||
});
|
||||
}
|
||||
finally { }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds terms' hierarchy and also caches flat list of terms
|
||||
* @param terms: SP.Taxonomy.ITermCollection object
|
||||
* @param termSetId: the indetifier of parent term set
|
||||
*/
|
||||
private buildTermsHierarchy(terms: SP.Taxonomy.ITermCollection, termSetId: string): ITerms {
|
||||
if (!terms)
|
||||
return {};
|
||||
const tree: ITerms = {};
|
||||
const flat: ITerms = {};
|
||||
|
||||
//
|
||||
// Iterating through terms to collect flat list and create ITermWithTerms instances
|
||||
//
|
||||
for (let i = 0, len = terms.get_count(); i < len; i++) {
|
||||
const term = terms.get_item(i);
|
||||
// creating instance
|
||||
const termEntity: ITermWithTerms = {
|
||||
id: term.get_id().toString(),
|
||||
name: term.get_name(),
|
||||
description: term.get_description(),
|
||||
labels: [],
|
||||
termsCount: term.get_termsCount(),
|
||||
isRoot: term.get_isRoot(),
|
||||
path: term.get_pathOfTerm().split(';'),
|
||||
termSetId: termSetId
|
||||
};
|
||||
|
||||
//
|
||||
// settings labels
|
||||
//
|
||||
const labels = term.get_labels();
|
||||
for (let lblIdx = 0, lblLen = labels.get_count(); lblIdx < lblLen; lblIdx++) {
|
||||
const lbl = labels.get_item(lblIdx);
|
||||
termEntity.labels.push({
|
||||
isDefaultForLanguage: lbl.get_isDefaultForLanguage(),
|
||||
value: lbl.get_value(),
|
||||
language: lbl.get_language()
|
||||
});
|
||||
}
|
||||
|
||||
// if term is root we need to add it to the tree
|
||||
if (termEntity.isRoot) {
|
||||
tree[termEntity.id] = termEntity;
|
||||
}
|
||||
|
||||
// adding term entity to flat list
|
||||
flat[termEntity.id] = termEntity;
|
||||
}
|
||||
|
||||
const keys = Object.keys(flat);
|
||||
//
|
||||
// iterating through flat list of terms to build the tree structure
|
||||
//
|
||||
for (let keyIdx = 0, keysLength = keys.length; keyIdx < keysLength; keyIdx++) {
|
||||
const key = keys[keyIdx];
|
||||
const currentTerm = flat[key];
|
||||
|
||||
// skipping root items
|
||||
if (currentTerm.isRoot) continue;
|
||||
|
||||
// getting parent term name
|
||||
const termParentName = currentTerm.path[currentTerm.path.length - 2];
|
||||
|
||||
//
|
||||
// second iteration to get parent term in flat list
|
||||
//
|
||||
for (let keySecondIndex = 0; keySecondIndex < keysLength; keySecondIndex++) {
|
||||
const secondTerm = flat[keys[keySecondIndex]];
|
||||
if (secondTerm.name === termParentName) {
|
||||
if (!secondTerm.terms)
|
||||
secondTerm.terms = {};
|
||||
secondTerm.terms[currentTerm.id] = currentTerm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._loadedTermsFlat.push(flat);
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts ITerms object to collection of ITerm objects
|
||||
* @param terms: ITerms object
|
||||
*/
|
||||
private getTermEntities(terms: ITerms): ITerm[] {
|
||||
const termsKeys = Object.keys(terms);
|
||||
const termEntities: ITerm[] = [];
|
||||
for (let keyIdx = 0, keysLength = termsKeys.length; keyIdx < keysLength; keyIdx++) {
|
||||
termEntities.push(terms[termsKeys[keyIdx]]);
|
||||
}
|
||||
|
||||
return termEntities;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,207 @@
|
|||
/**
|
||||
* SP namespace
|
||||
*/
|
||||
declare namespace SP {
|
||||
/**
|
||||
* Client Context class
|
||||
*/
|
||||
export class ClientContext {
|
||||
/**
|
||||
* API to get current context
|
||||
*/
|
||||
static get_current(): ClientContext;
|
||||
/**
|
||||
* API to load client objects
|
||||
*/
|
||||
load(obj: any, ...rest: string[]): void;
|
||||
/**
|
||||
* API to execute query
|
||||
*/
|
||||
executeQueryAsync(success?: (result: any) => void, error?: (error: any) => void);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base collection interface
|
||||
*/
|
||||
export interface ICollectionBase {
|
||||
/**
|
||||
* API to get count of items in the collection
|
||||
*/
|
||||
get_count(): number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Taxonomy namespace
|
||||
*/
|
||||
namespace Taxonomy {
|
||||
/**
|
||||
* Taxonomy Session class
|
||||
*/
|
||||
export class TaxonomySession {
|
||||
/**
|
||||
* API to get current Taxonomy Session
|
||||
* @param spContext: current Client Context
|
||||
*/
|
||||
static getTaxonomySession(spContext: ClientContext): TaxonomySession;
|
||||
|
||||
/**
|
||||
* API to get term stores
|
||||
*/
|
||||
get_termStores(): ITermStoreCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base interface of taxonomy objects
|
||||
*/
|
||||
export interface ITermBase {
|
||||
/**
|
||||
* API to get ID
|
||||
*/
|
||||
get_id(): any;
|
||||
/**
|
||||
* API to get name
|
||||
*/
|
||||
get_name(): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Term Store interface
|
||||
*/
|
||||
export interface ITermStore extends ITermBase {
|
||||
/**
|
||||
* API to get all groups in the term store
|
||||
*/
|
||||
get_groups(): ITermGroupCollection;
|
||||
/**
|
||||
* API to get group by its id
|
||||
*/
|
||||
getGroup(id: string): ITermGroup;
|
||||
/**
|
||||
* API to get term set by its id
|
||||
*/
|
||||
getTermSet(id: string): ITermSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Term Stores Collection interface
|
||||
*/
|
||||
export interface ITermStoreCollection extends ICollectionBase {
|
||||
/**
|
||||
* Gets item by index
|
||||
*/
|
||||
get_item(index: number): ITermStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Term Group interface
|
||||
*/
|
||||
export interface ITermGroup extends ITermBase {
|
||||
/**
|
||||
* API to get description
|
||||
*/
|
||||
get_description(): string;
|
||||
/**
|
||||
* API to get term sets from the group
|
||||
*/
|
||||
get_termSets(): ITermSetCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Term Groups Collection inteface
|
||||
*/
|
||||
export interface ITermGroupCollection extends ICollectionBase {
|
||||
/**
|
||||
* Gets item by index
|
||||
*/
|
||||
get_item(index: number): ITermGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Term Set Interface
|
||||
*/
|
||||
export interface ITermSet extends ITermBase {
|
||||
/**
|
||||
* API to get description
|
||||
*/
|
||||
get_description(): string;
|
||||
/**
|
||||
* API to get flat list of all terms in the Term set
|
||||
*/
|
||||
getAllTerms(): ITermCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Term Sets collection interface
|
||||
*/
|
||||
export interface ITermSetCollection extends ICollectionBase {
|
||||
/**
|
||||
* Gets item by index
|
||||
*/
|
||||
get_item(index: number): ITermSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Term interface
|
||||
*/
|
||||
export interface ITerm extends ITermBase {
|
||||
/**
|
||||
* API to get description
|
||||
*/
|
||||
get_description(): string;
|
||||
/**
|
||||
* API to get labels
|
||||
*/
|
||||
get_labels(): ILabelCollection;
|
||||
/**
|
||||
* API to get flag if current term is root term
|
||||
*/
|
||||
get_isRoot(): boolean;
|
||||
/**
|
||||
* API to get child terms count
|
||||
*/
|
||||
get_termsCount(): number;
|
||||
/**
|
||||
* API to get path of the term in defauld lcid
|
||||
*/
|
||||
get_pathOfTerm(): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Terms Collection interface
|
||||
*/
|
||||
export interface ITermCollection extends ICollectionBase {
|
||||
/**
|
||||
* Gets item by index
|
||||
*/
|
||||
get_item(index: number): ITerm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Labels collection interface
|
||||
*/
|
||||
export interface ILabelCollection extends ICollectionBase {
|
||||
/**
|
||||
* Gets item by index
|
||||
*/
|
||||
get_item(index: number): ILabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Label interface
|
||||
*/
|
||||
export interface ILabel {
|
||||
/**
|
||||
* API to get flag if current label is default for the language
|
||||
*/
|
||||
get_isDefaultForLanguage(): boolean;
|
||||
/**
|
||||
* API to get language
|
||||
*/
|
||||
get_language(): string;
|
||||
/**
|
||||
* API to get label's value
|
||||
*/
|
||||
get_value(): string;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,3 +16,4 @@
|
|||
/// <reference path="whatwg-fetch/whatwg-fetch.d.ts" />
|
||||
/// <reference path="knockout/knockout.d.ts" />
|
||||
/// <reference path="combokeys/combokeys.d.ts" />
|
||||
/// <referehce path="SP/SP.d.ts />
|
Loading…
Reference in New Issue